1. 程式人生 > >[計算幾何] (平面上)兩線段最短距離 向量法

[計算幾何] (平面上)兩線段最短距離 向量法

知識都是環環相扣的, 在閱讀這編文章之前, 要求懂兩個知識點

1. 會求點到線段的最短距離     傳送門

2. 會判斷點與線段位置關係     傳送門

如果上面兩個知識點都懂, 那麼就進入正題了

給出點A1、A2的座標, 構成線段A1A2, 再給出點B1,B2的座標, 構成線段B1B2, 求線段A1A2與線段B1B2的最短距離

兩條線段的擺放有很多情況

(1) 兩線段相交成 X 型

(2) 兩線段相交成 T 型

(3) 兩線段相交成 ^ 型, 其中有兩點重合

(4) 四個點在一條直線上, 視為相交

(5) 兩線段不相交

判斷兩線段是否相交

(1)(2)(3)(4)是兩條線段相交的例子, 對於兩線段相交的情況, 距離為0, 只要對[判斷點與線段位置關係

]加以應用, 就能輕鬆判斷出兩線段是否相交。

分別檢查兩條線段, 如果雙方都符合"另一條線段的兩個端點分別位於當前線段的順時針方向和逆時針方向", 則兩條線段相交。

判斷線段A1A2與線段B1B2是否相交的程式可以像下面這樣寫

if (dir(A1, A2, B1)*dir(A1, A2, B2) <= 0 && dir(B1, B2, A1)*dir(B1, B2, A2) <= 0)  
    cout << "相交" << endl;  

只要事先將 dir 返回值定義為

逆時針             返回   -1

順時針             返回    1

反向延長線上  返回    -2

延長線上          返回    2

線段上              返回    0

dir(A1,A2,B1)*dir(A1,A2,B2) 在B1、B2位於不同側時就會得出 -1,B1或B2位於線段A1A2上時得出0。點A1、A2相對於線段B1B2的位置也是同理。接下來, 只要線段A1A2、B1B2的判斷均小於等於0, 即可確定他們相交。

若不相交, 求兩線段最短距離

在瞭解如何求點到線段的最短距離, 這就好辦了

線段A1A2與線段B1B2的距離為以下四個距離中最小的一個

1.點A1到線段B1B2的距離

2.點A2到線段B1B2的距離

3.點B1到線段A1A2的距離

4.點B2到線段A1A2的距離

程式程式碼參考

#include<iostream>
#include<cmath>
#include<algorithm>
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 dis2(NODE a, NODE b)                //點a、b距離的平方
{
	return (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;     //逆時針
	else if (cross(A, B, P)>0) return 1;   //順時針
	else if (dot(A, B, P) < 0) return -2;  //反延長線
	else if (dot(A, B, P) >= 0&&dis2(A,B)>=dis2(A,P))
	{
		if (dis2(A, B) < dis2(A, P)) return 2;  //延長線
		return 0;                          //在線上
	}                                      
}
double disMin(NODE A, NODE B, NODE P)      //點P到線段AB的最短距離
{
	double r = ((P.x-A.x)*(B.x-A.x) + (P.y-A.y)*(B.y-A.y)) / dis2(A, B);
	if (r <= 0) return sqrt(dis2(A, P));
	else if (r >= 1) return sqrt(dis2(B, P));
	else
	{
		double AC = r*sqrt(dis2(A,B));
		return sqrt(dis2(A,P)-AC*AC);
	}
}
int main()
{
	NODE A1, A2, B1, B2;
	cin >> A1.x >> A1.y;
	cin >> A2.x >> A2.y;
	cin >> B1.x >> B1.y;
	cin >> B2.x >> B2.y;
	if (dir(A1, A2, B1)*dir(A1, A2, B2) <= 0 && dir(B1, B2, A1)*dir(B1, B2, A2) <= 0)  //兩線段相交, 距離為0
		cout << 0 << endl;
	else                                                   //如不相交, 則最短距離為每個端點到另一條線段距離的最小值
		cout << min(min(min(disMin(A1, A2, B1), disMin(A1, A2, B2)), disMin(B1, B2, A1)),disMin(B1,B2,A2)) << endl;  
	return 0;
}

參考書籍: 挑戰程式設計競賽2