判断线段相交的向量叉积算法

这是一道比较简单的判断线是否相交的题。

题意是:

每个case给出ABC三条线:

Line A 的起始点 (x1, y1) & 终点 (x2, y2)

Line B 的起始点 (x1, y1) & 终点 (x2, y2) 

Line C 的起始点 (x1, y1) & 终点 (x2, y2)

有五种可能的关系:

1. 三条线都不想交

2.AB相交,C与它们分离

3.BC相交,A与它们分离

4.AC相交,B与它们分离

5.ABC都相交


在下手写之前,先理解一下可以用的方法

请参考下面的文档,我觉得写得特别的好:

http://hsfzxjy.github.io/the-simplest-way-to-find-out-if-two-segments-are-intersected/


基本思路是

bool checkCross(line1,line2) {

        if(两条线平行且重叠) {

                 return true;

         }

        if(两线相交)   {  // direct(AC, AD) != direct(BC, BD) direct(CA, CB) != direct(DA, DB)

                 return true;

         }

         return false;

}


具体的判断是判断方向,判断方向是点和线

当向量叉积为0是判断是否重叠

// if direct(a,b) is clockwise, 180<=<a,b><=360, sin<a,b> <= 0
// while anticlockwise 0<=<a,b><=180, sin<a,b> >=0
int checkDirect(Line l, Point p) {
    Vect v1 = makeVect(p,l.start);
    Vect v2 = makeVect(p,l.end);
    int direct = v1.x*v2.y - v2.x*v1.y;  // cout the direct of v1 and v2 vector
    if(direct == 0) {
        if(checkInside(l,p)) {
            return 2;
        } else {
            return 0;
        }
    } else {
        return direct>0?1:-1;
    }
}

重叠的判断方法:

bool checkInside(Line l, Point p) { // check whether point inside the line
	int lX = getMin(l.start.x, l.end.x);
	int rX = getMax(l.start.x, l.end.x);
	int lY = getMin(l.start.y, l.end.y);
	int rY = getMax(l.start.y, l.end.y);
	if(lX<=p.x && p.x<=rX && lY<=p.y && p.y<=rY) {
		return true;
	} else {
		return false;
	}
}


全套的代码:
#include <cstdio>
#include <iostream>
using namespace std;
#define TEST
#define NUM 3
#define getMin(a,b) a<b?a:b
#define getMax(a,b) a>b?a:b;
int result[3][3];
int ans[3];

typedef struct {
	int x;
	int y;
}Point;

typedef struct {
	int x;
	int y;
}Vect;

typedef struct {
	Point start;
	Point end;
	Vect v;
}Line;

Line line[3];

Vect makeVect(Point p1, Point p2) {
	Vect tmp;
	tmp.x = p2.x-p1.x;
	tmp.y = p2.y-p1.y;
	return tmp;
}

bool checkInside(Line l, Point p) { // check whether point inside the line
	int lX = getMin(l.start.x, l.end.x);
	int rX = getMax(l.start.x, l.end.x);
	int lY = getMin(l.start.y, l.end.y);
	int rY = getMax(l.start.y, l.end.y);
	if(lX<=p.x && p.x<=rX && lY<=p.y && p.y<=rY) {
		return true;
	} else {
		return false;
	}
}

// if direct(a,b) is clockwise, 180<=<a,b><=360, sin<a,b> <= 0
// while anticlockwise 0<=<a,b><=180, sin<a,b> >=0
int checkDirect(Line l, Point p) {
	Vect v1 = makeVect(p,l.start);
	Vect v2 = makeVect(p,l.end);
	int direct = v1.x*v2.y - v2.x*v1.y;  // cout the direct of v1 and v2 vector
	if(direct == 0) {
		if(checkInside(l,p)) {
			return 2;
		} else {
			return 0;
		}
	} else {
		return direct>0?1:-1;
	}
}

bool checkCross(Line l1, Line l2) {
#ifdef TEST 
	cout<<"checkDirect(l2, l1.start) = "<<checkDirect(l2, l1.start)<<" compare  checkDirect(l2, l1.end) = "<< checkDirect(l2, l1.end)<<endl;
	cout<<"checkDirect(l1, l2.start) = "<<checkDirect(l1, l2.start)<<" compare  checkDirect(l1, l2.end) = "<< checkDirect(l1, l2.end)<<endl;
#endif
	if(checkDirect(l2, l1.start) == 2 || checkDirect(l2, l1.end)==2 || checkDirect(l1, l2.start) == 2 || checkDirect(l1, l2.end) == 2) {
		return true;
	}
// The necessary and sufficient condition is direct(AC,AD)!=direct(BC,BD) && direct(CA,CB)!=direct(DA,DB)
	if(checkDirect(l2, l1.start) != checkDirect(l2, l1.end)) {
		if(checkDirect(l1, l2.start)!= checkDirect(l1, l2.end)) {
			return true;
		}
	}
	return false;
}


void printResult() {
	cout<<"result:"<<endl;
	int i,j;
	for(i=0;i<NUM;i++) {
		for(j=0;j<NUM;j++) {
			cout<<result[i][j]<<" ";
		}
		cout<<endl;
	}
}

void printAnswer() {
	int i,j;
	char def = 'A';
	bool mark=false;
	for(i=0;i<NUM;i++) {
		for(j=i+1;j<NUM;j++) {
			if(result[i][j]==1)
			{
				ans[i]=1;
				ans[j]=1;
				mark = true;
			}
		}
	}
	if(mark == false) {
		cout<<"X";
	} else {
		for(i=0;i<NUM;i++) {
			if(ans[i]==1){
				cout<<(char)(def+i);
			}
		}
	}
	cout<<endl;
#ifdef TEST
	cout<<"ans:"<<endl;
	for(i=0;i<NUM;i++) {
		cout<<ans[i]<<" ";
	}
	cout<<endl;
#endif
}

void init() {
	int i,j;
	for(i=0;i<NUM;i++) {
		for(j=0;j<NUM;j++) {
			if(i==j) {
				result[i][j] = -1;
			} else {
				result[i][j] = 0;
			}
		}
		ans[i] = 0;
	}
}

int main(int argc, char** argv)
{
	int tc, T;
	int i,j;
	freopen("input.txt", "r", stdin);

	cin >> T;
	for(tc = 0; tc < T; tc++)
	{
		init();
		for(i=0; i<NUM; i++) {
			cin>>line[i].start.x;
			cin>>line[i].start.y;
			cin>>line[i].end.x;
			cin>>line[i].end.y;
			line[i].v.x = line[i].end.x - line[i].start.x;
			line[i].v.y = line[i].end.y - line[i].start.y;
		}
		for(i=0;i<NUM;i++) {
			for(j=i+1;j<NUM;j++) {
				if(i!=j) {
					bool r = checkCross(line[i],line[j]);
					if(r == true) {
						result[i][j] = 1;
						result[j][i] = 1;
					}else {
						result[i][j] = 0;
						result[j][i] = 0;
					}
				}
			}
		}
#ifdef TEST
		printResult();
#endif
		printAnswer();
	}
	return 0;//Your program should return 0 on normal termination.
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值