简体   繁体   English

检测轴对齐矩形和定向超椭圆之间的交点的算法

[英]Algorithm to detect intersection between an axis-aligned rectangle and an oriented superellipse

I am in the process of writing a function to test for the intersection of a rectangle with a superellipse. 我正在编写一个函数来测试矩形与超椭圆的交点。 The rectangle will always be axis-aligned whereas the superellipse may be oriented with an angle of rotation alpha. 矩形将始终与轴对齐,而超椭圆可能以旋转角度α定向。

In the case of an axis-aligned rectangle intersecting an axis-aligned superellipse I have written these two short functions that work beautifully. 在轴对齐的矩形与轴对齐的超椭圆相交的情况下,我编写了这两个简短的函数,它们效果很好。 The code is concise, clear and efficient. 代码简明,清晰,高效。 If possible, I would like to keep a similar structure for the new more general function. 如果可能,我想为新的更通用的功能保留类似的结构。

Here is what I have for detecting if an axis-aligned rectangle intersects an axis-aligned superellipse: 这是我用于检测轴对齐的矩形是否与轴对齐的超椭圆相交的内容:

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);
}

This works correctly but - as I said - only for an axis-aligned superellipse. 这可以正常工作,但是-正如我所说-仅适用于轴对齐的超椭圆形。

Now I would like to generalize it to an oriented superellipse, keeping the algorithm structure as close to the above as possible. 现在,我想将其概括为定向的超椭圆形,并保持算法结构尽可能接近上述形式。 The obvious expansion of the previous two functions would then become something like: 前面两个功能的明显扩展将变为:

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);
}

For an oriented superellipse, the above doesn't work correctly, even though point_inside_oriented_superellipse() by itself works as expected. 对于定向的超级椭圆,即使point_inside_oriented_superellipse()本身按预期工作,上述方法也无法正常工作。 I cannot use the above functions to test for an intersection with an axis-aligned rectangle. 我无法使用上述功能来测试与轴对齐的矩形的交点。 I have been researching online for about a week now and I have found some solutions requiring an inverse matrix transform to equalize the superellipse axes and bring its origin at (0, 0). 我已经在线研究了大约一个星期,发现一些解决方案需要逆矩阵变换来均衡超椭圆轴并将其原点设为(0,0)。 The tradeoff is that now my rectangle won't be a rectangle anymore and certainly not axis-aligned. 权衡是,现在我的矩形将不再是矩形,并且肯定不再是轴对齐的。 I would like to avoid going down that route. 我想避免沿着那条路线走。 My question is to show how to make the above algorithm work keeping its structure more or less unaltered. 我的问题是展示如何使上述算法工作,或多或少保持其结构不变。 If it is not possible to keep the same algorithmic structure, please show the simplest, most efficient algorithm to test for the intersection between an axis-aligned rectangle and an oriented superellipse. 如果无法保持相同的算法结构,请显示最简单,最有效的算法,以测试轴对齐的矩形和定向的超椭圆之间的交集。 I only need to know if the intersection occurred or not (boolean result). 我只需要知道相交是否发生(布尔结果)。 The range of the exponent parameter can vary from 0.25 to 100.0. 指数参数的范围可以在0.25到100.0之间变化。

Thanks for any assistance. 感谢您的协助。

Take a look at point 2 in this source . 看一下本资料中的第二点。 In simple terms, you will need to do the following tests: 简而言之,您将需要进行以下测试:

1. Are there any rectangle vertexes in the ellipse? 1.椭圆中是否有矩形顶点?

2. Is a rectangle edge intersecting the ellipse? 2.矩形的边缘是否与椭圆相交?

3. Is the center of the ellipse inside the rectangle? 3.椭圆的中心在矩形内吗?

The ellipse and the rectangle intersect each-other if any of the questions above can be answered with a yes , so, your function should return something like this: 如果可以使用yes回答上面的任何问题,则椭圆和矩形彼此相交,因此,您的函数应返回如下内容:

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

The doc even has an example of implementation, which is reasonably close to yours. 该文档甚至还有一个实现示例,与您的示例相当接近。

To check whether any of the vertex is inside the ellipse, you can compute their coordinates against the inequality of the ellipse. 要检查椭圆中是否有任何顶点,可以针对椭圆的不等式计算其坐标。 To check whether an edge overlaps the ellipse, you will need to check whether its line goes through the ellipse or touches it. 要检查边缘是否与椭圆重叠,您需要检查其线是穿过椭圆还是接触椭圆。 If so, you will need to check whether the segment where the line goes through the ellipse or touches it intersects the segment defined by the edge. 如果是这样,则需要检查直线穿过椭圆或触摸椭圆的线段是否与边缘定义的线段相交。 To check whether the center of the ellipse is inside the rectangle you will need to check the center against the inequalities of the rectangle. 要检查椭圆的中心是否在矩形内,您需要对照矩形的不等式检查中心。

Note, that these are very general terms, they do not even assume that your rectangle is axis oriented, yet alone your ellipse. 请注意,这些是非常笼统的术语,它们甚至不假设您的矩形是面向轴的,而仅假设您的椭圆是。

First you should rule out the obvious non-intersecting cases using the separating axis theorem -- The super-ellipse has possibly two bounding boxes (cases where exponent n>1) and case where n<=1. 首先,您应该使用分隔轴定理排除明显的非相交情况-超椭圆可能具有两个边界框(指数n> 1的情况)和n <= 1的情况。

In the SAT, all vertices in Bounding Box ABCD are compared against all (directed) edges in the BB(abcd) of super-ellipse; 在SAT中,将边界框ABCD中的所有顶点与超椭圆BB(abcd)中的所有(有向)边进行比较; then vice versa. 反之亦然。 If the signed distances to the separating axis are all positive (ie outside), the objects don't collide. 如果到分隔轴的符号距离均为正(即在外部),则对象不会碰撞。

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

The exponent n==1 divides the cases further -- n<=1 makes the super-ellipsoid concave, in which case ABCD intersects abcd only, if one or more points are inside the super-ellipsoid. 指数n == 1进一步划分了情况-n <= 1使超椭圆形凹入,在这种情况下,如果超椭圆形内部有一个或多个点,则ABCD仅与abcd相交。 When n>1, one must solve the intersection point of the line segment in AABB and the super-ellipsoid, which may have to be approximated by splines or another proxy must be found. 当n> 1时,必须求解AABB中线段与超椭圆体的交点,这可能必须由样条线近似或必须找到另一代理。 After all, the actual intersection point is not of interest, but putting the equations to wolfram alpha failed to produce any results in standard execution time. 毕竟,实际的交点并不重要,但是将公式放到Wolfram alpha上无法在标准执行时间内产生任何结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM