Алгоритм обнаружения пересечения между выровненным по оси прямоугольником и ориентированным суперэллипсом

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

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

Вот что у меня есть для определения того, пересекает ли выровненный по оси прямоугольник выровненный по оси суперэллипс:

double fclamp(double x, double min, double max)
{
    if (x <= min) return min;
    if (x >= max) return max;
    return x;
}

bool rect_intersects_superellipse(const t_rect *rect, double cx, double cy, double rx, double ry, double exponent)
{
    t_pt closest;
    closest.x = fclamp(cx, rect->x, rect->x + rect->width);
    closest.y = fclamp(cy, rect->y, rect->y + rect->height);
    return point_inside_superellipse(&closest, cx, cy, rx, ry, exponent);
}

bool point_inside_superellipse(const t_pt *pt, double cx, double cy, double rx, double ry, double exponent)
{
    double dx = fabs(pt->x - cx);
    double dy = fabs(pt->y - cy);

    double dxp = pow(dx, exponent);
    double dyp = pow(dy, exponent);

    double rxp = pow(rx, exponent);
    double ryp = pow(ry, exponent);

    return (dxp * ryp + dyp * rxp) <= (rxp * ryp);
}

Это работает правильно, но, как я уже сказал, только для суперэллипса, выровненного по оси.

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

bool rect_intersects_oriented_superellipse(const t_rect *rect, double cx, double cy, double rx, double ry, double exponent, double radians)
{
    t_pt closest;
    closest.x = fclamp(cx, rect->x, rect->x + rect->width);
    closest.y = fclamp(cy, rect->y, rect->y + rect->height);
    return point_inside_oriented_superellipse(&closest, cx, cy, rx, ry, exponent, radians);
}

bool point_inside_oriented_superellipse(const t_pt *pt, double cx, double cy, double rx, double ry, double exponent, double radians)
{
    double dx = pt->x - cx;
    double dy = pt->y - cy;

    if (radians) {

        double c = cos(radians);
        double s = sin(radians);

        double new_x = dx * c - dy * s;
        double new_y = dx * s + dy * c;

        dx = new_x;
        dy = new_y;
    }
    double dxp = pow(fabs(dx), exponent);
    double dyp = pow(fabs(dy), exponent);

    double rxp = pow(rx, exponent);
    double ryp = pow(ry, exponent);

    return (dxp * ryp + dyp * rxp) < (rxp * ryp);
}

Для ориентированного суперэллипса вышеуказанное работает некорректно, хотя point_inside_oriented_superellipse() сам по себе работает должным образом. Я не могу использовать вышеуказанные функции для проверки пересечения с прямоугольником, выровненным по оси. Я занимаюсь онлайн-исследованиями около недели и нашел некоторые решения, требующие обратного матричного преобразования, чтобы уравнять оси суперэллипса и привести его начало в (0, 0). Компромисс в том, что теперь мой прямоугольник больше не будет прямоугольником и уж точно не выровнен по оси. Я бы не хотел идти по этому пути. Мой вопрос - показать, как заставить работать вышеуказанный алгоритм, сохраняя его структуру более или менее неизменной. Если невозможно сохранить ту же алгоритмическую структуру, пожалуйста, покажите простейший и наиболее эффективный алгоритм для проверки пересечения между выровненным по оси прямоугольником и ориентированным суперэллипсом. Мне нужно только знать, произошло ли пересечение или нет (логический результат). Диапазон параметра показателя степени может варьироваться от 0,25 до 100,0.

Спасибо за любую помощь.


person Luigi Castelli    schedule 26.08.2017    source источник
comment
Вы уверены, что ваш метод работает? Вы просто проверяете вершины прямоугольника, да? Но даже для круга у вас может быть круг и прямоугольник, пересекающиеся без того, чтобы ни одна из вершин находилась в круге.   -  person dmuir    schedule 28.08.2017
comment
Для суперэллипса, выровненного по оси - да - я уверен, что мой метод работает должным образом. Я его тщательно протестировал и постоянно использую. Нет, я не тестирую только вершины прямоугольника. Код есть, попробуйте сами и посмотрите ...   -  person Luigi Castelli    schedule 28.08.2017


Ответы (2)


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

1. Есть ли в эллипсе вершины прямоугольника?

2. Край прямоугольника пересекает эллипс?

3. Находится ли центр эллипса внутри прямоугольника?

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

return areVertexesInsideEllipse(/*params*/) || areRectangleEdgesIntersectingEllipse(/*params*/) || isEllipseCenterInsideRectangle(/*params*/);

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

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

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

person Lajos Arpad    schedule 26.08.2017
comment
Обратите внимание, что в связанной статье обсуждаются только пересечения с эллипсами, но не с суперэллипсами. Выяснить, пересекает ли произвольная линия суперэллипс, значительно сложнее. - person Anton; 26.08.2017
comment
@ user3290797 правда, ответ, возможно, придется отредактировать, но я думаю, что он уже полезен, хотя и не полон. Если в предложении упоминаются конкретные проблемы, то я всегда их учту и отредактирую ответ. Однако в целом идея для суперэллипсов такая же, как и для эллипсов. - person Lajos Arpad; 26.08.2017
comment
Я ценю, что вы нашли время ответить, однако ваш ответ бесполезен - даже немного. Я довольно четко сформулировал свой вопрос и упомянул конкретную проблему. Мне нужно решить проблему обнаружения пересечения выровненного по оси прямоугольника с ориентированным суперэллипсом. Предоставление решения для эллипса с выравниванием по оси говорит мне то, что я уже знаю, и не отвечает на мой вопрос. - person Luigi Castelli; 28.08.2017
comment
@LuigiCastelli, почему вы думаете, что эллипс, о котором я говорю в своем ответе, выровнен по оси? - person Lajos Arpad; 29.08.2017
comment
Прошу прощения ... Теперь я вижу, что в вашем ответе эллипс может быть ориентирован. Но то же самое можно сказать и о прямоугольнике, который в моем вопросе не так. Я хотел бы использовать тот факт, что я знаю, что прямоугольник выровнен по оси, чтобы сделать код более эффективным. В любом случае - как уже упоминал user3290797 - в моем вопросе мы говорим о суперэллипсе, а не об эллипсе. - person Luigi Castelli; 29.08.2017
comment
@LuigiCastelli, вы можете использовать этот факт в целях оптимизации, поскольку прямоугольник с выравниванием по оси - это особый случай. Однако критика в отношении того, что этот ответ касается только эллипсов, верна. Тем не менее, я считаю ответ полезным, даже если он неполный. Я считаю, что решение для суперэллипса не должно сильно отличаться от решения для эллипса. Я сознательно не вписал формулу в ответ, так как у меня не было времени исследовать детали, но если вы можете столкнуться с конкретными проблемами здесь, в разделе комментариев, я постараюсь помочь. - person Lajos Arpad; 30.08.2017

Сначала вы должны исключить очевидные непересекающиеся случаи, используя теорему о разделяющей оси - суперэллипс, возможно, имеет две ограничивающие рамки (случаи, когда показатель степени n> 1) и случай, когда n ‹= 1.

В SAT все вершины граничной рамки ABCD сравниваются со всеми (направленными) ребрами в BB (abcd) суперэллипса; потом наоборот. Если все обозначенные расстояния до разделяющей оси положительны (т.е. снаружи), объекты не сталкиваются.

          b
       a  
   A------B
   |      |     d
   |      |  c
   C------D

Показатель n == 1 делит случаи дальше - n ‹= 1 делает суперэллипсоид вогнутым, и в этом случае ABCD пересекает abcd только, если одна или несколько точек находятся внутри суперэллипсоида. Когда n> 1, необходимо определить точку пересечения отрезка прямой в AABB и суперэллипсоида, который, возможно, придется аппроксимировать сплайнами или найти другой прокси. В конце концов, фактическая точка пересечения не представляет интереса, но преобразование уравнений в вольфрам-альфа не дало никаких результатов при стандартном времени выполнения.

person Aki Suihkonen    schedule 26.08.2017