我需要一个基本函数来求点到线段的最短距离。你可以随意用任何你想要的语言来编写解决方案;我可以把它翻译成什么我正在使用(Javascript)。
编辑:线段由两个端点定义。线段AB由两点A (x1,y1)和B (x2,y2)定义。我要求的是这条线段到点C (x3,y3)的距离。我的几何技能生疏了,所以我看到的例子让我很困惑,我很遗憾地承认。
我需要一个基本函数来求点到线段的最短距离。你可以随意用任何你想要的语言来编写解决方案;我可以把它翻译成什么我正在使用(Javascript)。
编辑:线段由两个端点定义。线段AB由两点A (x1,y1)和B (x2,y2)定义。我要求的是这条线段到点C (x3,y3)的距离。我的几何技能生疏了,所以我看到的例子让我很困惑,我很遗憾地承认。
当前回答
基于Joshua Javascript的AutoHotkeys版本:
plDist(x, y, x1, y1, x2, y2) {
A:= x - x1
B:= y - y1
C:= x2 - x1
D:= y2 - y1
dot:= A*C + B*D
sqLen:= C*C + D*D
param:= dot / sqLen
if (param < 0 || ((x1 = x2) && (y1 = y2))) {
xx:= x1
yy:= y1
} else if (param > 1) {
xx:= x2
yy:= y2
} else {
xx:= x1 + param*C
yy:= y1 + param*D
}
dx:= x - xx
dy:= y - yy
return sqrt(dx*dx + dy*dy)
}
其他回答
忍不住用python来编码:)
from math import sqrt, fabs
def pdis(a, b, c):
t = b[0]-a[0], b[1]-a[1] # Vector ab
dd = sqrt(t[0]**2+t[1]**2) # Length of ab
t = t[0]/dd, t[1]/dd # unit vector of ab
n = -t[1], t[0] # normal unit vector to ab
ac = c[0]-a[0], c[1]-a[1] # vector ac
return fabs(ac[0]*n[0]+ac[1]*n[1]) # Projection of ac to n (the minimum distance)
print pdis((1,1), (2,2), (2,0)) # Example (answer is 1.414)
fortran也是一样:)
real function pdis(a, b, c)
real, dimension(0:1), intent(in) :: a, b, c
real, dimension(0:1) :: t, n, ac
real :: dd
t = b - a ! Vector ab
dd = sqrt(t(0)**2+t(1)**2) ! Length of ab
t = t/dd ! unit vector of ab
n = (/-t(1), t(0)/) ! normal unit vector to ab
ac = c - a ! vector ac
pdis = abs(ac(0)*n(0)+ac(1)*n(1)) ! Projection of ac to n (the minimum distance)
end function pdis
program test
print *, pdis((/1.0,1.0/), (/2.0,2.0/), (/2.0,0.0/)) ! Example (answer is 1.414)
end program test
JavaScript中一个基于这个公式的更简洁的解决方案:
distToSegment: function (point, linePointA, linePointB){
var x0 = point.X;
var y0 = point.Y;
var x1 = linePointA.X;
var y1 = linePointA.Y;
var x2 = linePointB.X;
var y2 = linePointB.Y;
var Dx = (x2 - x1);
var Dy = (y2 - y1);
var numerator = Math.abs(Dy*x0 - Dx*y0 - x1*y2 + x2*y1);
var denominator = Math.sqrt(Dx*Dx + Dy*Dy);
if (denominator == 0) {
return this.dist2(point, linePointA);
}
return numerator/denominator;
}
嘿,我昨天才写的。它在Actionscript 3.0中,基本上是Javascript,尽管你可能没有相同的Point类。
//st = start of line segment
//b = the line segment (as in: st + b = end of line segment)
//pt = point to test
//Returns distance from point to line segment.
//Note: nearest point on the segment to the test point is right there if we ever need it
public static function linePointDist( st:Point, b:Point, pt:Point ):Number
{
var nearestPt:Point; //closest point on seqment to pt
var keyDot:Number = dot( b, pt.subtract( st ) ); //key dot product
var bLenSq:Number = dot( b, b ); //Segment length squared
if( keyDot <= 0 ) //pt is "behind" st, use st
{
nearestPt = st
}
else if( keyDot >= bLenSq ) //pt is "past" end of segment, use end (notice we are saving twin sqrts here cuz)
{
nearestPt = st.add(b);
}
else //pt is inside segment, reuse keyDot and bLenSq to get percent of seqment to move in to find closest point
{
var keyDotToPctOfB:Number = keyDot/bLenSq; //REM dot product comes squared
var partOfB:Point = new Point( b.x * keyDotToPctOfB, b.y * keyDotToPctOfB );
nearestPt = st.add(partOfB);
}
var dist:Number = (pt.subtract(nearestPt)).length;
return dist;
}
此外,这里有一个关于这个问题的相当完整和可读的讨论:notejot.com
这里它使用Swift
/* Distance from a point (p1) to line l1 l2 */
func distanceFromPoint(p: CGPoint, toLineSegment l1: CGPoint, and l2: CGPoint) -> CGFloat {
let A = p.x - l1.x
let B = p.y - l1.y
let C = l2.x - l1.x
let D = l2.y - l1.y
let dot = A * C + B * D
let len_sq = C * C + D * D
let param = dot / len_sq
var xx, yy: CGFloat
if param < 0 || (l1.x == l2.x && l1.y == l2.y) {
xx = l1.x
yy = l1.y
} else if param > 1 {
xx = l2.x
yy = l2.y
} else {
xx = l1.x + param * C
yy = l1.y + param * D
}
let dx = p.x - xx
let dy = p.y - yy
return sqrt(dx * dx + dy * dy)
}
Lua: 查找线段(不是整条线)与点之间的最小距离
function solveLinearEquation(A1,B1,C1,A2,B2,C2)
--it is the implitaion of a method of solving linear equations in x and y
local f1 = B1*C2 -B2*C1
local f2 = A2*C1-A1*C2
local f3 = A1*B2 -A2*B1
return {x= f1/f3, y= f2/f3}
end
function pointLiesOnLine(x,y,x1,y1,x2,y2)
local dx1 = x-x1
local dy1 = y-y1
local dx2 = x-x2
local dy2 = y-y2
local crossProduct = dy1*dx2 -dx1*dy2
if crossProduct ~= 0 then return false
else
if ((x1>=x) and (x>=x2)) or ((x2>=x) and (x>=x1)) then
if ((y1>=y) and (y>=y2)) or ((y2>=y) and (y>=y1)) then
return true
else return false end
else return false end
end
end
function dist(x1,y1,x2,y2)
local dx = x1-x2
local dy = y1-y2
return math.sqrt(dx*dx + dy* dy)
end
function findMinDistBetnPointAndLine(x1,y1,x2,y2,x3,y3)
-- finds the min distance between (x3,y3) and line (x1,y2)--(x2,y2)
local A2,B2,C2,A1,B1,C1
local dx = y2-y1
local dy = x2-x1
if dx == 0 then A2=1 B2=0 C2=-x3 A1=0 B1=1 C1=-y1
elseif dy == 0 then A2=0 B2=1 C2=-y3 A1=1 B1=0 C1=-x1
else
local m1 = dy/dx
local m2 = -1/m1
A2=m2 B2=-1 C2=y3-m2*x3 A1=m1 B1=-1 C1=y1-m1*x1
end
local intsecPoint= solveLinearEquation(A1,B1,C1,A2,B2,C2)
if pointLiesOnLine(intsecPoint.x, intsecPoint.y,x1,y1,x2,y2) then
return dist(intsecPoint.x, intsecPoint.y, x3,y3)
else
return math.min(dist(x3,y3,x1,y1),dist(x3,y3,x2,y2))
end
end