Я пытаюсь написать функцию в 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);
}
Создает прямоугольник, который выглядит следующим образом:
Линии показывают расстояние от прямоугольника. Он отлично работает, но ТОЛЬКО для расстояний вне прямоугольника. Внутри прямоугольника расстояние статическое 0..
Как получить точные расстояния внутри прямоугольника с помощью единой формулы?
Ответ 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));
}
Здесь результат, где зеленый обозначает положительное расстояние и красный негатив (код ниже):
Структура:
-
Получите знаковое расстояние от границ 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)).
Эти две части уравнения являются взаимоисключающими, т.е. только один будет производить ненулевое значение и может быть суммирован.
-
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 |
|
||
|
Опытный Профиль Репутация: нет
|
как определить минимальное расстояние до прямоугольника? пришло в голову только определить минимальные расстояния до 4 отрезков его составляющих. и еще вопрос. |
||
|
|||
| Akina |
|
||||
|
Советчик Профиль
Репутация: 20
|
Сначала надо дать определение того, что есть это расстояние. Ибо возможны минимум 3 разных варианта — даже при условии, что точка не лежит внутри или на границе.
За каким хреном определять какую-то там «точку перетаскивания»? или он ещё и поворачивается при перетаскивании? ——————— О(б)суждение моих действий — в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция — Администрация форума. |
||||
|
|||||
| Earnest |
|
||
Эксперт Профиль
Репутация: 7
|
Запоминаешь точку начала перетаскивания, в конце перетаскивания определяешь вектор, и сдвигаешь на него весь прямоугольник. ——————— … |
||
|
|||
| sQu1rr |
|
||||||
Опытный Профиль
Репутация: нет
|
Расстояние от точки x до границы
?? всмысле до каждой стороны? так в чем проблема, наименьшее из них и есть минимальное расстояние, или я дурак?
когда пользователь нажимает, запоминаем положение курсора и каждый раз отрисовываем его с относительным смещением всех вершин. |
||||||
|
|||||||
| mrgloom |
|
||
|
Опытный Профиль Репутация: нет
|
пока я так решил проблему, но по идее, можно не считать все 4 расстояния, а определить сначала в каком из 9 секторов находится точка относительно прямоугольника и посчитать меньшее кол-во. и еще вопрос как определяется расстояние между двумя отрезками? Добавлено через 5 минут |
||
|
|||
| sQu1rr |
|
||||||
Опытный Профиль
Репутация: нет
|
Ну, твое же задание, кому как не тебе лучше знать. Если отрезки пересекаются или касаются, то расстояние между ними = 0 с точки зрения геоментрии.
сам и ответил на свой вопрос
самый простой способ, зато верный |
||||||
|
|||||||
![]() ![]()
|
| Правила форума «Алгоритмы» | |
|
|
Форум «Алгоритмы» предназначен для обсуждения вопросов, связанных только с алгоритмами и структурами данных, без привязки к конкретному языку программирования и/или программному продукту.
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, maxim1000. |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) |
| 0 Пользователей: |
| « Предыдущая тема | Алгоритмы | Следующая тема » |















)























