Skip to content

193. Shortest distance from a point to a line

January 18, 2015

A recent forum thread asked how to find the shortest distance from a point to a line. As usual, our resident mathematician was there to provide the answer.

In fact, two answers.

A long one,

-- Returns the distance from the point p to the line segment from a to b
-- p,a,b are all vec2s
function lineDist(p,a,b)
    -- Vector along the direction of the line from a to b
    local v = b - a
    -- Project p onto the vector v and compare with the projections of a and b
    if v:dot(p) < v:dot(a) then     -- The projection of p is lower than a, so return the distance from p to a         return p:dist(a)     elseif v:dot(p) > v:dot(b) then
    -- The projection of p is beyond b, so return the distance from p to b
        return p:dist(b)
    -- The projection of p is between a and b, so we need to take the dot product with the orthogonal vector to v, so we rotate v and normalise it.
    v = v:rotate90():normalize()
    -- This is the distance from v to the line segment.
    return math.abs(v:dot(p)-v:dot(a))

and a shorter version (one line of code).

function lineDistB(p,a,b)
    return (p-a):dist(math.max(math.min((p-a):dot(b-a)/(b-a):lenSqr(),1),0)*(b-a))

plus a bonus function for getting the intersection point

function lineIntersectPoint(p,a,b)
    return math.max(math.min((p-a):dot(b-a)/(b-a):lenSqr(),1),0)*(b-a)+a

Here is his explanation of the solution. Any blue text is my comment.

Dot products are the heart of geometry. For this particular problem, the key piece of information is that if you have a unit vector, say u, then the orthogonal projection of a point, say p, onto the line in the direction of u is (p.u) u. If v is a vector which is not necessarily a unit vector, the formula is (p.v) v / (v.v).

So in your code, you first work out the vector along the line (this is the dx = x2 - x1 dy = y2 - y1 part).

Then you work out the square of the length of this vector (temp = dx * dx + dy * dy); this is the same as the dot product of (dx,dy) with itself.

Next, you work out the projection of (px,py) onto the line in the direction of (dx,dy) but as our line doesn’t pass through the origin, we have to adjust it slightly by projecting (px,py) - (x1,y1) so that everything is relative to one end of the line (this is the a=((px-x1)*dx+(py-y1)*dy)/temp part). So in dot product terms, your variable a contains (p.v) / (v.v).

Since we’re dealing with a line segment, we test whether this a is between 0 and 1 and clamp it to that range (I’d probably do a a = math.max(math.min(a,1),0) instead of the conditionals here).

Once you’ve clamped, you compute the point (x1,y1) + a (dx,dy) which is the closest point to (px,py) on the line segment.

Finally, you return its distance from (px,py).

Mathematically, you compute

||p - Max(Min(p.(b - a)/(b-a).(b-a),1),0)(b-a)||

lf you didn’t understand all that, well, nor did I, but I filed the function away in case I ever need it, and I thought I’d share it, because forum threads get buried very quickly.



From → Graphics, Programming

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: