Я пишу функцию для проверки пересечения прямоугольника с суперэллипсом. Прямоугольник всегда будет выровнен по оси, тогда как суперэллипс может быть ориентирован с углом поворота альфа.
В случае прямоугольника, выровненного по оси, пересекающего суперэллипс, выровненный по оси, я написал эти две короткие функции, которые прекрасно работают. Код краткий, понятный и эффективный. Если возможно, я хотел бы сохранить аналогичную структуру для новой более общей функции.
Вот что у меня есть для определения того, пересекает ли выровненный по оси прямоугольник выровненный по оси суперэллипс:
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.
Спасибо за любую помощь.