Rating:1763 | 几何基础(2) 判断两条线段是否相交: 矢量如果一条线段的端点是有次序之分的话,那么这种线段就称为 有向线段,如果有向线段p1p2的起点p1在坐标的原点,则可以把它称为矢量 p2 矢量的加减设二维矢量 P = (x1, y1), Q = (x2, y2),则 P + Q = (x1 + x2, y1 + y2), P - Q = (x1 - x2, y1 - y2),且有 P + Q = Q + P, P - Q = -(Q - P) 矢量叉积设矢量 P = (x1, y1), Q = (x2, y2),则 P * Q = x1 * y2 - x2 * y1; 其结果是一个由 (0, 0), P, Q, P + Q 所组成的平行四边形的 带符号的面积,P * Q = -(Q * P), P * (- Q) = -(P * Q) 叉积的一个非常重要的性质是可以通过它的符号来判断两矢量相互之间的顺逆时针关系: 若 P * Q > 0,则 P 在 Q 的顺时针方向 若 P * Q < 0, 则 P 在 Q 的逆时针方向 若 P * Q = 0,则 P 与 Q 共线,但不确定 P, Q 的方向是否相同 折线段的拐向判断如图,假设有折线段 p0p1p2 ,要确定 p1p2 相对于 p0p1 是向左拐还是向右拐,可以通过计算(p2 - p0)*(p1 - p0) 的符号来确定折线的拐向(点 p2 - p0 实际上就是向量 p2,但这里要注意线段和矢量的区别) 判断点是否在线段上设点 Q = (x, y), P1 = (x1, y1), P2 = (x2, y2),若 (Q - P1)*(P2 - P1) = 0 且 min(x1, x2) <= x <= max(x1, x2) && min(y1, y2) <= y <= max(y1, y2),则点 Q 在线段 P1P2 上 判断两线段是否相交1)快速排斥试验设以线段 P1P2 为对角线的矩形为 R,设以线段 Q1Q2 为对角线的矩形为 T,若 R、T 不相交,则两线段不可能相交 假设 P1 = (x1, y1), P2 = (x2, y2), Q1 = (x3, y3), Q2 = (x4, y4),设矩形 R 的 x 坐标的最小边界为 minRX = min(x1, x2),以此类推,将矩形表示为 R = (minRX, minRY, maxRX, maxRY) 的形式,若两矩形相交,则相交的部分构成了一个新的矩形 F,如图所示,我们可以知道 F 的 minFX = max(minRX, minTX), minFY = max(minRY, minTY), maxFX = min(maxRX, maxTX), maxFY = min(maxRY, maxTX),得到 F 的各个值之后,只要判断矩形 F 是否成立就知道 R 和 T 到底有没有相交了,若 minFX > maxFX 或 minFY > maxFy 则 F 无法构成,RT不相交,否则 RT相交 2)跨立试验若 P1P2 跨立 Q1Q2,则 P1,P2 分别在 Q1Q2 所在直线的两端,则有 (P1 - Q1)*(Q2 - Q1) * (Q2 - Q1)*(P2 - Q1) > 0,当 (P1 - Q1)*(Q2 - Q1) = 0 时,说明 (P1 - Q1) 与 (Q2 - Q1) 共线,但由于已经经过快速排斥试验,所以 Q1 必为 P1P2 与 Q1Q2 的交点,依然满足线段相交的条件,则跨立试验可改为: 当 (P1 - Q1)*(Q2 - Q1) * (Q2 - Q1)*(P2 - Q1) >= 0,则 P1P2 跨立 Q1Q2,当 Q1Q2 也跨立 P1P2 的时候,则 P1P2 相交 (注意式子中被隔开的 * 代表两个叉积的值的相乘,而另外的两个 * 则代表计算矢量叉积)
判断线段与直线是否相交: 首先在直线上随便取两点做为线段l1; 再与线段L2进行判断,因为是直线所以现在只需判断L2是否跨立l1就行了。
代码如下: bool inter_line(line l1,line l2)//判断直线与线段是否相交 { return sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e))<=0; }
代码实现: 再这之前可以先去学下,C++的运算符重载部分 运算符重载详解 http://blog.chinaunix.net/uid-21411227-id-1826759.html #include <iostream> #include <cstdio> #include <cmath> using namespace std; const double eps=1e-8; const double PI=acos(-1.0); int sgn(double x) { if(fabs(x)<eps) return 0; if(x < 0) return -1; else return 1; }
struct point { double x,y; point(){} point (double _x,double _y) { x=_x,y=_y; } point operator -(const point &b)const { return point(x-b.x,y-b.y); } point operator +(const point &b)const { return point (x+b.x,y+b.y); } double operator ^(const point &b)const //叉积 { return x*b.y-y*b.x; } double operator *(const point &b)const //点积 { return x*b.x+y*b.y; } void transxy(double B) //绕原点旋转角度B(弧度值),后的x,y { double tx=x,ty=y; x= tx*cos(B) - ty*sin(B); y= tx*sin(B) + ty*cos(B); } };
struct line//线 { point s,e; line(){} line(point _s,point _e) { s=_s,e=_e; } };
double dist(point a,point b)//两点间距离 { return sqrt((a-b)*(a-b)); }
bool inter(line l1,line l2)//判断线段相交 { return max(l1.s.x,l1.e.x)>=min(l2.s.x,l2.e.x)&& max(l2.s.x,l2.e.x)>=min(l1.s.x,l1.e.x)&& max(l1.s.y,l1.e.y)>=min(l2.s.y,l2.e.y)&& max(l2.s.y,l2.e.y)>=min(l1.s.y,l1.e.y)&& sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e))<=0&& sgn((l1.s-l2.e)^(l2.s-l2.e))*sgn((l1.e-l2.e)^(l2.s-l2.e))<=0; }
bool inter_line(line l1,line l2)//判断直线与线段是否相交 { return sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e))<=0; }
int main() { double ax,ay,bx,by,cx,cy,dx,dy; while(scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&ax,&ay,&bx,&by,&cx,&cy,&dx,&dy)!=EOF)//分别输入两条线段的首尾端点 { point k1(ax,ay); point k2(bx,by); point k3(cx,cy); point k4(dx,dy); line l1(k1,k2); line l2(k3,k4); if(inter(l1,l2)) printf("Yes\n"); else printf("No\n"); } return 0; }
|