35
\$\begingroup\$

I have a 2D rectangle with x, y position, height and width, and a randomly positioned point nearby.

Is there a way to check if this point might collide with the rectangle if it is closer than a certain distance? Imagine an invisible radius outside of that point colliding with said rectangle. I have problems with this simply because it is not a square!

\$\endgroup\$

6 Answers 6

32
\$\begingroup\$

If (x,y) is the centre of the rectangle, the squared distance from a point (px,py) to the rectangle's border can be computed this way:

dx = max(abs(px - x) - width / 2, 0);
dy = max(abs(py - y) - height / 2, 0);
return dx * dx + dy * dy;

If that squared distance is zero, it means the point touches or is inside the rectangle.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Sorry for the old comment, but does this equation assume that the rectangle is axis-aligned? \$\endgroup\$
    – BitNinja
    Commented Oct 25, 2014 at 1:13
  • 1
    \$\begingroup\$ @BitNinja yes, that’s what the question assumes. If it’s not axis-aligned, the fastest/simplest algorithm will depend on how the rectangle information is stored. \$\endgroup\$ Commented Oct 25, 2014 at 13:47
  • \$\begingroup\$ say, point is (4:4), rectangle is at (5:5) with width/height (5:5). Your code would claim that the point touches or is inside the rectangle, but it's obviously outside \$\endgroup\$
    – LRN
    Commented Aug 3, 2017 at 12:54
  • 1
    \$\begingroup\$ @LRN a rectangle centered at (5:5) with width/height (5:5) spans from (2.5:2.5) to (7.5:7.5). The point (4:4) is inside that rectangle. \$\endgroup\$ Commented Aug 3, 2017 at 14:17
  • \$\begingroup\$ Ah, "centered at". I usually view rectangle coordinates as the coordinates of the top-left corner of the rectangle (if the coordinate system origin is in the top left corner of the space). You should mention that in the answer, i think. \$\endgroup\$
    – LRN
    Commented Sep 23, 2017 at 18:42
12
\$\begingroup\$

I assume your rectangle is Axis-Aligned.

You just have to "clamp" the point into the rectangle and then compute the distance from the clamped point.

Point = (px, py), Rectangle = (rx, ry, rwidth, rheight) // (top left corner, dimensions)

function pointRectDist (px, py, rx, ry, rwidth, rheight)
{
    var cx = Math.max(Math.min(px, rx+rwidth ), rx);
    var cy = Math.max(Math.min(py, ry+rheight), ry);
    return Math.sqrt( (px-cx)*(px-cx) + (py-cy)*(py-cy) );
}
\$\endgroup\$
3
\$\begingroup\$

You must use circle-rectangle collisions for this. There's a similar question on Stack Overflow.

Your circle's center would be the point in question, and the radius would be the distance you want to check.

\$\endgroup\$
3
\$\begingroup\$

If you're trying to figure out the distance from a point to a rectangle's edge, working with each of the nine regions created by the rectangle might be the fastest way:

function pointRectangleDistance(x, y, x1, y1, x2, y2) {
    var dx, dy;
    if (x < x1) {
        dx = x1 - x;
        if (y < y1) {
            dy = y1 - y;
            return Math.sqrt(dx * dx + dy * dy);
        }
        else if (y > y2) {
            dy = y - y2;
            return Math.sqrt(dx * dx + dy * dy);
        }
        else {
            return dx;
        }
    }
    else if (x > x2) {
        dx = x - x2;
        if (y < y1) {
            dy = y1 - y;
            return Math.sqrt(dx * dx + dy * dy);
        }
        else if (y > y2) {
            dy = y - y2;
            return Math.sqrt(dx * dx + dy * dy);
        }
        else {
            return dx;
        }
    }
    else {
        if (y < y1) {
            return y1 - y;
        }
        else if (y > y2) {
            return y - y2;
        }
        else {
            return 0.0; // inside the rectangle or on the edge
        }
    }
}
\$\endgroup\$
2
\$\begingroup\$

[Modified answer based on comments]

If you want to see if the point is within say 10 units if the grey rectangle in the image below, you check if the point is in any one of

  1. red rectangle
  2. Blue rectangle
  3. any one of the green circles (radius 10)

enter image description here

inside=false;

bluerect.x=oldrect.x-10;
bluerect.y=oldrect.y;
bluerect.width=oldrect.width;
bluerect.height=oldrect.height+20;

if(  point.x >=bluerect && point.x <=redrect.x+bluerect.width &&
     point.y >=bluerect && point.y <=redrect.y+bluerect.height){
         //now point is side the blue rectangle
         inside=true;
}

redrect.x=oldrect.x;
redrect.y=oldrect.y-10;
redrect.width=oldrect.width+20;
redrect.height=oldrect.height;

if(  point.x >=redrect&& point.x <=redrect.x+redrect.width &&
     point.y >=redrect&& point.y <=redrect.y+redrect.height){
         //now point is side the redrectangle
         inside=true;
}


d1= distance(point, new point(oldrect.x, oldrect.y)) //calculate distance between point and (oldrect.x, oldrect.y)
d2= distance(point, new point(oldrect.x+10, oldrect.y))
d3= distance(point, new point(oldrect.x, oldrect.y+10))
d4= distance(point, new point(oldrect.x+10, oldrect.y+10))
if (d1 < 10 || d2 <10 || d3 < 10 || d4 <10){
    inside=true;
}

//inside is now true if the point is within 10 units of rectangle

This approach is a little inelegant. A similar method which avoids having to test all 4 corners by using rectangle symmetry is documented here on stackoverflow

\$\endgroup\$
6
  • \$\begingroup\$ In the diagonal direction this will give a false positive to points that are eg. 11 units away. \$\endgroup\$
    – Eric B
    Commented Nov 26, 2012 at 15:56
  • \$\begingroup\$ The updated picture is blatantly wrong, in fact it actually illustrates the error case and makes it appear correct. That green point could easily be more than 10 units away and be inside that outer rectangle. \$\endgroup\$
    – Eric B
    Commented Nov 26, 2012 at 16:15
  • \$\begingroup\$ Hey @EricB, I've fixed the error you pointed out, how about undoing your downvote? \$\endgroup\$
    – Ken
    Commented Nov 26, 2012 at 17:37
  • \$\begingroup\$ Your answer will no longer give strictly incorrect results, so I removed the downvote, but it is also not the best way at all. Why not just test to see if the center is within the rectangle, and if the four line segments intersect the circle? The construction of these new rectangles and circles is just not necessary. Your answer also does not provide the actual distance from the point to the rectangle. \$\endgroup\$
    – Eric B
    Commented Nov 26, 2012 at 17:52
  • \$\begingroup\$ This answer is honestly awful. 12 additions, 4 object constructions, 12 tests, 4 square roots for a task that actually requires 3 lines of code? \$\endgroup\$ Commented Nov 28, 2012 at 12:12
-2
\$\begingroup\$

You could use something like this: enter image description here

\$\endgroup\$
3
  • \$\begingroup\$ This method seems unnecessarily complicated. Finding x1 and y1 is not necessary to solve this problem. \$\endgroup\$
    – Eric B
    Commented Nov 26, 2012 at 15:59
  • \$\begingroup\$ In fact, this doesn't even satisfy the requirement of finding a collision within a given distance. It's just a bad way of detecting if the point is within the rectangle. \$\endgroup\$
    – Eric B
    Commented Nov 26, 2012 at 16:06
  • \$\begingroup\$ A measure of distance is already implicitly there. if (d2<10*10) {/*within 10 units of measure*/} \$\endgroup\$ Commented Nov 27, 2012 at 12:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .