计算几何基础2
写在前面
建议不要看这篇文章,因为:
- 这是本juruo的第二篇blog
我自己还没学完呢- 这原来只是我为了提升自己水平写的(众所周知给别人讲解可以吸收90%的芝士)
因此,请划走,不要浪费你宝贵的时间。
如果你真要看这篇文章,请先阅读这个,本文是它的续集。
线段与直线
直线有关
直线的正交判定
判断两条直线是否正交有很多方法,这里讲述在上一章的基础上最简单的方法。
首先,判断两条直线是否正交可以转化为判断两个向量是否正交。我们知道,
接下来思考如何实现把判断直线是否正交转化为判断向量是否正交。为了达到这个目标,我们需要先计算
Vector getVector(Point a,Point b)
{
return makeVector(b.x-a.x,b.y-a.y);
}
接着是判断两个向量是否正交的函数:
bool ifOrthogonal(Vector a,Vector b)
{
return equals(dot(a,b),0.0);
}
最后是实现直线与向量的转化并判断两直线是否正交的函数:
bool ifOrthogonal(Line a,Line b)
{
Vector p,q;
p=getVector(a.p1,a.p2);
q=getVector(b.p1,b.p2);
return ifOrthogonal(p,q);
}
直线的平行判定
直线的平行判定与正交判定差别很小。唯一的不同是,正交看的是向量的数量积,而平行则需要看向量积。
代码实现如下:
bool ifParallel(Vector a,Vector b)
{
return equals(cross(a,b),0.0);
}
bool ifParallel(Line a,Line b)
{
Vector p,q;
p=getVector(a.p1,a.p2);
q=getVector(b.p1,b.p2);
return ifParallel(p,q);
}
映射
我们可以通过向量来较为简单地求点
首先,我们设向量
我们可以发现,
按照上面的步骤,首先我们算出比例,
计算映射点的代码如下。
Point project(Line s,Point p)
{
Vector P=makeVector(p.x,p.y),P1=makeVector(s.p1.x,s.p1.y),P2=makeVector(s.p2.x,s.p2.y);
double ratio=dot(P-P1,P2-P1)/(P2-P1).norm();
Vector x=P1+(P2-P1)*ratio;
return makePoint(x.x,x.y);
}
映像
之前的东西都太难了,这次来点简单的,放松一下。
映像,就是指以直线
看这个图,我们可以发现,求映像的方法很简单:先求出映射点,再把从点
Point reflect(Line s,Point p)
{
Point pro=project(s,p);
Vector Vpro=makeVector(pro.x,pro.y),P=makeVector(p.x,p.y);
Vector x=P+(Vpro-P)*2;
return makePoint(x.x,x.y);
}
线段有关
CCW
CCW(Counter_Clockwise),实现很简单却非常重要。CCW可以求出三个点的位置关系。
具体地,你需要把
方便起见,我们设
- 外积的大小
\left|\overrightarrow{a}\times\overrightarrow{b}\right|>0 时,\overrightarrow{b} 位于\overrightarrow{a} 的逆时针方向。 - 外积的大小
\left|\overrightarrow{a}\times\overrightarrow{b}\right|<0 时,\overrightarrow{b} 位于\overrightarrow{a} 的顺时针方向。 - 不符合1,2时,说明
P_2 位于直线P_0P_1 上。由于\cos\theta 在90^\circ<\theta\leq180^\circ 为负,因此在\overrightarrow{a}\cdot\overrightarrow{b}<0 时,P_2 位于线段P_0P_1 后方,即属于第3种情况。 - 不符合1,2,3时,说明
\overrightarrow{a} 与\overrightarrow{b} 方向相同。此时只需比较它们的模,就能知道到底该属哪种情况了。当\left|\overrightarrow{a}\right|<\left|\overrightarrow{b}\right| 时,属于第4种情况。 - 不符合1,2,3,4时,只能是第5种情况。
ccw() 函数可以返回三个点的位置。
int ccw(Point p0,Point p1,Point p2)
{
Vector a=getVector(p0,p1),b=getVector(p0,p2);
if(cross(a,b)>EPS)
return 1;
if(cross(a,b)<-EPS)
return -1;
if(dot(a,b)<-EPS)
return 2;
if(a.length()<b.length()-EPS)
return -2;
return 0;
}
判断线段相交
"线段相交"被定义为两条线段有重合部分。因此,两条线段共用一个端点、重合等情况均被视为相交。
给定4个点
线段
如何判断线段和直线是否相交?如果
bool intersect(Segment s1,Line s2)
{
return (ccw(s2.p1,s2.p2,s1.p1)*ccw(s2.p1,s2.p2,s1.p2)<=0);
}
再回到原来的线段与线段的问题:
bool intersect(Segment s1,Segment s2)
{
Line S1,S2;
S1.p1=s1.p1;S1.p2=s1.p2;
S2.p1=s2.p1;S2.p2=s2.p2;
return (intersect(s1,S2) && intersect(s2,S1));
}
线段的交点
现在需要求线段
首先,我们可以求出
点
接下来,就可以求出
所以交点
计算线段交点的代码如下:
Point crossPoint(Segment s1,Segment s2)
{
Point A=s1.p1,B=s1.p2,C=s2.p1,D=s2.p2;
Vector a=getVector(A,B),b=getVector(C,D),h=getVector(A,C),s=getVector(A,D);
double d1=abs(cross(a,h)),d2=abs(cross(a,s));
double t=d1/(d1+d2);
Vector ans=makeVector(C.x,C.y)+b*t;
return makePoint(ans.x,ans.y);
}
注意上述代码中,
Update
2023-4-11 写完上一章,写完“直线的平行判定”。
2023-4-13 写完“映像”。
2023-4-14 写完“ccw”。