前言
(NOIP)结束之后,我下定决心来好好学习一些省选算法了。
计算几何应该是一个比较复杂的算法吧,虽然出现得较少,但还是蛮实用的。
接着上一次学习的点与向量·叉积与点积,我继续学习了点、直线、线段的关系。
直线与线段
我们可以用这样子的结构体来表示直线与线段:
struct Line//一个结构体用来存储一条直线
{
Point A,B;//存储直线上两点坐标,两点确定一条直线
Line(Point x=Point(0,0),Point y=Point(0,0)):A(x),B(y){}//构造函数
};
typedef Line Segment;//线段在代码中其实与直线差不多,因此可以直接typedef一下
点到直线的距离
如图所示,我们要求点(A)到直线(BC)的距离。
首先,我们连结(AB)和(AC),并过点(A)作(ADot BC):
则显然(vec{AB} imesvec{BC})的绝对值就是(2S_{△ABC})。
而由三角形面积公式可得,(BCcdot AD=2S_{△ABC})。(注意,这里的(cdot)是乘法的意思)
因此,我们用这个值去(2S_{△ABC})去除以(BC)的长度(|vec{BC}|),即可求出点(AD),即(A)到直线(BC)的距离了。
用一个式子表示就是:
[AD=frac{|vec{AB}cdotvec{BC}|}{|vec{BC}|}
]
注意,这个式子中上面的(|)表示绝对值,下面的(|)表示长度。
代码实现如下:
inline double PToL(Point A,Line L) {return fabs(Cro(A-L.A,L.A-L.B))/Len(L.A-L.B);}//点到直线的距离
点到线段的距离
考虑依然过点(A)作(ADot BC),则此时的点(D)可能不在(BC)边上,因此就要分情况讨论了。
- 如果点(D)在(BC)边上。直接按求点到直线上的距离做即可。
- 如果点(D)不在(BC)边上。那么我们就要找到点(B)与点(C)中离点(D)较近的点与点(D)的距离。
代码实现如下:
inline double PToS(Point A,Segment S)//点到线段的距离
{
if(Dot(A-S.A,S.B-S.A)<0) return Len(A-S.A);//如果垂直点不在线段上,且点A离线段的A端点较近
if(Dot(A-S.B,S.A-S.B)<0) return Len(A-S.B);//如果垂直点不在线段上,且点A离线段的B端点较近
return PToL(A,S);//如果垂直点在线段上,直接按求点到直线上的距离做即可
}
判断两条线段是否相交
我们可以用叉积来分别判断两条线段的两端是否在另一条线段的两侧。
代码如下:
inline bool IsIntersect(Segment S1,Segment S2)//判断两条线段是否相交
{
register double f1=Cro(S1.B-S1.A,S2.A-S1.A),f2=Cro(S1.B-S1.A,S2.B-S1.A);//判断第一条线段的两端是否在第二条线段两端
register double g1=Cro(S2.B-S2.A,S1.A-S2.A),g2=Cro(S2.B-S2.A,S1.B-S2.A);//判断第二条线段的两端是否在第一条线段两端
return ((f1<0)^(f2<0))&&((g1<0)^(g2<0));//退出函数
}
求两直线交点
不难想到用叉积后的比值来计算,虽然精度流失大,但还是比较好理解的。
代码如下:
inline Point LineIntersection(Line L1,Line L2) {return L1.A+(L1.B-L1.A)*Cro(L2.B-L2.A,L1.A-L2.A)/Cro(L1.B-L1.A,L2.B-L2.A);}//求两直线交点
后记
计算几何中关于点、直线和线段关系的内容大致就是这些吧。
如果有其他内容我会再进行补充的