欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

计算几何学 | 判断线段相交 | Intersection | C/C++实现

程序员文章站 2022-04-01 13:32:09
...

问题描述

对于线段s1、s2,如果相交则输出“1”,否则输出“0”。
设s1的端点为p0、p1、,s2的端点为p2、p3。

输入:
第1行输入问题数q。接下来q行给出q个问题。各问题线段s1、s2的坐标按照以下格式给出:
xp0x_{p0} yp0y_{p0} xp1x_{p1} yp1y_{p1} xp2x_{p2} yp2y_{p2} xp3x_{p3} yp3y_{p3}
输出:
根据各问题输出1或0,每个问题占1行。
限制:
1 ≤ q ≤ 1000
-10000 ≤ xpi,ypix_{p_i},y_{p_i} ≤ 10000
p0、p1不是同一个点
p2、p3不是同一个点。

输入示例

3
0 0 3 0 1 1 2 -1
0 0 3 0 3 1 3 -1
0 0 3 0 3 -2 5 0

输出示例

1
1
0

讲解

这些情况均视为相交:

普通相交
一条线段的端点位于另一条线段上
两条线段公用一个端点
两条线段平行重合

前面讲的ccw用于检查点线间的位置关系(ccw详细解释:计算几何学 | 逆时针方向 | Counter-Clockwise | C/C++实现),只要对其加以应用,我们就能轻松判断出两条线段是否相交。

分别检查两条线段,如果双方都符合“另一条线段的两个端点分别位于当前线段的顺时针方向和逆时针方向”,则两条线段相交。

判断线段p1p2与线段p3p4是否相交的程序可以像下面这样写

判断线段p1p2和线段p3p4是否相交:

bool intersect(Point p1, Point p2, Point p3, Point p4) {
	return ( ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 &&
			ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0 );
}

bool intersect(Segment s1, Segment s2) {
	return intersect(s1.p1, s1.p2, s2.p1, s2.p2);
}

上述程序以四个点p1、p2、p3、p4为参数,判断线段p1p2与线段p3p4是否相交,若相交则返回true。

ccw(p1, p2, p3) * ccw(p1, p2, p4)用来分别检查p3、p4相对于线段p1p2的位置,然后求出结果的积。只要实现将ccw的返回值定义为
COUNTER_CLOCKWISE = -1;
CLOCKWISE = 1;
ON_SEGMENT = 0;
ccw(p1, p2, p3) * ccw(p1, p2, p4)在p3、p4位于不同侧时就会得出-1,p3或p4位于线段p1p2上时得出0.点p1、p2相对于线段p3p4的位置也是同理。接下来,只要线段p1p2、p3p4的判断均小于等于0,即可确定它们相交。

AC代码如下

#include<stdio.h>
#include<iostream>
#include<cmath>
using namespace std;

#define EPS (1e-10)
#define equals(a, b) (fabs((a) - (b)) < EPS)

class Point {//Point类,点 
	public:
		double x, y;
		
		Point(double x = 0, double y = 0): x(x), y(y) {}

		Point operator + (Point p) { return Point(x + p.x, y + p.y); }
		Point operator - (Point p) { return Point(x - p.x, y - p.y); }
		Point operator * (double a) { return Point(a * x, a * y); }
		Point operator / (double a) { return Point(x / a, y / a); }

		double abs() { return sqrt(norm()); }
		double norm() { return x * x + y * y; }
		
		bool operator < (const Point &p) const {
			return x != p.x ? x < p.x : y < p.y;
		}

		bool operator == (const Point &p) const {
			return fabs(x - p.x) < EPS && fabs(y - p.y) < EPS;
		}
};

typedef Point Vector;//Vector类,向量 

struct Segment{//Segment 线段 
	Point p1, p2;
};
double dot(Vector a, Vector b) {//内积 
	return a.x * b.x + a.y * b.y;
}

double cross(Vector a, Vector b) {//外积 
	return a.x*b.y - a.y*b.x;
}

Point reflect(Segment s, Point p) {//映像 以线段s为对称轴与点p成先对称的点
//对于三个点p1、p2、p,设以通过p1、p2的直线为对称轴与点p成线对称的点为x,
//求点x的坐标(点p对于直线p1p2的映像) 
	return p + (project(s, p) - p) * 2.0;
} 
*/
static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -2;
static const int ON_SEGMENT = 0;

int ccw(Point p0, Point p1, Point p2) {
	Vector a = p1 - p0;
	Vector b = p2 - p0;
	if( cross(a, b) > EPS ) return COUNTER_CLOCKWISE;
	if( cross(a, b) < -EPS ) return CLOCKWISE;
	if( dot(a, b) < -EPS ) return ONLINE_BACK;
	if( a.norm() < b.norm() ) return ONLINE_FRONT;

	return ON_SEGMENT;
}

bool intersect(Point p1, Point p2, Point p3, Point p4) {
	return ( ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 &&
			ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0 );
}

bool intersect(Segment s1, Segment s2) {
	return intersect(s1.p1, s1.p2, s2.p1, s2.p2);
}

int main(){
	int q;
	cin>>q;
	
	Segment s1, s2;
	
	while(q--){
		cin>>s1.p1.x>>s1.p1.y>>s1.p2.x>>s1.p2.y>>s2.p1.x>>s2.p1.y>>s2.p2.x>>s2.p2.y;
		cout<<intersect(s1, s2)<<endl;
	}
} 

注:以上本文未涉及代码的详细解释参见:计算几何学