JS:求點與線段的最短距離,並返回該最短距離線上段上的座標。
直接上程式碼:
function PointToLineDistance (xx, yy, x1, y1, x2, y2) {
let ang1, ang2, ang, m;
let result = 0;
// 分別計算三條邊的長度
const a = Math.sqrt((x1 - xx) * (x1 - xx) + (y1 - yy) * (y1 - yy));
if (a === 0) {
return [0, { x: x1,
y: y1 }];
}
const b = Math.sqrt((x2 - xx) * (x2 - xx) + (y2 - yy) * (y2 - yy));
if (b === 0) {
return [0, { x: x2,
y: y2 }];
}
const c = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
// 如果線段是一個點則退出函式並返回距離
if (c === 0) {
result = a;
return [result, { x: x1,
y: y1 }];
}
// 如果點(xx,yy到點x1,y1)這條邊短
if (a < b) {
// 如果直線段AB是水平線。得到直線段AB的弧度
if (y1 === y2) {
if (x1 < x2) {
ang1 = 0;
} else {
ang1 = Math.PI;
}
} else {
m = (x2 - x1) / c;
if (m - 1 > 0.00001) {
m = 1;
}
ang1 = Math.acos(m);
if (y1 > y2) {
ang1 = Math.PI * 2 - ang1;
}// 直線(x1,y1)-(x2,y2)與折X軸正向夾角的弧度
}
m = (xx - x1) / a;
if (m - 1 > 0.00001) {
m = 1;
}
ang2 = Math.acos(m);
if (y1 > yy) {
ang2 = Math.PI * 2 - ang2;
}// 直線(x1,y1)-(xx,yy)與折X軸正向夾角的弧度
ang = ang2 - ang1;
if (ang < 0) {
ang = -ang;
}
if (ang > Math.PI) {
ang = Math.PI * 2 - ang;
}
// 如果是鈍角則直接返回距離
if (ang > Math.PI / 2) {
return [a, { x: x1,
y: y1 }];
}
// 返回距離並且求得當前距離所線上段的座標
if (x1 === x2) {
return [b * Math.sin(ang), { x: x1,
y: yy }];
} else if (y1 === y2) {
return [b * Math.sin(ang), { x: xx,
y: y1 }];
}
// 直線的斜率存在且不為0的情況下
let x = 0,
y = 0;
const k1 = ((y2 - y1) / x2 - x1);
const kk = -1 / k1;
const bb = yy - xx * kk;
const b1 = y2 - x2 * k1;
x = (b1 - bb) / (kk - k1);
y = kk * x + bb;
return [a * Math.sin(ang), { x,
y }];
}
// 如果兩個點的縱座標相同,則直接得到直線斜率的弧度
if (y1 === y2) {
if (x1 < x2) {
ang1 = Math.PI;
} else {
ang1 = 0;
}
} else {
m = (x1 - x2) / c;
if (m - 1 > 0.00001) {
m = 1;
}
ang1 = Math.acos(m);
if (y2 > y1) {
ang1 = Math.PI * 2 - ang1;
}
}
m = (xx - x2) / b;
if (m - 1 > 0.00001) {
m = 1;
}
ang2 = Math.acos(m);// 直線(x2-x1)-(xx,yy)斜率的弧度
if (y2 > yy) {
ang2 = Math.PI * 2 - ang2;
}
ang = ang2 - ang1;
if (ang < 0) {
ang = -ang;
}
if (ang > Math.PI) {
ang = Math.PI * 2 - ang;
}// 交角的大小
// 如果是對角則直接返回距離
if (ang > Math.PI / 2) {
return [b, { x: x2,
y: y2 }];
}
// 如果是銳角,返回計算得到的距離,並計算出相應的座標
if (x1 === x2) {
return [b * Math.sin(ang), { x: x1,
y: yy }];
} else if (y1 === y2) {
return [b * Math.sin(ang), { x: xx,
y: y1 }];
}
// 直線的斜率存在且不為0的情況下
let x = 0,
y = 0;
const k1 = ((y2 - y1) / x2 - x1);
const kk = -1 / k1;
const bb = yy - xx * kk;
const b1 = y2 - x2 * k1;
x = (b1 - bb) / (kk - k1);
y = kk * x + bb;
return [b * Math.sin(ang), { x,
y }];
}