【问题标题】:A linear-time algorithm to find any vertex of a polygon visible from other vertex一种线性时间算法,用于查找从其他顶点可见的多边形的任何顶点
【发布时间】:2012-11-05 23:04:33
【问题描述】:

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

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

有没有一种算法可以在O(N) 或更好的时间内做到这一点?有没有一种算法可以在O(N)时间找到所有可见的顶点?

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

【问题讨论】:

  • 嗯,这是binary space partition。它允许在 O(N) 中进行可见性检查,但设置要复杂得多,所以如果你反复检查同一个多边形,它只会对你有好处,而且我猜你不是......
  • @XavierHolt:实际上,我会多次搜索不同的顶点 V,因此最多 O(N*log N) 的设置是可以的。但是检查 O(N) 可能还不够。只需遍历多边形的所有边,我就可以在没有任何数据结构的情况下进行 O(N) 检查。

标签: algorithm geometry computational-geometry


【解决方案1】:

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

ElGindy 和 Avis,“从一个点计算可见性多边形的线性算法”,J。算法 2,1981 年,p。 186--197。

Joe & Simpson 在 1985 年发表了一篇非常好的论文,“Visibility of a simple polygon from a point”,提供了经过仔细验证的伪代码: (PDF download link)。 从那以后,这肯定已经用多种语言实现了很多次。 例如,the Wikipedia article on the topic 有一个链接。

【讨论】:

  • 如果有人对 Joe 和 Simpson 算法的实现感兴趣,请参阅 this
【解决方案2】:

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

从一个反射a开始,让a'为下一个顶点,沿着多边形,直到找到一条穿过a--a'的边 em>在边a',设b为这条边与a--a'线的交点c 边缘的终点(a--c 右边的那个)。

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

这是 C 中的一个实现,它假设反射点 aP[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;
}

【讨论】:

  • 这似乎几乎可以工作。但是当多边形缠绕太多(螺旋多边形)时,会出现问题,第一个找到的顶点 c 将无法正确找到。此外,检查 d 是否在三角形内可能是不够的,因为它可能会阻止 c 的可见性,即使它不在三角形内。
【解决方案3】:

您可以在 O(n) 时间内测试任何单个顶点,因此可以在 O(n^2) 时间内测试所有顶点。要测试是否有任何单个顶点 UV 可见,请在 VU 之间构建线。我们称这条线为L。现在,测试 L 以查看它是否与任何多边形边相交。如果不是,则 U 不会V 遮挡。如果是这样,U 会被遮挡。

此外,您可以像这样测试 L 是否位于多边形内:假设 V 上的入射边是 E1 E2。计算 E1E2 之间(称为 a1)以及 E1L(称之为a2)。 a2 的符号应与 a1 相同(即,LE1的“同一”侧> 因为 E2 是),并且 a2 应该小于 a1 (即,L 'comes before' E2)。

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

此外,如果 U 共享与 V 相关的任何边缘,则 UV 上很容易看到.

【讨论】:

  • 我应该选择哪一个?你的算法是 O(N^2)。
  • 当然,您还需要检查 L 是否位于多边形内部
  • 你应该选择第一个通过测试的U。
  • OK,但编辑后仍然是 O(N^2)。我只对 O(N) 算法感兴趣。
  • 很公平!如果我想到什么我会告诉你的。您是否可以以任何方式预处理多边形?另外,出于兴趣,这些多边形有多少个顶点?
【解决方案4】:

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

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

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

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

【讨论】:

  • 谢谢。一个有效的答案,但我宁愿不以这种方式实现它。在 O(N*log(N)) 中实现三角剖分算法并非易事。
【解决方案5】:

通过从 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-19
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    • 2018-01-21
    • 2010-10-03
    • 1970-01-01
    相关资源
    最近更新 更多