1. 程式人生 > >[計算幾何] 點與線段的位置 向量法

[計算幾何] 點與線段的位置 向量法

給出點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;
	int q;
	cin >> q;
	while (q--)
	{
		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;
}

如果覺得本文對你有啟發, 不妨贊一下, 在精神上鼓勵鼓勵博主;

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

如果發現本文有錯誤的地方, 歡迎指正。