我实现了一个算法来找到一组点的 alpha 形状。alpha 形状是一组点的凹壳,其形状取决于决定哪些点组成壳的参数 alpha。
我已经解决了凹壳中的点集。这些点构成一个凹多边形。我想以顺时针的方式排列这些点。
当它是凸形时,以顺时针方式对点进行排序很简单。带着凹凸的东西,真不知道怎么办。背后是什么算法?我查看了“非交叉最短路径”算法、“最短非交叉哈密顿路径”算法,但我仍然不相信任何这些解决方案。
有简单的解决方案吗?
编辑我实现了一个算法来找到一组点的 alpha 形状。alpha 形状是一组点的凹壳,其形状取决于决定哪些点组成壳的参数 alpha。
我已经解决了凹壳中的点集。这些点构成一个凹多边形。我想以顺时针的方式排列这些点。
当它是凸形时,以顺时针方式对点进行排序很简单。带着凹凸的东西,真不知道怎么办。背后是什么算法?我查看了“非交叉最短路径”算法、“最短非交叉哈密顿路径”算法,但我仍然不相信任何这些解决方案。
有简单的解决方案吗?
编辑我不确定你的问题是什么。
一个 alpha 形状代码以 delaunay tessellation 开始(至少我的如此。)它会侵蚀任何不符合 alpha 形状标准的单纯形,一次一个单纯形。剩下的仍然是域的细分。
接下来,我将通过列出每个单纯形的所有方面(在二维中,这些只是边)来找到该复杂的外边界。这些边由顶点列表中的索引对定义。查找重复的面,并排除在列表中出现两次的所有边。(确保您将边缘 [1,2] 捕获为边缘 [2,1] 的副本。)列表中剩余的必须仅是外表面上的那些,因为它们是非共享的面。
因此,在二维中,我们现在有一个包含封闭多边形的边列表,根据 alpha 的值,它可能是凹的,以生成该 alpha 形状。将边连接成一个多边形,依次遍历一个到下一个。同样,由于边列表实际上是对顶点列表的引用列表,因此排序将很简单。如果您的选择最终是逆时针多边形,请翻转它们。
编辑:
也许解释这一点的唯一方法是举个例子。我正在使用自己的一套工具来计算 alpha 形状等,但是无论您使用什么工具,这些想法都是相同的。所有计算均在 MATLAB 中完成。
我将从平面上的 121 个格点开始,然后通过咬掉一个边缘来排除一些格点,从而排除其中的 34 个点。
xy = lattice({0:.1:1,0:.1:1})
xy =
0 0
0 0.1
0 0.2
0 0.3
0 0.4
0 0.5
0 0.6
0 0.7
0 0.8
0 0.9
0 1
0.1 0
0.1 0.1
...
1 0.7
1 0.8
1 0.9
1 1
>> k = sqrt(sum(bsxfun(@minus,[.8 .5],xy).^2,2)) < .35;
>> sum(k)
ans =
34
>> xy(k,:) = [];
>> plot(xy(:,1),xy(:,2),'o')
现在,我可以对该集合进行三角剖分。同样,我将使用我自己的工具,但在 matlab 中,delaunay.m 正是这样做的。
>> tri = delaunayn(xy)
ans =
84 79 83
86 80 85
75 79 84
84 66 75
75 66 71
85 80 76
76 67 85
...
镶嵌本身只是一组参考。该数组的每一行都是一个三角形,所以我们有一个三角形作为 [84 79 83],它是对我们原始点集的那些点的引用。三角剖分是这样的:
请注意沿右边缘跨越孔的长刻面。现在,计算一个 alpha 形状。最大最近邻距离为 0.1,所以这里我将使用半径为 0.2 的 alpha 球(最大最近邻距离的两倍。)
>> sc = alphashape(xy,.2);
>> tria = sc.tessellation;
所以阿尔法形状是黄色的,它的三角形有黑色边缘。我已经用蓝色边缘覆盖了 delaunay 三角剖分。
alpha 形状中有 130 个三角形,因为每个三角形有 3 条边,我将有一个 130x3 = 390 条边的列表,其中一些是重复的。该列表中实际上只有 216 条边。
>> edgelist = [tria(:,[1 2]);tria(:,[1 3]);tria(:,[2 3])];
>> size(unique(sort(edgelist,2),'rows'))
ans =
216 2
然而,我们希望看到的只是那个 alpha 形状边界上的边缘。这些边是在列表中恰好出现一次的边。一些仔细的排序可以解决边界边缘。
>> sortrows(sort(edgelist,2))
ans =
1 2
1 12
2 3
2 12
2 12
2 13
2 13
2 14
2 14
3 4
3 14
3 14
3 15
3 15
4 5
4 15
4 15
5 6
...
因此,我们看到边缘 [1 2] 在列表中出现了一次,但边缘 [2 12] 出现了两次,边缘 [2 13] 也是如此。如果我们想看到边界,这些后面的边缘是我们希望移除的内部边缘。在 MATLAB 中,我会这样做以删除列表中出现两次的那些:
>> k = find(all(diff(sortrows(sort(edgelist,2))) == 0,2));
>> edgelist(k,:) = []
edgelist =
79 83
80 85
75 79
76 80
50 51
60 61
49 50
67 68
58 59
78 82
1 2
2 12
10 21
10 20
...
42 条边保留在代表边界多边形的 1 流形中。
我仍然不确定你在哪里卡住了,但这应该可以解决一些问题。只需使用这 42 条边作为多边形,连接点。你应该如何用你最喜欢的语言来做这件事我无法解决,因为我什至不知道那是什么。