点到线段的最短位置

效果

计算点到线段的最短位置,效果如下:



代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
* 定义向量
*/
var Vec2 = function(x, y) {
this.x = x;
this.y = y;
};
Vec2.prototype.add = function(vector, out) {
out = out || new Vec2();
out.x = this.x + vector.x;
out.y = this.y + vector.y;
return out;
};

Vec2.prototype.sub = function (vector, out) {
out = out || new Vec2();
out.x = this.x - vector.x;
out.y = this.y - vector.y;
return out;
};

Vec2.prototype.magSqr = function () {
return this.x * this.x + this.y * this.y;
};

Vec2.prototype.mul = function (num, out) {
out = out || new Vec2();
out.x = this.x * num;
out.y = this.y * num;
return out;
};

Vec2.prototype.dot = function (vector) {
return this.x * vector.x + this.y * vector.y;
};

Vec2.prototype.project = function (vector) {
return vector.mul(this.dot(vector) / vector.dot(vector));
};

/**
* 计算点到直线距离最短的点(垂足或离点最近的两端点)
* @param {*} x
* @param {*} y
* @param {*} x1
* @param {*} y1
* @param {*} x2
* @param {*} y2
*/
function calcShortestPoint(x, y, x1, y1, x2, y2) {
var op = new Vec2(x, y);
var op1 = new Vec2(x1, y1);
var op2 = new Vec2(x2, y2);

// 做垂足
var p1p2 = op2.sub(op1);
var p1p = op.sub(op1);
var p2p = op.sub(op2);
var proj_pp2_p1p2 = p2p.project(p1p2);
var ot = op2.add(proj_pp2_p1p2); // t的坐标
// 计算距离
var pt = op.sub(ot);
var tp1 = op1.sub(ot);
var tp2 = op2.sub(ot);
var len2_pp1 = p1p.magSqr();
var len2_pp2 = p2p.magSqr();
var len2_pt = pt.magSqr();
var pos = [op1, op2, ot][[len2_pp1, len2_pp2, len2_pt].indexOf(Math.min(len2_pp1, len2_pp2, len2_pt))];
// 判断垂足点在线段内
if (tp1.magSqr() + tp2.magSqr() - p1p2.magSqr() > 0) {
pos = [op1, op2][[len2_pp1, len2_pp2].indexOf(Math.min(len2_pp1, len2_pp2))];
}
return pos;
}

算法解析

点到直线的最短距离为点到直线的垂线段的长度,所以点到直线的最短位置即为垂足的位置

所以,当垂足在线段内时,垂足即为所求的点,否则为线段两端点中距离最近的点,问题转化为求垂足的位置。

使用解析式求垂足计算量巨大,这里采用向量运算求解。

求垂足

pic

如图,已知点 P 和线段 p1p2 可知,垂足 T 的坐标即为向量 P1P 在向量 P1P2投影的点的坐标。

判断点是否在线段内

由于已知 p1、t、p2共线,故当线段 p1t 长度 + 线段 tp2 长度大于线段 p1p2 长度时,点 T 不在线段内。