Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to calculate the distance from a point to the nearest point of a rectange?

I have an axis-aligned rectangle in a 2d coordinate system represented by the point on the lower left and the point on the upper right, as well as a point which might be inside or outside of the rectangle. I want to find the distance of the point to the nearest point of the rectangle, regardless of whether it is inside the rectangle or not. Of course I could just write a switch case with 9 different outcomes but I am hoping that there is a more elegant solution.
Also, I have found multiple solutions to this problem (like this one), but all of them would calculate the distance as 0 if the point is inside the box, which I don't want.

like image 782
ddominnik Avatar asked Oct 21 '25 03:10

ddominnik


2 Answers

My answer is slightly longer than the others, but it comes from a different perspective.

The key isn't if you're inside the rectangle, but if you're anywhere within the corridors defined by taking the sides of the rectangle and extending them infinitely (picture an infinite plus sign, centered on the rectangle).

If it's inside those corridors, then the closest distance is orthogonal to one of the sides.

If it's outside, then the closest distance is the distance to the nearest corner.

Your code could look like this:

nearest_distance(rectangle, point):
    d_top = abs(rectangle.top - point.y)
    d_bottom = abs(rectangle.bottom - point.y)
    corner_y = d_top < d_bottom ? rectangle.top : rectangle.bottom

    d_left = abs(rectangle.left - point.x)
    d_right = abs(rectangle.right - point.x)
    corner_x = d_left < d_right ? rectangle.left : rectangle.right

    d_cx = corner_x - point.x
    d_cy = corner_y - point.y
    d_corner = sqrt(d_cx*d_cx + d_cy*d_cy)

    return min(d_top, d_bottom, d_left, d_right, d_corner)

If you wanted to try to save a sqrt, you could check if you're inside the corridors vs. outside of them. In that case, you would rearrange it as follows:

nearest_distance(rectangle, point):
    d_top = abs(rectangle.top - point.y)
    d_bottom = abs(rectangle.bottom - point.y)
    d_left = abs(rectangle.left - point.x)
    d_right = abs(rectangle.right - point.x)

    r = rectangle # just to make the next line neater
    if r.left <= point.x <= r.right or r.bottom <= point.y <= r.top:
        return min(d_top, d_bottom, d_left, d_right)
    else:
        corner_y = d_top < d_bottom ? rectangle.top : rectangle.bottom
        corner_x = d_left < d_right ? rectangle.left : rectangle.right

        d_cx = corner_x - point.x
        d_cy = corner_y - point.y
        d_corner = sqrt(d_cx*d_cx + d_cy*d_cy)
        return d_corner
like image 140
Scott Mermelstein Avatar answered Oct 24 '25 19:10

Scott Mermelstein


You could extend the linked solution of MultiRRomero and do some additional computations for points inside the rectangle.

For these points the closest point on the boundary of the rectangle has the same x or y coordinate as the point. So computing the distances to the lines is straight forward and the smallest will be the desired distance.

function distance(rect, p) {
  var dx = Math.max(rect.min.x - p.x, 0, p.x - rect.max.x);
  var dy = Math.max(rect.min.y - p.y, 0, p.y - rect.max.y);
  var distance = Math.sqrt(dx*dx + dy*dy)
  if (distance == 0) {
    distance = Math.min(p.x - rect.min.x, rect.max.x - p.x, p.y - rect.min.y, rect.max.y - p.y)
  }
  return distance
}

Edit: typos fixed

like image 34
pH 74 Avatar answered Oct 24 '25 18:10

pH 74



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!