一种线性时间算法,用于查找从其他顶点可见的多边形的任何顶点

时间:2012-11-17 10:54:18

标签: algorithm geometry computational-geometry

假设有一个没有孔的多边形和由n个顶点定义的自交叉(即一个简单的多边形)。选择此多边形的反射顶点v

我想找到从顶点u“可见”的同一多边形的任何其他顶点v。可见我的意思是,vu之间的线段完全位于多边形内。

是否有算法在O(N)时间或更好时间执行此操作?是否有算法可以在O(N)时间内找到所有可见顶点?

快速研究表明,对于给定的多边形和此多边形内的任何点,可以在O(N)中构造visibility polygon。我认为找到一个可见的顶点应该更容易。

5 个答案:

答案 0 :(得分:6)

这个问题在30年前解决了:

  

ElGindy和Avis,“用于计算点的可见性多边形的线性算法”, J.算法 2 ,1981,p。 186--197。

Joe& amp; Simpson,1985,“一个简单多边形的可见性”,提供经过仔细验证的伪代码: (PDF download link)。 自从多种语言以来,这肯定已经实施了很多次。 例如,the Wikipedia article on the topic处有一个链接。

答案 1 :(得分:2)

我修改了算法,因为它不正确。我希望这次它涵盖了所有情况!。

从反射 a 开始,让 a'下一个顶点并跟随多边形,直到找到穿过 a - a'的边 a'的一边,让 b 这条边与线 a - a'的交点c 边缘的终点( a - c 右侧的那个)。

现在继续浏览多边形的边缘,如果边缘从左到右穿过 a - b 段,则将 b 设置为新的点交叉点和 c 到结束顶点。完成后我们有一个三角形 a - b - c 。现在从 c 重新开始查看每个顶点,看它是否在三角形内 a - b - c ,在这种情况下设置 c 到新的顶点。最后 a - c 是多边形的对角线。

以下是C中的一个实现,它假定反射点 a 位于P[0]中:

struct pt {
    double x,y;
    friend pt operator+(pt a, pt b){a.x+=b.x; a.y+=b.y; return a;}
    friend pt operator-(pt a, pt b){a.x-=b.x; a.y-=b.y; return a;}
    friend pt operator*(pt a, double k){a.x*=k; a.y*=k; return a;}
    bool leftof(pt a, pt b) const{
        // returns true if the *this point is left of the segment a--b.
        return (b.x-a.x)*(y-a.y) - (x-a.x)*(b.y-a.y) > 0;
    }
};
pt intersect(pt a, pt b, pt c, pt d){// (see O'rourke p.222)
    double s,t, denom;
    denom = (a.x-b.x)*(d.y-c.y)+ (d.x-c.x)*(b.y-a.y);
    s = ( a.x*(d.y-c.y)+c.x*(a.y-d.y)+d.x*(c.y-a.y) )/denom;
    return a + (b-a)*s;
}
/**
    P is a polygon, P[0] is a reflex (the inside angle at P[0] is > pi).
    finds a vertex t such that P[0]--P[t] is a diagonal of the polygon.
**/
int diagonal( vector<pt> P ){
    pt a = P[0], b = P[1]; //alias
    int j=2;
    if( !b.leftof(a,P[j]) ){
        // find first edge cutting a--b to the right of b
        for(int k = j+1; k+1 < int(P.size()); ++k)
            if( P[k].leftof(a,b) && P[k+1].leftof(b,a) && b.leftof(P[k+1],P[k]) )
                j = k,
                b = intersect( a,b,P[k],P[k+1] );
        // find nearest edge cutting the segment a--b 
        for(int k = j+1; k+1 < int(P.size()); ++k)
            if( P[k].leftof(a,b) && P[k+1].leftof(b,a) &&
                a.leftof(P[k+1],P[k]) && b.leftof(P[k],P[k+1]) ){
                b = intersect( a,b,P[k],P[k+1] );
                j = k+1;
            }
    }
    for(int k = j+1; k+1 < int(P.size()); ++k)
        if( P[k].leftof(a,b) && P[k].leftof(b,P[j]) && P[k].leftof(P[j],a) )
            j = k;
    return j;
}

答案 2 :(得分:0)

您可以在 O(n)时间内测试任何单个顶点,从而测试 O(n ^ 2)中的所有顶点。要测试 V 中是否有任何单个顶点 U ,请在 V U 之间构建一条线。我们将此行称为 L 。现在,测试 L 以查看它是否与任何多边形边相交。如果没有, U V 隐藏 。如果是,则 U 被遮挡。

此外,您可以测试 L 是否位于多边形内,如下所示:假设 V 上的事件边缘为 E1 E2 即可。计算 E1 E2 (称为 a1 )之间以及 E1 L <之间的有符号角度/ strong>(称之为 a2 )。 a2 的符号应与 a1 相同(即 L 位于 E1 E2 是), a2 应小于 a1 (即, L '来自'<强> E2 )。

小心交叉点测试,因为 L 会轻微地与入射到 V 的多边形边相交。您可以忽略这些交叉点。

此外,如果 U 分享 V 的任何边缘, V 可以从 V 中轻易看到 U

答案 3 :(得分:0)

您可以使用多边形的三角剖分。

假设您有一个三角测量T,可以通过检查三角测量中的相关内部边缘找到顶点U的一组可见顶点V。具体来说,如果遍历附加到V的三角形集并且标识了内部边(那些出现两次!),则集U是除V之外的所有边顶点。

请注意,这不一定是来自V所有可见顶点,只是一个|U| >= 0 的集合(必须至少是V的一个内部边缘)< /秒>。但它是有效的 - 仅O(m)其中m是所访问的三角形/边数,对于合理的输入,它基本上是O(1)

当然,您需要先建立一个三角测量。有一些有效的算法允许在O(n*log(n))中构建受约束的Delaunay三角剖分,但这不是O(n)。可以在TriangleCGAL中找到良好约束的Delaunay实现。

答案 4 :(得分:0)

继续沿着某个方向前进通过以V开头的顶点并更新可见顶点列表。如果我没有错过任何东西,那将是O(n)。

为简单起见,我们将V称为可见。

我已经尝试了一天用文字表示,失败并使用了伪代码:)

visible_vertices = {V}
for each next segment in counter-clockwise polygon traversal
  if segment is counter-clockwise (looking from V)
    if (visible_vertices.last -> segment.end) is counter-clockwise
      visible_vertices.add(segment.end)
  else
    while segment hides visible_vertices.last or segment.start=visible_vertices.last
      visible_vertices.remove_last