知识都是环环相扣的, 在阅读这编文章之前, 要求懂两个知识点
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