/*
利用矢量叉积判断是逆时针还是顺时针。
设A(x1,y1),B(x2,y2),C(x3,y3),则三角形两边的矢量分别是:
AB=(x2-x1,y2-y1), AC=(x3-x1,y3-y1)
则AB和AC的叉积为:(2*2的行列式)
|x2-x1, y2-y1|
|x3-x1, y3-y1|
值为:(x2-x1)*(y3-y1) - (y2-y1)*(x3-x1)
利用右手法则进行判断:
如果AB*AC>0,则三角形ABC是逆时针的
如果AB*AC<0,则三角形ABC是顺时针的
*/
#include<stdio.h>
#include<math.h>
typedef struct point{
double x,y;
}Point; //点
typedef struct vector{
double x,y;
}Vector; //向量
typedef struct{
double xmax,xmin; //矩形在x轴的最小值和最大值
double ymax,ymin; //矩形在y轴的最小值和最大值
}MBR; //最小外包矩形
/*返回x和y的最大值*/
double Max(double x,double y){
return x>y?x:y;
}
/*返回x和y的最小值*/
double Min(double x,double y){
return x<y?x:y;
}
/*
由 A(x1,y1),B(x2,y2) 构造向量AB
向AB=OB-OA=(x2-x1, y2-y1)
*/
Vector VectorConstruct(Point A, Point B) {
Vector v;
v.x = B.x - A.x;
v.y = B.y - A.y;
return v;
}
/*
向量a(x1,y1)和b(x2,y2)的叉积
a×b = x1y2-x2y1
*/
double CrossProduct(Vector a, Vector b) {
return a.x * b.y - a.y * b.x;
}
/*
由A点和B点构造一个最小外包矩形【以线段AB为对角线的矩形】
*/
MBR MbrConstruct(Point A, Point B) {
MBR m;
/*将点A和点B中x值大的赋给xmax,小的赋给xmin*/
if(A.x > B.x){
m.xmax = A.x;
m.xmin = B.x;
}
else{
m.xmax = B.x;
m.xmin = A.x;
}
/*将点A和点B中y值大的赋给ymax,小的赋给ymin*/
if(A.y > B.y){
m.ymax = A.y;
m.ymin = B.y;
}
else{
m.ymax = B.y;
m.ymin = A.y;
}
return m;
}
/*
快速排斥实验【两个MBR在x以及y坐标的投影是否有重合】
判断两个MBR是否有交集,有返回1,否0
*/
int MbrOverlap(MBR m1, MBR m2) {
double xmin, xmax, ymin, ymax;
xmin = Max(m1.xmin, m2.xmin); //两个外包矩形中最小x中的大者
xmax = Min(m1.xmax, m2.xmax); //两个外包矩形中最大x中的小者
ymin = Max(m1.ymin, m2.ymin); //两个外包矩形中最小y中的大者
ymax = Min(m1.ymax, m2.ymax); //两个外包矩形中最大y中的小者
/*
如果:两个外包矩形中最大x中的小者 >= 两个外包矩形中最小x中的大者
并且:两个外包矩形中最大y中的小者 >= 两个外包矩形中最小y中的大者
则两个MBR是有交集,返回1
*/
return (xmax >= xmin && ymax >= ymin) ? 1 : 0;
}
/*
方法一:快速排斥+跨立
判断两线段(线段AB和CD)是否相交,是返回1,否0
*/
int SegmentIntersection(Point A, Point B, Point C, Point D) {
MBR m1,m2;
Vector CA,CB,CD,AC,AD,AB;
/*求线段AB和CD所在的MBR*/
m1 = MbrConstruct(A, B);
m2 = MbrConstruct(C, D);
/*【快速排斥实验】判断AB和CD所在的MBR是否相交*/
if(MbrOverlap(m1, m2) == 0) //不相交,直接返回0
return 0;
/*求向量CA、CB、CD的坐标表示*/
CA = VectorConstruct(C, A);
CB = VectorConstruct(C, B);
CD = VectorConstruct(C, D);
/*求向量AC、AD、AB的坐标表示*/
AC = VectorConstruct(A, C);
AD = VectorConstruct(A, D);
AB = VectorConstruct(A, B);
/*【跨立实验】进一步判断*/
/*AB跨立CD,并且,CD跨立AB*/
/*判断条件:(CA×CD)*(CB×CD)<= 0 && (AC×AB)*(AD×AB)<= 0*/
if (CrossProduct(CA, CD) * CrossProduct(CB, CD) <= 0 && CrossProduct(AC, AB) * CrossProduct(AD, AB) <= 0)
return 1;
else
return 0;
}
void main(){
int tag;
Point A,B,C,D;
A.x=1,A.y=2;
B.x=3,B.y=4;
C.x=1,C.y=3;
D.x=4,D.y=1;
/*方法一:快速排斥+跨立试验*/
tag=SegmentIntersection(A,B,C,D);
if(tag)
printf("两直线相交\n");
else
printf("两直线不相交\n");
}