我需要一个基本函数来求点到线段的最短距离。你可以随意用任何你想要的语言来编写解决方案;我可以把它翻译成什么我正在使用(Javascript)。

编辑:线段由两个端点定义。线段AB由两点A (x1,y1)和B (x2,y2)定义。我要求的是这条线段到点C (x3,y3)的距离。我的几何技能生疏了,所以我看到的例子让我很困惑,我很遗憾地承认。


当前回答

现在我的解决方案...... (Javascript)

这是非常快的,因为我试图避免任何数学。战俘的功能。

如你所见,在函数的最后,我得到了直线的距离。

代码来自lib http://www.draw2d.org/graphiti/jsdoc/#!/例子

/**
 * Static util function to determine is a point(px,py) on the line(x1,y1,x2,y2)
 * A simple hit test.
 * 
 * @return {boolean}
 * @static
 * @private
 * @param {Number} coronaWidth the accepted corona for the hit test
 * @param {Number} X1 x coordinate of the start point of the line
 * @param {Number} Y1 y coordinate of the start point of the line
 * @param {Number} X2 x coordinate of the end point of the line
 * @param {Number} Y2 y coordinate of the end point of the line
 * @param {Number} px x coordinate of the point to test
 * @param {Number} py y coordinate of the point to test
 **/
graphiti.shape.basic.Line.hit= function( coronaWidth, X1, Y1,  X2,  Y2, px, py)
{
  // Adjust vectors relative to X1,Y1
  // X2,Y2 becomes relative vector from X1,Y1 to end of segment
  X2 -= X1;
  Y2 -= Y1;
  // px,py becomes relative vector from X1,Y1 to test point
  px -= X1;
  py -= Y1;
  var dotprod = px * X2 + py * Y2;
  var projlenSq;
  if (dotprod <= 0.0) {
      // px,py is on the side of X1,Y1 away from X2,Y2
      // distance to segment is length of px,py vector
      // "length of its (clipped) projection" is now 0.0
      projlenSq = 0.0;
  } else {
      // switch to backwards vectors relative to X2,Y2
      // X2,Y2 are already the negative of X1,Y1=>X2,Y2
      // to get px,py to be the negative of px,py=>X2,Y2
      // the dot product of two negated vectors is the same
      // as the dot product of the two normal vectors
      px = X2 - px;
      py = Y2 - py;
      dotprod = px * X2 + py * Y2;
      if (dotprod <= 0.0) {
          // px,py is on the side of X2,Y2 away from X1,Y1
          // distance to segment is length of (backwards) px,py vector
          // "length of its (clipped) projection" is now 0.0
          projlenSq = 0.0;
      } else {
          // px,py is between X1,Y1 and X2,Y2
          // dotprod is the length of the px,py vector
          // projected on the X2,Y2=>X1,Y1 vector times the
          // length of the X2,Y2=>X1,Y1 vector
          projlenSq = dotprod * dotprod / (X2 * X2 + Y2 * Y2);
      }
  }
    // Distance to line is now the length of the relative point
    // vector minus the length of its projection onto the line
    // (which is zero if the projection falls outside the range
    //  of the line segment).
    var lenSq = px * px + py * py - projlenSq;
    if (lenSq < 0) {
        lenSq = 0;
    }
    return Math.sqrt(lenSq)<coronaWidth;
};

其他回答

这里它使用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)
}

I'm assuming you want to find the shortest distance between the point and a line segment; to do this, you need to find the line (lineA) which is perpendicular to your line segment (lineB) which goes through your point, determine the intersection between that line (lineA) and your line which goes through your line segment (lineB); if that point is between the two points of your line segment, then the distance is the distance between your point and the point you just found which is the intersection of lineA and lineB; if the point is not between the two points of your line segment, you need to get the distance between your point and the closer of two ends of the line segment; this can be done easily by taking the square distance (to avoid a square root) between the point and the two points of the line segment; whichever is closer, take the square root of that one.

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

只是遇到了这个,我想我应该添加一个Lua实现。它假设点以表{x=xVal, y=yVal}给出,直线或线段由包含两个点的表给出(见下面的例子):

function distance( P1, P2 )
    return math.sqrt((P1.x-P2.x)^2 + (P1.y-P2.y)^2)
end

-- Returns false if the point lies beyond the reaches of the segment
function distPointToSegment( line, P )
    if line[1].x == line[2].x and line[1].y == line[2].y then
        print("Error: Not a line!")
        return false
    end

    local d = distance( line[1], line[2] )

    local t = ((P.x - line[1].x)*(line[2].x - line[1].x) + (P.y - line[1].y)*(line[2].y - line[1].y))/(d^2)

    local projection = {}
    projection.x = line[1].x + t*(line[2].x-line[1].x)
    projection.y = line[1].y + t*(line[2].y-line[1].y)

    if t >= 0 and t <= 1 then   -- within line segment?
        return distance( projection, {x=P.x, y=P.y} )
    else
        return false
    end
end

-- Returns value even if point is further down the line (outside segment)
function distPointToLine( line, P )
    if line[1].x == line[2].x and line[1].y == line[2].y then
        print("Error: Not a line!")
        return false
    end

    local d = distance( line[1], line[2] )

    local t = ((P.x - line[1].x)*(line[2].x - line[1].x) + (P.y - line[1].y)*(line[2].y - line[1].y))/(d^2)

    local projection = {}
    projection.x = line[1].x + t*(line[2].x-line[1].x)
    projection.y = line[1].y + t*(line[2].y-line[1].y)

    return distance( projection, {x=P.x, y=P.y} )
end

使用示例:

local P1 = {x = 0, y = 0}
local P2 = {x = 10, y = 10}
local line = { P1, P2 }
local P3 = {x = 7, y = 15}
print(distPointToLine( line, P3 ))  -- prints 5.6568542494924
print(distPointToSegment( line, P3 )) -- prints false

在f#中,点c到a和b之间的线段的距离为:

let pointToLineSegmentDistance (a: Vector, b: Vector) (c: Vector) =
  let d = b - a
  let s = d.Length
  let lambda = (c - a) * d / s
  let p = (lambda |> max 0.0 |> min s) * d / s
  (a + p - c).Length

向量d沿着线段从a指向b。d/s与c-a的点积给出了无限直线与点c之间最接近点的参数。使用min和max函数将该参数钳制到范围0..s,使该点位于a和b之间。最后,a+p-c的长度是c到线段上最近点的距离。

使用示例:

pointToLineSegmentDistance (Vector(0.0, 0.0), Vector(1.0, 0.0)) (Vector(-1.0, 1.0))