Как рассчитать расстояние между точкой и выровненным по оси прямоугольником?

29

У меня есть 2D-прямоугольник с позициями x, y, высотой и шириной, а также случайно расположенная точка рядом.

Есть ли способ проверить, может ли эта точка сталкиваться с прямоугольником, если он ближе определенного расстояния? Представьте себе невидимый радиус за пределами этой точки, сталкивающейся с указанным прямоугольником. У меня проблемы с этим просто потому, что это не квадрат!

Джон Смит
источник

Ответы:

26

Если (x,y)это центр прямоугольника, квадрат расстояния от точки (px,py)до границы прямоугольника можно вычислить следующим образом:

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

Если это квадратное расстояние равно нулю, это означает, что точка касается или находится внутри прямоугольника.

Сэм Хоцевар
источник
6
Для всех, кому интересно, (x, y) - центр прямоугольника, а не угол
Грег Розмаринович
2
Извините за старый комментарий, но предполагает ли это уравнение, что прямоугольник выровнен по оси?
BitNinja
1
@BitNinja да, это то, что предполагает вопрос. Если он не выровнен по оси, самый быстрый / простой алгоритм будет зависеть от того, как хранится информация о прямоугольнике.
Сэм Хоцевар
скажем, точка (4: 4), прямоугольник в (5: 5) с шириной / высотой (5: 5). Ваш код будет утверждать , что точка прикосновения или находится внутри прямоугольника, но это, очевидно , снаружи
ЛРН
@LRN прямоугольник с центром в (5: 5) с шириной / высотой (5: 5) простирается от (2,5: 2,5) до (7,5: 7,5). Точка (4: 4) находится внутри этого прямоугольника.
Сэм Хочевар
11

Я предполагаю, что ваш прямоугольник выровнен по оси.

Вам просто нужно «зажать» точку в прямоугольнике, а затем вычислить расстояние от зажатой точки.

Point = (px, py), Rectangle = (rx, ry, rwidth, rheight) // (верхний левый угол, размеры)

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

Для этого вы должны использовать столкновения прямоугольник с кругом. Есть похожий вопрос по переполнению стека.

Центром вашего круга будет точка, о которой идет речь, а радиусом будет расстояние, которое вы хотите проверить.

Эрик Б
источник
3

Если вы пытаетесь определить расстояние от точки до края прямоугольника, работа с каждой из девяти областей, созданных прямоугольником, может быть наиболее быстрой:

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

[Измененный ответ на основе комментариев]

Если вы хотите увидеть, находится ли точка в пределах, скажем, 10 единиц, если серый прямоугольник на изображении ниже, вы проверяете, находится ли точка в каком-либо из

  1. красный прямоугольник
  2. Синий прямоугольник
  3. любой из зеленых кругов (радиус 10)

введите описание изображения здесь

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

Этот подход немного не элегантен. Подобный метод, который избегает необходимости тестировать все 4 угла с помощью симметрии прямоугольника, описан здесь на stackoverflow

кругозор
источник
В диагональном направлении это даст ложный положительный результат для точек, например. 11 единиц.
Эрик Б
Обновленная картинка явно ошибочна, на самом деле она на самом деле иллюстрирует случай ошибки и делает ее правильной. Эта зеленая точка может легко находиться на расстоянии более 10 единиц и находиться внутри этого внешнего прямоугольника.
Эрик Б
Привет @EricB, я исправил ошибку, которую ты указал, как насчет отмены твоего downvote?
Кен
Ваш ответ больше не будет давать строго неверные результаты, поэтому я убрал понижение рейтинга, но это тоже не лучший способ. Почему бы просто не проверить, находится ли центр внутри прямоугольника и пересекаются ли четыре отрезка линии с окружностью? Построение этих новых прямоугольников и кругов просто не нужно. Ваш ответ также не предоставляет фактическое расстояние от точки до прямоугольника.
Эрик Б
Этот ответ честно ужасен. 12 дополнений, 4 конструкции объектов, 12 тестов, 4 квадратных корня для задачи, которая на самом деле требует 3 строки кода?
Сэм Хочевар
-2

Вы можете использовать что-то вроде этого: введите описание изображения здесь

AlexanderBrevig
источник
Этот метод кажется излишне сложным. Поиск x1 и y1 не является необходимым для решения этой проблемы.
Эрик Б
Фактически, это даже не удовлетворяет требованию обнаружения столкновения на заданном расстоянии. Это просто плохой способ определить, находится ли точка внутри прямоугольника.
Эрик Б
Мера расстояния уже неявно существует. if (d2 <10 * 10) {/ * в пределах 10 единиц измерения * /}
AlexanderBrevig