Làm cách nào để tính khoảng cách giữa một điểm và hình chữ nhật thẳng hàng trục?


29

Tôi có một hình chữ nhật 2D với vị trí x, y, chiều cao và chiều rộng và một điểm được định vị ngẫu nhiên gần đó.

Có cách nào để kiểm tra xem điểm này có thể va chạm với hình chữ nhật không nếu nó gần hơn một khoảng cách nhất định? Hãy tưởng tượng một bán kính vô hình bên ngoài điểm đó va chạm với hình chữ nhật đã nói. Tôi có vấn đề với điều này đơn giản vì nó không phải là một hình vuông!

Câu trả lời:


26

Nếu (x,y)là tâm của hình chữ nhật, khoảng cách bình phương từ một điểm (px,py)đến đường viền của hình chữ nhật có thể được tính theo cách này:

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

Nếu khoảng cách bình phương đó bằng 0, có nghĩa là điểm chạm hoặc nằm bên trong hình chữ nhật.


6
Đối với bất cứ ai khác đang tự hỏi, (x, y) là trung tâm của hình chữ nhật, không phải là góc
Greg Rozmarynowycz

2
Xin lỗi cho nhận xét cũ, nhưng phương trình này có cho rằng hình chữ nhật được căn trục không?
BitNinja

1
@BitNinja có, đó là những gì câu hỏi giả định. Nếu nó không được căn chỉnh theo trục, thuật toán nhanh nhất / đơn giản nhất sẽ phụ thuộc vào cách lưu trữ thông tin hình chữ nhật.
sam hocevar

giả sử, điểm là (4: 4), hình chữ nhật nằm ở (5: 5) với chiều rộng / chiều cao (5: 5). Mã của bạn sẽ cho rằng điểm chạm hoặc nằm trong hình chữ nhật, nhưng rõ ràng là bên ngoài
LRN

@LRN một hình chữ nhật có tâm ở (5: 5) với chiều rộng / chiều cao (5: 5) kéo dài từ (2.5: 2.5) đến (7.5: 7.5). Điểm (4: 4) nằm bên trong hình chữ nhật đó.
sam hocevar

11

Tôi giả sử hình chữ nhật của bạn được căn chỉnh theo trục.

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) );
}

3

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.


3

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
        }
    }
}

2

[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


In the diagonal direction this will give a false positive to points that are eg. 11 units away.
Eric B

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.
Eric B

Hey @EricB, I've fixed the error you pointed out, how about undoing your downvote?
Ken

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.
Eric B

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?
sam hocevar

-2

You could use something like this: enter image description here


This method seems unnecessarily complicated. Finding x1 and y1 is not necessary to solve this problem.
Eric B

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.
Eric B

A measure of distance is already implicitly there. if (d2<10*10) {/*within 10 units of measure*/}
AlexanderBrevig
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.