凹多边形“船体”发现

计算科学 算法 计算几何 图论 凸包
2021-12-04 20:34:29

我实现了一个算法来找到一组点的 alpha 形状。alpha 形状是一组点的凹壳,其形状取决于决定哪些点组成壳的参数 alpha。

我已经解决了凹壳中的点集。这些点构成一个凹多边形。我想以顺时针的方式排列这些点。

当它是凸形时,以顺时针方式对点进行排序很简单。带着凹凸的东西,真不知道怎么办。背后是什么算法?我查看了“非交叉最短路径”算法、“最短非交叉哈密顿路径”算法,但我仍然不相信任何这些解决方案。

有简单的解决方案吗?

编辑

在此处输入图像描述

1个回答

我不确定你的问题是什么。

一个 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 条边作为多边形,连接点。你应该如何用你最喜欢的语言来做这件事我无法解决,因为我什至不知道那是什么。