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

Я пытаюсь написать функцию в GLSL, которая возвращает знаковое расстояние до прямоугольника. Прямоугольник выравнивается по оси. Я чувствую себя немного застрявшим; Я просто не могу оборачивать голову тем, что мне нужно сделать, чтобы он работал.

Лучшее, что я придумал, это:

float sdAxisAlignedRect(vec2 uv, vec2 tl, vec2 br)
{
    // signed distances for x and y. these work fine.
    float dx = max(tl.x - uv.x, uv.x - br.x);
    float dy = max(tl.y - uv.y, uv.y - br.y);
    dx = max(0.,dx);
    dy = max(0.,dy);
    return sqrt(dx*dx+dy*dy);
}

Создает прямоугольник, который выглядит следующим образом:

enter image description here

Линии показывают расстояние от прямоугольника. Он отлично работает, но ТОЛЬКО для расстояний вне прямоугольника. Внутри прямоугольника расстояние статическое 0..

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

4b9b3361

Ответ 1

Как насчет этого…

float sdAxisAlignedRect(vec2 uv, vec2 tl, vec2 br)
{
    vec2 d = max(tl-uv, uv-br);
    return length(max(vec2(0.0), d)) + min(0.0, max(d.x, d.y));
}

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

enter image description here


Структура:

  • Получите знаковое расстояние от границ x и y. u - left и right - u — это два расстояния по оси x. Принимая максимум этих значений, вы получаете знаковое расстояние до ближайшей границы. Просмотр d.x и d.y отображается отдельно на изображениях ниже.

  • Объединить x и y:

    • Если оба значения отрицательные, возьмите максимум (т.е. ближе к границе). Это делается с помощью min(0.0, max(d.x, d.y)).

    • Если только одно значение положительно, то требуемое расстояние.

    • Если оба значения положительны, ближайшая точка является углом, и в этом случае мы хотим длину. Это можно комбинировать с приведенным выше случаем, принимая длину в любом случае и убедившись, что оба значения положительны: length(max(vec2(0.0), d)).

    Эти две части уравнения являются взаимоисключающими, т.е. только один будет производить ненулевое значение и может быть суммирован.

enter image description hereenter image description here


void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord.xy / iResolution.xy;
    uv -= 0.5;
    uv *= vec2(iResolution.x/iResolution.y,1.0);
    uv += 0.5;
    float d = sdAxisAlignedRect(uv, vec2(0.3), vec2(0.7));
    float m = 1.0 - abs(d)/0.1;
    float s = sin(d*400.0) * 0.5 + 0.5;
    fragColor = vec4(s*m*(-sign(d)*0.5+0.5),s*m*(sign(d)*0.5+0.5),0,1);
}

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

Мой ответ немного длиннее других, но он исходит с другой точки зрения.

Ключ не в том, если вы находитесь внутри прямоугольник, а в том, что вы находитесь где-нибудь в пределах коридоров, определенных путем взятия сторон прямоугольника и их бесконечного удлинения (изобразите бесконечный знак плюса в центре прямоугольника).

Если он находится внутри этих коридоров, то ближайшее расстояние перпендикулярно одной из сторон.

Если он снаружи, то ближайшее расстояние — это расстояние до ближайшего угла.

Ваш код может выглядеть так:

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)

Если вы хотите попытаться сохранить sqrt, вы можете проверить, находитесь ли вы внутри коридоров или вне их. В этом случае вы бы переставили его следующим образом:

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

У меня есть выровненный по оси прямоугольник в 2-мерной системе координат, представленный точкой в ​​нижнем левом углу и точкой в ​​правом верхнем углу, а также точкой, которая может находиться внутри или вне прямоугольника. Я хочу найти расстояние от точки до ближайшей точки прямоугольника, независимо от того, находится она внутри прямоугольника или нет. Конечно, я мог бы просто написать случай переключателя с 9 различными результатами, но я надеюсь, что есть более элегантное решение.
Кроме того, я нашел несколько решений этой проблемы (например, этот), но все они вычислили бы расстояние как 0, если точка находится внутри поля, что мне не нужно.

3 ответа

Мой ответ немного длиннее других, но он исходит с другой точки зрения.

Ключ не в том, если вы находитесь внутри прямоугольника , а в том, что вы находитесь где-нибудь в пределах коридоров, определенных путем взятия сторон прямоугольника и их бесконечного удлинения (изобразите бесконечный знак плюса с центром на прямоугольник).

Если он находится внутри этих коридоров, то ближайшее расстояние перпендикулярно одной из сторон.

Если он снаружи, то ближайшее расстояние — это расстояние до ближайшего угла.

Ваш код может выглядеть так:

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)

Если вы хотите попытаться сохранить sqrt, вы можете проверить, находитесь ли вы внутри коридоров или вне их. В этом случае вы бы переставили его следующим образом:

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


3

Scott Mermelstein
24 Авг 2018 в 17:51

Вы можете расширить связанное решение MultiRRomero и выполнить некоторые дополнительные вычисления для точек внутри прямоугольника.

Для этих точек ближайшая точка на границе прямоугольника имеет ту же координату x или y, что и точка. Таким образом, вычислить расстояния до линий просто, и желаемое расстояние будет наименьшим.

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
}

Изменить: исправлены опечатки


1

pH 74
25 Авг 2018 в 07:48


Форум программистов Vingrad

Поиск:

Ответ в темуСоздание новой темы
Создание опроса
> минимальное расстояние до прямоугольника 

:(

   

Опции темы

mrgloom
Дата 11.10.2011, 17:08 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Опытный
**

Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет

как определить минимальное расстояние до прямоугольника?

пришло в голову только определить минимальные расстояния до 4 отрезков его составляющих.

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

PM MAIL   Вверх
Akina
Дата 11.10.2011, 17:44 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Советчик
****

Профиль
Группа: Модератор
Сообщений: 20561
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 20
Всего: 453

Цитата(mrgloom @  11.10.2011,  18:08 Найти цитируемый пост)
как определить минимальное расстояние до прямоугольника?

Сначала надо дать определение того, что есть это расстояние. Ибо возможны минимум 3 разных варианта — даже при условии, что точка не лежит внутри или на границе.

Цитата(mrgloom @  11.10.2011,  18:08 Найти цитируемый пост)
требуется еще его перетаскивать, но не очень понятно как это реализовать ,имеем точку в которой пользователь нажал кнопку мыши, определяем ближайшую точку на прямоугольнике и за эту точку надо перетаскивать прямоугольник, только непонятно как это отрисовывать, т.е. точка приложения разная, а процедура отрисовки прямоугольника одна. 

За каким хреном определять какую-то там «точку перетаскивания»? или он ещё и поворачивается при перетаскивании?

———————

 О(б)суждение моих действий — в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция — Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
Earnest
Дата 11.10.2011, 20:03 (ссылка)
|    (голосов:1)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Эксперт
****

Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 7
Всего: 183

Цитата(mrgloom @  11.10.2011,  18:08 Найти цитируемый пост)
 только непонятно как это отрисовывать, т.е. точка приложения разная, а процедура отрисовки прямоугольника одна. 

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

———————

PM   Вверх
sQu1rr
Дата 11.10.2011, 20:06 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Опытный
**

Профиль
Группа: Участник
Сообщений: 597
Регистрация: 11.11.2008
Где: london

Репутация: нет
Всего: 13

Цитата(mrgloom @  11.10.2011,  17:08 Найти цитируемый пост)
как определить минимальное расстояние до прямоугольника?

Расстояние от точки x до границы
1. Берешь по две вершины a и b
2. Ставишь точку c на середние отрезка с вершинами a и b
3. находишь наименьший xa xb или xc. Если xa или xb меньший — запоминаешь и делаешь то же самое для других сторон
4. если xc меньше чем xa и xb, то определяешь c за a, меньший из (старых) ax, bx за b, и идешь к шагу 2.
вконце должно получица 4 отрезка от точки x к какждой стороне. Находишь минимальное.
Не самый лучший по производительности вариант, но самый быстрый по скорости написания и объяснения

Цитата(mrgloom @  11.10.2011,  17:08 Найти цитируемый пост)
пришло в голову только определить минимальные расстояния до 4 отрезков его составляющих.

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

Цитата(mrgloom @  11.10.2011,  17:08 Найти цитируемый пост)
требуется еще его перетаскивать, но не очень понятно как это реализовать ,имеем точку в которой пользователь нажал кнопку мыши, определяем ближайшую точку на прямоугольнике и за эту точку надо перетаскивать прямоугольник, только непонятно как это отрисовывать, т.е. точка приложения разная, а процедура отрисовки прямоугольника одна. 

когда пользователь нажимает, запоминаем положение курсора и каждый раз отрисовываем его с относительным смещением всех вершин.  smile 

PM MAIL Skype GTalk   Вверх
mrgloom
Дата 19.10.2011, 11:59 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Опытный
**

Профиль
Группа: Участник
Сообщений: 829
Регистрация: 8.6.2011

Репутация: нет
Всего: нет

Цитата
пришло в голову только определить минимальные расстояния до 4 отрезков его составляющих.

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

пока я так решил проблему, но по идее, можно не считать все 4 расстояния, а определить сначала в каком из 9 секторов находится точка относительно прямоугольника и посчитать меньшее кол-во.

и еще вопрос как определяется расстояние между двумя отрезками? 
например отрезки AB CD определяем минимальное расстояние из 4 расстояний от C до AB,от D до AB, от A до CD, от B до CD?

Добавлено через 5 минут
+ еще непонятно при пересечении считать что расстояние 0 или считать что расстояние ноль только когда концевые точки совмещены.

PM MAIL   Вверх
sQu1rr
Дата 19.10.2011, 22:51 (ссылка)
| (нет голосов)
Загрузка ... Загрузка …




Быстрая цитата

Цитата

Опытный
**

Профиль
Группа: Участник
Сообщений: 597
Регистрация: 11.11.2008
Где: london

Репутация: нет
Всего: 13

Цитата(mrgloom @  19.10.2011,  11:59 Найти цитируемый пост)
еще непонятно при пересечении считать что расстояние 0 или считать что расстояние ноль только когда концевые точки совмещены. 

Ну, твое же задание, кому как не тебе лучше знать. Если отрезки пересекаются или касаются, то расстояние между ними = 0 с точки зрения геоментрии.

Цитата(mrgloom @  19.10.2011,  11:59 Найти цитируемый пост)
пока я так решил проблему, но по идее, можно не считать все 4 расстояния, а определить сначала в каком из 9 секторов находится точка относительно прямоугольника и посчитать меньшее кол-во.

сам и ответил на свой вопрос smile тока не 9 секторов, а 4, линии разделения которых парралельны сторонам прямоугольника и пересекаются в его центре. Сразу определишь точку (или 2 стороны к ней прилежащие). Это в теории, на практике, нужно найти наименьшее расстояние до 4х вершин, а там уже смотреть на прилежащие стороны к точке до которой наименшьее расстояние, если до 2х точек расстояние одинаковое, то сразу смотреть на сторону между ними.

Цитата(mrgloom @  19.10.2011,  11:59 Найти цитируемый пост)
и еще вопрос как определяется расстояние между двумя отрезками? 
например отрезки AB CD определяем минимальное расстояние из 4 расстояний от C до AB,от D до AB, от A до CD, от B до CD?

самый простой способ, зато верный smile

PM MAIL Skype GTalk   Вверх



















Ответ в темуСоздание новой темы
Создание опроса
Правила форума «Алгоритмы»

maxim1000

Форум «Алгоритмы» предназначен для обсуждения вопросов, связанных только с алгоритмами и структурами данных, без привязки к конкретному языку программирования и/или программному продукту.

  • Для решения контрольных, курсовых, дипломов и т.п. обращайтесь в Центр помощи
  • Похвалиться своими достижениями можно в разделе Интересные и занимательные задачи
  • Для поиска нужной литературы есть специальный раздел

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, maxim1000.

 

0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Алгоритмы | Следующая тема »

Понравилась статья? Поделить с друзьями:

Не пропустите также:

  • Как найти знак зодиака козерог
  • Как найти вектор через другие вектора
  • Как составить списки для профосмотров
  • Как составить смету расходов на строительство
  • Когда горчит квашеная капуста как исправить

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии