计算几何图形算法经典问题学习整理

几何算法经典问题


一、几何基础问题

1. 判断两条线段是否相交

  • 考点:向量叉积,跨立实验,快速排斥
  • 推荐实现方法
    • 使用 cross(p1, p2, q1)cross(p1, p2, q2) 判断是否在同侧(跨立实验)
    • 边界处理:是否共线、点在线段上
步骤:
1.快速排斥:先检查两条线段的包围盒是否重叠,符合if表示不重叠
判断条件:if(max(x1, x2) < min(x3,x4) || max(x3, x4) < min(x1, x2) || max(y1, y2) < min(y3, y4) || max(y3, y4) < min(y1, y2)
包围盒空间上就错开了,那线段更不可能相交

2.跨立实验:把线段2的两个端点看成顶点1和顶点2,判断两顶点在线段1的哪一测
计算cross(p1, p2, q1)cross(p1, p2, q2)
两者结果相乘,位于同侧为正,分居两侧为负 

关键点:先排除明显不可能相交的情况,以及利用叉积判断顶点在左侧还是右侧

2. 判断点是否在多边形内

  • 方法
    • 射线法(ray casting)
    • 奇偶规则判断穿过边的次数
  • 注意:边界点、共线点的特殊处理
从点向右发射水平射线,统计与多边形交点的个数(奇数在内,偶数在外)
多边形是顶点有序的,每条边由​​相邻两个顶点​​定义

步骤:
1.遍历每条边
for(int i = 0, j = n - 1; i < n; j = i++)
i从0出发,j表示i的前一个顶点,ij组成一条边, j = i++理解为j=i,然后再i++,即前进一条边

2.检查顶点是否在两端点
if ((p.x == a.x && p.y == a.y) || (p.x == b.x && p.y == b.y))

3.排除水平边和无效y范围
if((a.y > p.y) == (b.y > p.y))
有交点得点p位于a点和b点的中间区域

4.因为是水平向右的射线,实际是计算a和b之间的插值
交点的y是肯定的,即p的y值,所以插值计算x值
x = a.x + (p.y - a.y) * (b.x - a.x) / (b.y - a.y);
if(x > p.x) inside = !inside;
如果插值的x在p点右边,即交点,则切换一次内外状态

关键:关键点在于先得排除不存在交点的情况。计算交点坐标关键点在于确定y值是固定的,便可以转化为插值算x值的问题。

3. 凸包计算

  • 算法
    • Graham 扫描法(极角排序)
  • 输入:平面上一组点
  • 输出:构成凸包的顶点顺序列表
方法一:经典Graham算法
步骤:
1.选定一个基准点(y值最小点)
2.以逆时针方向,对其他顶点做极角排序(以基准点x轴正方向为0度)
极角排序用到atan2(y - y0, x - x0)计算,atan2的计算是基于atan的分段逻辑
3.建立栈,第一个点(基准)和第二个点入栈,遍历顶点。
4.计算叉乘,判断右转出栈,左转入栈

方法二:
步骤:
1.对所有顶点根据x值排序,得到p1,p2...pn
2.划分为上凸包和下凸包
3.对于上凸包:p1和p2入upper栈,遍历顶点入栈,取末尾三个点找右拐,非右拐则删去倒数第二个顶点(三个点的中间那个)
4.对于下凸包:pn和pn-1入lower栈,遍历顶点入栈,取末尾三个点找右拐,非右拐则删去倒数第二个顶点
5.合并upper和lower(删去重复的p首尾顶点)

关键:先排序完才能遍历,以及利用叉积判断右拐

4. 判断一个有序点集的方向(顺时针 or 逆时针)

  • 方法Shoelace 公式求有向面积
    对于三角形:
    S = 1 2 ∣ x 1 y 1 1 x 2 y 2 1 x 3 y 3 1 ∣ S = \frac{1}{2}\left|\begin{array}{ccc} x_{1} & y_{1} & 1 \\ x_{2} & y_{2} & 1 \\ x_{3} & y_{3} & 1 \end{array}\right| S=21 x1x2x3y1y2y3111
    二维向量叉积的模等于两向量张成的平行四边形面积,三角形面积则除以2。那三角形面积即二分之一的向量叉积,然后写成行列式即上式。
    对于多边形:
    S t o t a l = 1 2 ∑ i = 0 n − 1 ( x i y i + 1 − x i + 1 y i ) S_{total} = \frac{1}{2}\sum_{i=0}^{n-1}(x_iy_{i+1}- x_{i+1}y_{i}) Stotal=21i=0n1(xiyi+1xi+1yi)
    (下标i+1对n取模,确保闭合)
    将多边形分割为N−2个三角形(以固定点P0为公共顶点),总面积A为所有三角形有向面积之和

  • 正面积 → 逆时针,负面积 → 顺时针

方法一:有向面积法:既包含面积大小,又包含方向信息
想象用绳子拉出多边形,逆时针拉紧时绳子“储存”正面积,顺时针则“释放”负面积

步骤:
1.遍历有序点集 for (int i = 0; i < n; i++) int j = (i + 1) % n
2.累加计算Shoelace公式
3.判断结果正负值

方法二:极值点向量叉积法​
选择多边形的一个极值点(如最高点),通过其相邻边的向量叉积判断方向
(该方法需要另外处理凹点情况,要区别极值点不在凹部,不过找y坐标最小的点这样点必为凸点)
步骤:
1.找到y坐标最大的点p
2.取出点p的前驱点和后继点形成两个向量
3.计算两个向量叉积,>0为逆时针

5. 求多边形面积和重心

  • 面积:Shoelace 公式
  • 重心:每个三角形加权中心,积分法近似
求取面积:
取Shoelace 公式结果的绝对值,前提是顶点有序

求重心:
方法一:顶点坐标算数平均法(适用于均匀密度的简单多边形)

方法二:加权面积法(能算到精确重心)
步骤:
1.计算多边形面积,得到A
2.遍历顶点,按公式累加Gx和Gy
3.除以6A得到重心坐标

加权面积法公式:多边形重心是所有三角形重心坐标的加权平均,权重为面积(面积越大的三角形对整体重心影响越大)

G x = 1 6 A ∑ i = 0 n − 1 ( x i + x i + 1 ) ( x i y i + 1 − x x + 1 y i ) G_x = \frac{1}{6A}\sum_{i = 0}^{n-1}(x_i + x_{i+1})(x_iy_{i+1} - x_{x+1}y_i) Gx=6A1i=0n1(xi+xi+1)(xiyi+1xx+1yi)
G y = 1 6 A ∑ i = 0 n − 1 ( y i + y i + 1 ) ( x i y i + 1 − x x + 1 y i ) G_y = \frac{1}{6A}\sum_{i = 0}^{n-1}(y_i + y_{i+1})(x_iy_{i+1} - x_{x+1}y_i) Gy=6A1i=0n1(yi+yi+1)(xiyi+1xx+1yi)


二、 高阶图形问题

6. 最小外接矩形(Minimum Bounding Rectangle)

  • 描述:能够完全包围给定二维图形(如点集、多边形或轮廓)的最小面积的矩形。根据矩形的边是否与坐标轴平行,可分为两类:轴对齐最小外接矩形(Axis-Aligned MBR)​旋转最小外接矩形(Rotated MBR)​重点是后者。
  • 算法:Rotating Calipers(旋转卡壳),通过旋转凸包的切线寻找最小面积矩形。寻找过程中通过计算三角形面积来找对踵点。
  • 输出:最小面积矩形的顶点坐标
轴对齐最小外接矩形:
相当于计算轴对齐包围盒,找顶点集的x,y的最大值和最小值

旋转最小外接矩形:旋转卡壳法
(“卡壳”想象一个可以夹住凸包的夹子)
实际执行的时候,通过找每条对应边的的最远点
旋转卡壳步骤:
1.计算所有点集的凸包
2.遍历边,找距离最远的点(通过叉积计算三角形面积,单峰,面积上升到最高点后,又会下降)

该方法用于计算最小外接矩形的伪代码:
def min_bounding_rectangle(points):
    hull = convex_hull(points)
    min_area = float('inf')
    for i in range(len(hull)):
        edge = vector(hull[i], hull[(i+1)%len(hull)])
        angle = atan2(edge.y, edge.x)
        rotate all points to align edge with x-axis
        find max_y, min_y, max_x, min_x of rotated points
        area = (max_x - min_x) * (max_y - min_y)
        update min_area and rectangle if smaller
    return rectangle

7. 多边形之间是否相交

  • 方法
    • 分离轴定理(SAT)
    • 任意一条边与对方多边形边相交
    • 一个多边形顶点是否落入另一个多边形
分离轴定理:如果存在一条直线,使得连个多边形在该直线上的投影不存在重叠,则这两个多边形不相交。(只要找到一处即足够证明不相交)

步骤:
1.快速排斥:用轴对齐包围盒粗略检测是否重叠
2.分离轴定理:遍历两个多边形的所有边的法向量(作为分离轴)
3.分别计算两个多边形在分离轴上的投影,计算出各自的(min, max)投影范围
计算投影:每个顶点计算在单位法向量的投影,即顶点p · 单位法向量n,得到标量投影值,投影值是顶点在分离轴方向上的“阴影”位置
4.若存在任一法向量满足max1 < min2 或者 max2 < min1,则多边形不相交。

最坏情况时间复杂度O((M + N) * (M + N))
实际遇到一个满足条件则提前终止,平均复杂度接近O(M + N) 

8. 判断圆与矩形是否相交

  • 技巧:求圆心到矩形边的最近距离 vs 半径
方法一:可以用分离轴定理
利用矩形的四个边的法向量作为分离轴,计算圆和矩形在每条轴上的投影区间
(如果旋转矩形,可以将旋转至轴对齐的情况,将问题简化为轴对齐矩阵检测)
方法二:圆心到矩形边的最短距离
1.计算圆形到矩形四条边的最短距离,先找在矩形上的最近点
closestX = clamp(cx, rx, rx + w)
closestY = clamp(cy, ry, ry + h)
2.若最短距离 ≤ 圆的半径 r;否则不相交
dx = cx - closestX
dy = cy - closestY
distance_squared = dx*dx + dy*dy

9. 求两个矩形的交集面积

  • 方法:投影后在 x、y 轴求重叠部分面积
  • 扩展:多个矩形并集面积(扫描线 + 线段树)
轴对齐情况:坐标比较
步骤1:选出理论相交区域的左下角和右上角
步骤2:检查是否相交,即检查是否满足xmin < xmax 和 ymin < ymax
步骤3:满足则计算面积

旋转矩形情况:Sutherland–Hodgman 多边形裁剪算法
用来裁剪的多边形A(要求凸多边形)和被裁剪的多边形B(A和B都要求逆时针有序)
用A的每一条边去找到与B的交点(遍历B的每条边判断线段相交并计算交点)
步骤:
1.对于A的每条边,都遍历B的每条边(用两个点表示该边p1,p2)
2.判断两边相交情况
if(p2在裁剪边右侧)
	if(p1在裁剪边左侧)
		计算交点并将交点入栈
	else //表示该边在内部
		将p2入栈
else if(p1在裁剪边右侧)	//此时p2在左侧
		计算交点并入栈
3.结果点集也是逆时针有序的多边形顶点
4.利用有向面积法或者说鞋带公式计算面积

10. 点到线段的最短距离

  • 用处:点线距离,圆线碰撞
  • 技巧:投影判断点是否在线段投影内,若否取端点距离;点积投影
投影法:判断P点是否在线段AB的投影范围内
步骤:
1.计算AB,AP两个向量,其中AB归一化为方向向量
2.计算AP点积AB,结果为AP向量在AB方向的投影长度
3.判断长度,<0或者>AB长度都表示不在投影范围内,分别取PA和PB最短的距离即结果
4.否则可以用勾股定理计算P到AB的长度

关键:点积算投影

11. 线段与多边形交点个数

  • 应用:路径穿越、可见性、地图遮挡判断
遍历多边形的每条边,判断是否与线段相交
即问题简化为线段与线段的相交

12. 多边形剪切(Polygon Clipping)

  • 算法
    • Sutherland-Hodgman 算法(见问题9)
    • Weiler–Atherton 算法(适用于复杂多边形)
Weiler–Atherton 算法:通用的多边形裁剪算法,找到连个多边形的交点p和q,从p出发沿着多边形A的边界遍历直至遇到q点,再从q点出发沿着多边形B的边界遍历直至遇到q点。(多个连通分量则分别处理)

步骤:
1.针对多边形A的所有边,都遍历多边形B的所有边,计算线段之间交点
2.从一个交点(入口)出发开始遍历,遵循以下规则:
沿着A遍历,直至遇到一个交代;
转到沿着B继续遍历,直至遇到另一个交点;
再回到A,继续该交替过程;
重复直至回到起点,形成一个完整的多边形



三、 三维几何算法问题(3D Geometry)

13. 三维向量运算与空间关系判断

  • 内容:点积、叉积、共线性、共面性判断
  • 应用:3D建模、物理仿真中的方向与法线计算
1.点积可以判断夹角
2.叉积可以计算法线
3.三向量共面使用混合积:a·(b X c),向量a与向量b和c的法向量夹角是904.两向量共线:存在非零实数 使b=λa
5.点积=0:两向量垂直
6.叉积=0:两向量平行
 

14. 判断两条3D线段是否相交

  • 方法:建立参数方程后解方程组,判断参数值,以及能求交点
计算方向向量,使用二者叉积来判段平行性作为快速排除
使用参数方程:
A + t(B − A) = C + s(D − C)解这个方程组,得到 t 和 s 的值
如果 t 和 s 都在 [0,1] 范围内,那么交点在线段上,两条线段相交。否则,它们不相交。

15. 3D 点到直线/平面的最短距离、投影点坐标计算

  • 公式:矢量投影法、法线点积法
点到直线的最短距离可以套用2D的方法,见问题10

点到平面距离:
法向量点积(平面由顶点组成):
步骤:
1.对多面体的每个面,计算法向量N = (v1-v0)X(v2-v0)
2.计算待测点P到面的有向距离d = N·(P - v0)

矢量投影法:利用平面的法向量(平面为平面方程表示)
平面方程:
Ax + By + Cz + D = 0
该平面的法向量是(A, B, C)
使用以下公式计算:

在这里插入图片描述
投影点坐标:

计算投影点坐标,先算出点到平面的最短距离d,然后对P点往单位法向量的反方向移动距离d,即得到投影点坐标:
P` = P - d·N

16. 判断点是否在三角形/多面体内部

  • 方法:面积法、射线法扩展到 3D、体积法(四面体体积符号)
  • 应用:点在多面体内部判断,BSP 树构建
判断点是否在三角形内部:
面积法:p与三条边组成的三个三角形面积和≈该三角形面积(考虑浮点误差),则P在三角形内

判断点是否多面体内部:
法向量点积测试:
步骤:
1.对多面体的每个面,计算法向量N = (v1-v0)X(v2-v0)
2.计算待测点P到面的有向距离d = N·(P - v0)
3.判断d大小,若 d>0:P 在面的外侧(对当前面可见);若 d≤0:P 在面的内侧或面上
4.若所有面都满足d≤0,则点P在多面体内部;否则在外部

射线法:计算射线与每个面(三角形面或凸多边形)的交点,奇内偶外
(从2D计算射线与线段的交点变成3D中射线与三角形的交点)

射线与三角形求交点计算方法:
1.建立射线方程:
R ( t ) = P o + t ⋅ D d i r R(t)=P_o+t\cdot D_{dir} R(t)=Po+tDdir
2.对平面建立点法式方程:
N ⋅ ( P − P 1 ) = 0 N\cdot (P - P_1) = 0 N(PP1)=0
3.将1中R(t)代入2中的P:
N ⋅ ( P 0 + t D d i r − P 1 ) = 0 N\cdot(P_0+tD_{dir} - P_1) = 0 N(P0+tDdirP1)=0
式中只有t式未知量,求解t:
t = N ⋅ ( P 1 − P 0 ) N ⋅ D d i r t=\frac{N\cdot(P_1 - P_0)}{N\cdot D_{dir}} t=NDdirNP1P0
4.当t>0:交点在射线前方;当t<0:交点在射线后方(通常忽略)。利用t值在1式中算出具体交点坐标值

附:两种常用的平面方程表达式:
在这里插入图片描述

17. 三维凸包构建

  • 算法:Incremental/QuickHull/Chan’s algorithm
  • 难点:三维可视面维护、面连通关系处理
增量法(Incremental):逐个添加点,动态维护当前凸包
步骤:
1.​​初始化​​:选择4个不共面的点构成初始四面体
2.遍历剩余点,增量添加,对于每个新点 P:
若 P 在当前凸包内,跳过(计算点P在凸包内方法见16,可用法向量点积法)
否则,找到所有“可见面”(即 P 在面外侧),删除这些面并用 P 与它们的边构成新面
3.终止​​:处理完所有点后,剩余面即为凸包


18. 求两个立方体是否相交

  • 方法:AABB 包围盒检测、OBB 分离轴定理(SAT)
  • 应用:3D 碰撞检测、物理引擎
相当于两个多边形是否相交的拓展
步骤:
1.快速排除:使用轴对齐包围盒AABB快速判断是否重叠
def aabb_intersect(minA, maxA, minB, maxB):
    # 检查三个轴是否重叠
    overlap_x = (maxA[0] >= minB[0]) and (maxB[0] >= minA[0])
    overlap_y = (maxA[1] >= minB[1]) and (maxB[1] >= minA[1])
    overlap_z = (maxA[2] >= minB[2]) and (maxB[2] >= minA[2])
    return overlap_x and overlap_y and overlap_z
2.分离轴定理:
遍历两个立方体的所有边法向量作为分离轴:两个立方体各3个本地轴,以及立方体1和立方体2所有边的组合法向量​​(共9条轴,由两立方体边的叉积得到)
将两个立方体的顶点投影到每条分离轴上(共15条),分别得到标量投影区间
若存在一条轴使得maxA<minB或maxB<minA,则立方体不相交(能找到一次符合即可)

19. 三维模型的旋转、缩放与变换

  • 方法:仿射变换矩阵(4x4)、欧拉角 vs 四元数
  • 实战:WebGL/OpenGL 中坐标变换链条

旋转:
在这里插入图片描述
位移:
在这里插入图片描述
缩放:
在这里插入图片描述


四、基础补充

20.将多边形顶点排序

  • 方法:质心计算、极角排序
步骤:
1.计算几何中心点​:顶点坐标平均
2.按极角排序:参考点是中心点(x0, y0),使用atan2计算极角后排序
3.使用有向面积法验证顺时针和逆时针方向

21.计算一条边的法向量

  • 内容:知道一条边的两个顶点p,q,需要计算它的法向量
  • 方法:将方向向量的x和y互换,并改变其中一个坐标的符号(计算结果是点积为0)
p(x1, y1)q(x2, y2)
1.计算pq方向向量:(x2 - x1, y2 - y1)
2.互换坐标获得法向量:
(-(y2 - y1), (x2 - x1))
或者
(y2 - y1, -(x2 - x1))

22.计算两条线段的交点

  • 内容:两条线段,线段1的端点为 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1, y_1),(x_2, y_2) (x1,y1),(x2,y2),线段2的端点为 ( x 3 , y 3 ) , ( x 4 , y 4 ) (x_3, y_3),(x_4, y_4) (x3,y3),(x4,y4)。已知线段1与线段2相交,计算其交点坐标。
  • 方法一:假设交点为 ( x , y ) (x, y) (x,y),利用斜率相同这一点形成方程组
    k 1 = y 2 − y 1 x 2 − x 1 = y − y 1 x − x 1 k_1 = \frac{y_2 - y_1}{x_2 - x_1} = \frac{y-y_1}{x-x_1} k1=x2x1y2y1=xx1yy1 k 2 = y 4 − y 3 x 4 − x 3 = y − y 3 x − x 3 k2 = \frac{y_4-y_3}{x_4-x_3}=\frac{y-y_3}{x-x_3} k2=x4x3y4y3=xx3yy3
  • 方法二:参数方程法:
    在这里插入图片描述

23.判断2D顶点在三角形内部

同向法:叉积判断P点都在三条边的同一侧(例如都位于三条边的右侧),则P在内部
或者用面积法,见16

五、学习建议

  • 熟练掌握 2D 向量操作:叉积、点积、投影、角度
  • 熟悉浮点精度问题的处理(EPS 比较)
  • 多写边界处理(共线、端点、浮点误差)
  • 三维空间概念理解要清晰:坐标系、法向、表面朝向
  • 练习:LeetCode、NowCoder、牛客网图形类题库
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值