在研究线段相交之前,我们首先来看一下点定位中是如何判断点是否在线段上的:
设有点Q及线段P1P2,判断Q在线段P1P2上包括两条依据:
(1)(Q-P1)×(P2-P1)=0(这里×表示叉积);
(2)Q在以P1P2为对角线的矩形内。
前者保证了Q在直线P1P2上,后者保证了点Q不在线段P1P2的延长线或反向延长线上。
代码入下:
typedef struct node { double x,y; }Point; bool on_segment(Point p1,Point p2,Point q) { if((q.x-p1.x)*(p2.y-p1.y)==(p2.x-p1.x)*(1.y-p1.y)&&min(p1.x,p2.x)<=q.x&&q.x<=max(p1.x,p2.x)&&min(p1.y,p2.y)<=q.y&&q.y<=max(p1.y,p2.y)) return true; else return false; }
有了点定位的概念,接下来我们看一下如何判断两线段相交:
对于给定4个点确定的两条线段A1A2,B1B2:
typedef struct { double x, y; } Point; Point A1,A2,B1,B2;
判断两线段是否相交需要分两步确定:
1.快速排斥实验
设以线段A1A2和线段B1B2为对角线的矩形为M,N;
若M,N 不相交,则两个线段显然不相交;
判断矩形相交,只需判断某一矩形是否有顶点在另一个矩形内即可。
所以:只有当满足第一个条件时,两个线段才可能相交。
2.跨立实验
如果两线段相交,则两线段必然相互跨立对方.若A1A2跨立B1B2,则矢量( A1 - B1 ) 和(A2-B1)位于矢量(B2-B1)的两侧,
即:((A1-B1) × (B2-B1) )·( (A2-B1) × (B2-B1))<0。
上式可以改写成:((A1-B1) × (B2-B1)) · ((B2-B1) × (A2-B1))>0。
当(A1-B1) × (B2-B1)=0时,说明A1B1和B2B1共线,但前面已经通过了快速排斥实验,所以A1一定在B1B2上,所以判断线段A1A2是否跨立B1B2的依据就是::((A1-B1) × (B2-B1)) · ((B2-B1) × (A2-B1))>=0。
同理,判断线段B1B2是否跨立A1A2的依据是::((B1-A1) × (A2-A1)) · ((A2-A1) × (B2-A1))>=0。
如图:
应用:
1. 判断两个线段相交
2. 判断线段与直线相交
3. 判断点在矩形内
代码如下:
/* 若线段v1和线段v2的界限框同时在x方向和y方向相交且v1(或者v2)跨立于v2(或者v1)所在的直线,则返回true,否则返回false */ typedef struct node { double x,y; }Point; typedef struct line { Point start,end; }vector; double multi(Point p1,Point p2,Point p0) { return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); } bool cross(vector v1,vector v2) { if(max(v1.start.x,v1.end.x)>=min(v2.start.x,v2.end.x)&&max(v2.start.x,v2.end.x)>=min(v1.start.x,v1.end.x)&&max(v1.start.y,v1.end.y)>=min(v2.start.y,v2.end.y)&&max(v2.start.y,v2.end.y)>=min(v1.start.y,v1.end.y)&&multi(v2.start,v1.end,v1.start)*multi(v1.end,v2.end,v1.start)>=0&&multi(v1.start,v2.end,v2.start)*multi(v2.end,v1.end,v2.start)>=0) return true; return false; }