[计算几何] (平面上)点与线段的位置 矢量法

给出点A、B的坐标, 构成线段AB, 再给出一点P的坐标, 判断点P与线段AB的位置关系

如下图, 点P与AB的关系可分为5种情况

(1) 点P在线段AB的顺时针方向

(2) 点P在线段AB的逆时针方向

(3) 点P在线段AB的反向延长线上

(4) 点P在线段AB的延长线上

(5) 点P在线段AB上

为了更好的解决此类问题, 借助两个工具: 向量的外积(叉积)和向量的内积(点积)

向量的外积(叉积)

a=(a_x,a_y,a_z), b=(b_x,b_y,b_z)的形式表示两个向量, 则 ab 的外积为

|a \times b|=(a_yb_z-a_zb_y, a_zb_x-a_xb_z, a_xb_y-a_yb_x) (可通过矩阵导出)

在二维平面上, z=0, 所以二维平面上向量a,b的外积的大小为

|a\times b|=|a||b|sin\theta =a_xb_y-a_yb_x

向量的内积(点积)

a\cdot b=|a||b|cos\theta =a_xb_x+a_yb_y

点与线段位置的判定标准

1. 如果向量AB与向量AP的外积(叉积)为正, 则点P在线段AB的逆时针方向, 如上图(1)

2. 如果向量AB与向量AP的外积(叉积)为负, 则点P在线段AB的顺时针方向, 如上图(2)

在上两种情况都不满足的情况下(讨论完外积(叉积), 下面讨论内积(点积))

注意: 必须上两种情况不满足才讨论以下情况

3. 如果向量AB与向量AP的内积(点积)为负, 则点P在线段AB的反向延长线上, 如上图(3)

4. 如果向量AB与向量AP的内积(点积)为非负, 有两种情况分别为上图(4) 和 (5)

4.1. 如果向量AB的模|AB|小于向量AP的模|AP|, 则点P在线段AB的延长线上, 如上图(4)

4.2. 如果向量AB的模|AB|等于或小于向量AP的模|AP|, 则点P在线段AB上, 如上图(5)     (|AB|=|AP|时, P点与B点重合)

程序代码

#include<iostream>
#include<cmath>
using namespace std;
typedef struct node
{
	double x, y;
}NODE;
double cross(NODE A, NODE B, NODE P)     //计算向量AB与AP的外积(叉积)的大小(不包括方向)
{
	NODE AB = { B.x - A.x, B.y - A.y };
	NODE AP = { P.x - A.x, P.y - A.y };
	return AB.x*AP.y - AB.y*AP.x;
}
double dot(NODE A, NODE B, NODE P)        //计算向量AB与向量AP的内积(点积)
{
	NODE AB = { B.x - A.x, B.y - A.y };
	NODE AP = { P.x - A.x, P.y - A.y };
	return AB.x*AP.x + AB.y*AP.y;
}
double mol(NODE a,NODE b)                //求向量ab的模(也可以说是点a、b的距离)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int dir(NODE A,NODE B,NODE P)            //判断点P在线段AB的哪个方向上
{
	if (cross(A, B, P) > 0) return 1;      //外积大于0,点p在线段AB的逆时针方向
	else if (cross(A, B, P) < 0) return 2; //外积小于0,点p在线段AB的顺时针方向
	else if (dot(A, B, P) < 0) return 3;   //内积小于0,点p在线段AB的反延长线上
	else if (dot(A, B, P)>=0)              //内积大于0,分两种情况
	{
		if (mol(A, B) < mol(A, P))  return 4;  //如果AB的模小于AP的模, 那么p在线段AB的延长线上
		else  return 5;                    //p在线段AB上
	}
}
int main()
{
	NODE A, B, P;
	cin >> A.x >> A.y;
	cin >> B.x >> B.y;
	cin >> P.x >> P.y;
	if (dir(A, B, P) == 1) cout << "COUNTER_CLOCKWISE" << endl;  //逆时针
	else if (dir(A, B, P) == 2) cout << "CLOCKWISE" << endl;     //顺时针
	else if (dir(A, B, P) == 3) cout << "ONLINE_BACK" << endl;   //反向延长线
	else if (dir(A, B, P) == 4) cout << "ONLINE_FRONT" << endl;  //延长线
	else if (dir(A, B, P) == 5) cout << "ON_SEGMENT" << endl;    //线段上
	return 0;
}

如果觉得本文对你有启发, 不妨赞一下, 在精神上鼓励鼓励博主;

如果有不懂的地方, 可以在下方留言, 博主一看到便会马上回复;

如果发现本文有错误的地方, 欢迎指正。

参考书籍: 挑战程序设计竞赛2

猜你喜欢

转载自blog.csdn.net/Mr_HCW/article/details/82824166