【问题标题】:Algorithm for edge intersection?边缘相交的算法?
【发布时间】:2010-08-11 21:17:04
【问题描述】:

给定多边形 P,我有它的顶点。我有一个有 4 个顶点的矩形 R 我怎么能这样做:

如果 P 的任何一条边(相邻顶点之间的线)与 R 的一条边相交,则返回 TRUE,否则返回 FALSE。

谢谢

      *             *    


      *             *    

【问题讨论】:

  • 矩形是轴方向的吗?
  • 它是一个上、左、下、右矩形

标签: c++ c algorithm


【解决方案1】:

您想要的是一种快速确定线段是否与轴对齐矩形相交的方法。然后只需对照矩形检查边缘列表中的每个线段。您可以执行以下操作:

1) 将线投影到 X 轴上,得到区间 Lx
2) 将矩形投影到 X 轴上,得到区间 Rx
3) 如果 Lx 和 Rx 不相交,则直线和矩形不相交。

[Y 轴重复]:

4) 将线投影到 Y 轴上,得到区间 Ly
5) 将矩形投影到 Y 轴上,得到一个区间 Ry
6) 如果 Ly 和 Ry 不相交,则直线和矩形不相交。

7) ...
8) 它们相交。

请注意,如果我们到达第 7 步,则无法用轴对齐的线分隔形状。现在要确定的是这条线是否完全在矩形之外。我们可以通过检查矩形上的所有角点是否在直线的同一侧来确定这一点。如果是,则直线和矩形不相交。

1-3和4-6背后的想法来自separating axis theorem;如果我们找不到分离轴,它们一定是相交的。 所有这些案例都必须经过测试才能断定它们相交。

这是匹配代码:

#include <iostream>
#include <utility>
#include <vector>

typedef double number; // number type

struct point
{
    number x;
    number y;
};

point make_point(number pX, number pY)
{
    point r = {pX, pY};
    return r;
}

typedef std::pair<number, number> interval; // start, end
typedef std::pair<point, point> segment; // start, end
typedef std::pair<point, point> rectangle; // top-left, bottom-right

namespace classification
{
    enum type
    {
        positive = 1,
        same = 0,
        negative = -1
    };
}

classification::type classify_point(const point& pPoint,
                                    const segment& pSegment)
{
    // implicit line equation
    number x = (pSegment.first.y - pSegment.second.y) * pPoint.x +
                (pSegment.second.x - pSegment.first.x) * pPoint.y +
                (pSegment.first.x * pSegment.second.y -
                 pSegment.second.x * pSegment.first.y);

    // careful with floating point types, should use approximation
    if (x == 0)
    {
        return classification::same;
    }
    else
    {
        return (x > 0) ? classification::positive :classification::negative;
    }
}

bool number_interval(number pX, const interval& pInterval)
{
    if (pInterval.first < pInterval.second)
    {
        return pX > pInterval.first && pX < pInterval.second;
    }
    else
    {
        return pX > pInterval.second && pX < pInterval.first;
    }
}

bool inteveral_interval(const interval& pFirst, const interval& pSecond)
{
    return number_interval(pFirst.first, pSecond) ||
            number_interval(pFirst.second, pSecond) ||
            number_interval(pSecond.first, pFirst) ||
            number_interval(pSecond.second, pFirst);
}

bool segment_rectangle(const segment& pSegment, const rectangle& pRectangle)
{
    // project onto x (discard y values)
    interval segmentX =
                std::make_pair(pSegment.first.x, pSegment.second.x);
    interval rectangleX =
                std::make_pair(pRectangle.first.x, pRectangle.second.x);

    if (!inteveral_interval(segmentX, rectangleX))
        return false;

    // project onto y (discard x values)
    interval segmentY =
                std::make_pair(pSegment.first.y, pSegment.second.y);
    interval rectangleY =
                std::make_pair(pRectangle.first.y, pRectangle.second.y);

    if (!inteveral_interval(segmentY, rectangleY))
        return false;

    // test rectangle location
    point p0 = make_point(pRectangle.first.x, pRectangle.first.y);
    point p1 = make_point(pRectangle.second.x, pRectangle.first.y);
    point p2 = make_point(pRectangle.second.x, pRectangle.second.y);
    point p3 = make_point(pRectangle.first.x, pRectangle.second.y);

    classification::type c0 = classify_point(p0, pSegment);
    classification::type c1 = classify_point(p1, pSegment);
    classification::type c2 = classify_point(p2, pSegment);
    classification::type c3 = classify_point(p3, pSegment);

    // test they all classify the same
    return !((c0 == c1) && (c1 == c2) && (c2 == c3));
}

int main(void)
{
    rectangle r = std::make_pair(make_point(1, 1), make_point(5, 5));
    segment s0 = std::make_pair(make_point(0, 3), make_point(2, -3));
    segment s1 = std::make_pair(make_point(0, 0), make_point(3, 0));
    segment s2 = std::make_pair(make_point(3, 0), make_point(3, 6));
    segment s3 = std::make_pair(make_point(2, 3), make_point(9, 8));

    std::cout << std::boolalpha;
    std::cout << segment_rectangle(s0, r) << std::endl;
    std::cout << segment_rectangle(s1, r) << std::endl;
    std::cout << segment_rectangle(s2, r) << std::endl;
    std::cout << segment_rectangle(s3, r) << std::endl;
}

希望这是有道理的。

【讨论】:

    【解决方案2】:

    我认为您的问题相当于凸多边形相交,在这种情况下this 可能会有所帮助。另见:How do I determine if two convex polygons intersect?

    【讨论】:

    • P 实际上并没有被指定为凸的。 OTOH,R 轴对齐可以在一般情况下减少一些角落。
    【解决方案3】:

    显然未经测试,但使用粗略的伪代码:

    // test two points against an edge
    function intersects ( side, lower, upper, pt1Perp, pt1Par, pt2Perp, pt2Par )
    {
        if ( ( pt1Perp < side and pt2Perp > side ) or ( pt1Perp > side and pt2Perp < side )
        {
            intersection = (side - pt1Perp) * (pt2Par - pt1Par) / (pt2Perp - pt1Perp);
            return (intersection >= lower and intersection <= higher);
        }
        else
        {
            return false;
        }
    }
    
    // left, right, bottom, top are the bounds of R
    for pt1, pt2 adjacent in P  // don't forget to do last,first
    {
        if ( intersects ( left, bottom, top, pt1.x, pt1.y, pt2.x, pt2.y )
             or intersects ( right, bottom, top, pt1.x, pt1.y, pt2.x, pt2.y )
             or intersects ( top, left, right, pt1.y, pt1.x, pt2.y, pt2.x )
             or intersects ( bottom, left, right, pt1.y, pt1.x, pt2.y, pt2.x ) )
        {
            return true;
        }
    }
    

    基本上,如果两个相邻的 P 顶点在 R 的一条边的相对两侧,则检查交点是否在范围内。

    【讨论】:

      【解决方案4】:

      仅供参考,geometrictools 是此类事情的绝佳资源(尤其是数学部分)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-07-23
        • 1970-01-01
        • 2021-12-27
        • 1970-01-01
        • 2012-07-14
        • 2011-09-15
        • 2018-04-10
        • 2011-01-17
        相关资源
        最近更新 更多