假设有一个没有孔的多边形和由n
个顶点定义的自交叉(即一个简单的多边形)。选择此多边形的反射顶点v
。
我想找到从顶点u
“可见”的同一多边形的任何其他顶点v
。可见我的意思是,v
和u
之间的线段完全位于多边形内。
是否有算法在O(N)
时间或更好时间执行此操作?是否有算法可以在O(N)
时间内找到所有可见顶点?
快速研究表明,对于给定的多边形和此多边形内的任何点,可以在O(N)
中构造visibility polygon。我认为找到一个可见的顶点应该更容易。
答案 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)
。可以在Triangle和CGAL中找到良好约束的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