我使用了一个库(JavaScript-Voronoi),它产生了一个代表闭合多边形的线段数组。这些段看起来是无序的,包括段出现的顺序以及段的每一端的点的排序。
(修改:如下面的评论中所述,我错了:库中的细分排序良好。但是,问题代表如下:让我们假设这些细分没有任何排序,因为这使它更有用。)
例如:
var p1 = {x:13.6,y:13.1}, p2 = {x:37.2,y:35.8}, p3 = {x:99.9,y:14.6},
p4 = {x:99.9,y:45.5}, p5 = {x:33.7,y:66.7};
var segments = [
{ va:p1, vb:p2 },
{ va:p3, vb:p4 },
{ va:p5, vb:p4 },
{ va:p3, vb:p2 },
{ va:p1, vb:p5 } ];
注意第一个段如何链接到最后一个段(它们共享一个公共点),以及倒数第二个段。保证每个段与另一个段完全共享。
我想将其转换为点列表以生成正确的SVG多边形:
console.log( orderedPoints(segments) );
// [
// {"x":33.7,"y":66.7},
// {"x":13.6,"y":13.1},
// {"x":37.2,"y":35.8},
// {"x":99.9,"y":14.6},
// {"x":99.9,"y":45.5}
// ]
点是顺时针还是逆时针顺序并不重要。
以下代码是我提出的,但在最坏的情况下,需要进行n^2+n
点比较。是否有更有效的算法将所有这些加在一起?
function orderedPoints(segs){
segs = segs.concat(); // make a mutable copy
var seg = segs.pop(), pts = [seg.va], link = seg.vb;
for (var ct=segs.length;ct--;){
for (var i=segs.length;i--;){
if (segs[i].va==link){
seg = segs.splice(i,1)[0]; pts.push(seg.va); link = seg.vb;
break;
}else if (segs[i].vb==link){
seg = segs.splice(i,1)[0]; pts.push(seg.vb); link = seg.va;
break;
}
}
}
return pts;
}
答案 0 :(得分:3)
如果你的多边形是凸的,你可以选择每个线段的中点,然后使用convex hull算法通过中间项找到凸多边形,之后,因为你知道什么是middles的排列,你也知道知道哪个中间属于哪个段,你可以在原始数组中找到一个排列。
如果你只是想找到一个凸包,直接使用凸包算法,它是O(n log n),它足够快,但你也可以在javascript here中找到一个Quickhull算法。 quickhull也在O(n logn)
,但平均而言,最坏的情况是O(n ^ 2),但由于常数因子较少,因此速度很快。
但是在通用算法的情况下:
将每个段的一端设置为First,将另一端设置为second(随机)。
按照第一个x
对细分进行排序,然后将其放入数组First
中,然后将其放在第一个排序段中,第一个排序段的第一个x
位于第一个y
之后,放入两个额外int
到您的结构中以保存具有相同第一个x
的项目的开始和结束位置。
然后再使用第二个x
值对您的细分进行排序,....并创建数组second
。
以上操作均为O(n log n)。
现在选择数组First
中的第一个细分,在数组x
和First
中搜索其第二个second
值,如果找到相似的值,请搜索相关子数组中的y
值(您具有相同x
项的开始和结束位置)。您知道此订单只有一个细分受众群(也不是当前细分受众群),因此找到下一个细分受众群需要O(log n)
,因为总共有n-1
下一个细分受众群需要O(n logn)
(同样预处理),它比O(n^2)
快得多。
答案 1 :(得分:2)
应该可以在线性时间内将点转换为(双重,无序?)链表:
for (var i=0; i<segments.length; i++) {
var a = segments[i].va,
b = segments[i].vb;
// nexts being the two adjacent points (in unknown order)
if (a.nexts) a.nexts.push(b); else a.nexts = [b];
if (b.nexts) b.nexts.push(a); else b.nexts = [a];
}
现在你可以迭代它来构建数组:
var prev = segments[0].va,
start = segments[0].vb, // start somewhere, in some direction
points = [],
cur = start;
do {
points.push(cur);
var nexts = cur.nexts,
next = nexts[0] == prev ? nexts[1] : nexts[0];
delete cur.nexts; // un-modify the object
prev = cur;
cur = next;
} while (cur && cur != start)
return points;
如果您不想修改对象,EcmaScript6 Map
(带对象键)会派上用场。作为一种变通方法,您可以使用点坐标的JSON序列化作为普通对象的键,但是您将被限制为不包含两次坐标的多边形。或者只使用库添加到顶点的唯一voronoiId
属性来识别它们。
答案 2 :(得分:2)
对于凸多边形,您甚至不需要知道侧面分段。你只需要一堆顶点。订购顶点的过程非常简单。
有序顶点按多边形顺序排列。你可以删除一些冗余。 1以线性时间运行,2以线性时间运行,3次以n log n运行。