我有一个计算几何问题,我认为应该有一个相对简单的解决方案,但我无法弄明白。
我需要确定由多个线段定义的区域的非凸轮廓。
我知道各种非凸壳体算法(例如alpha形状),但我不需要完全通用的算法,因为线段在大多数情况下定义了一个独特的解决方案。
正如@ Jean-FrançoisCorbett指出的,有些情况下有多种解决方案。我显然需要更多地考虑我的定义。
但是,我要做的是逆向工程并使用专有文件格式,以便我可以对自己和其他人收集的数据进行基本分析。文件格式很简单,但确定用于定义边界的算法要困难得多。
在许多可能导致非唯一解决方案的边缘情况下,导致相关软件在没有警告的情况下崩溃或者无法读取文件。
因此,当存在多个解决方案时,要么生成一个可接受的解决方案,要么能够确定存在多个解决方案,这是可以接受的。
多边形的轮廓不应该穿过任何线段,并且应该由连接所有线段端点的线组成。所有段必须完全位于多边形的边界内或沿着多边形的边界。大纲中不能使用多个端点(通过在需要多边形关闭的软件库的末尾添加第一个点来忽略“关闭”多边形。)。
如果有多个解决方案符合此标准,那么这些解决方案中的任何一个都是可以接受的。 (能够确定解决方案何时是非唯一的,但这不是绝对必要的,这将是很好的。)
举个例子,我有以下几点:
我想描述以下几个方面:
它也适用于非交叉段。 E.g。
我认为(?)在任何一种情况下都有一个独特的解决方案,符合之前的标准。 (编辑:一般来说,没有一个独特的解决方案,正如@ Jean-FrançoisCorbett所指出的那样。但是,我仍然对能够产生一种可接受的解决方案的算法感兴趣。)
对于测试用例,这里是生成上述数字的代码。我在这里使用python,但这个问题与语言无关。
import matplotlib.pyplot as plt
def main():
test1()
test2()
plt.show()
def test1():
"""Intersecting segments."""
segments = [[(1, 1), (1, 3)],
[(3.7, 1), (2, 4)],
[(2, 0), (3.7, 3)],
[(4, 0), (4, 4)],
[(4.3, 1), (4.3, 3)],
[(0, 2), (6, 3)]]
desired_outline = [segments[0][0], segments[5][0], segments[0][1],
segments[1][1], segments[2][1], segments[3][1],
segments[4][1], segments[5][1], segments[4][0],
segments[3][0], segments[1][0], segments[2][0],
segments[0][0]]
plot(segments, desired_outline)
def test2():
"""Non-intersecting segments."""
segments = [[(0, 1), (0, 3)],
[(1, 0), (1, 4)],
[(2, 1), (2, 3)],
[(3, 0), (3, 4)]]
desired_outline = [segments[0][0], segments[0][1], segments[1][1],
segments[2][1], segments[3][1], segments[3][0],
segments[2][0], segments[1][0], segments[0][0]]
plot(segments, desired_outline)
def plot(segments, desired_outline):
fig, ax = plt.subplots()
plot_segments(ax, segments)
ax.set_title('Segments')
fig, ax = plt.subplots()
ax.fill(*zip(*desired_outline), facecolor='gray')
plot_segments(ax, segments)
ax.set_title('Desired Outline')
def plot_segments(ax, segments):
for segment in segments:
ax.plot(*zip(*segment), marker='o', linestyle='-')
xmin, xmax, ymin, ymax = ax.axis()
ax.axis([xmin - 0.5, xmax + 0.5, ymin - 0.5, ymax + 0.5])
if __name__ == '__main__':
main()
有什么想法吗?
我开始怀疑其结果我试图再现该软件使用径向扫描算法在某种“内部”的坐标系(例如坐标与系统x-prime
和{{1沿着由点的扩展定义的主轴进行缩放和旋转。这使得问题更加“圆形”。)然而,这产生了在许多情况下轮廓与线段相交的解。很容易发现它并从那里强力发出它,但肯定有更好的方法吗?
答案 0 :(得分:17)
注意:如果存在不与任何其他线段相交的“自由浮动”外围线段,则会失败。但是,您指定“条形图唯一定义解决方案”,这将排除此失败条件。 (边远部分使两种不同的解决方案成为可能。)
编辑 ...或者更确切地说,外围细分可以制作两种截然不同的解决方案 - 具体取决于确切的布局。证明:下面是一个例子,我添加的黄色部分使得两种解决方案成为可能(蓝色和灰色可怕的手绘线)。如果黄色部分的方向垂直于它现在绘制的方式,那么只有一个解决方案是可能的。听起来你的问题定义不明确。
编辑实际上,如果你的网段集合是“非常凹的”,也可能会失败,也就是说,如果在你的一堆网段隐藏的角落里隐藏着端点。在下图中,我添加了一个黑色部分。我的算法会非法将其端点连接到另一个端点(灰色虚线)。我会留下我的答案,万一其他人倾向于建立它。
编辑之后再多想一想: 即使在“非常凹”的情况下,这个解决方案肯定会给你所有您的凹形船体的点以正确的顺序排列,但它们可能会散布着额外的不合适的点,例如黑色的点。所以可能会有太多的点。
当然,答案是做一些修剪。这将是相当复杂的修剪,特别是如果你可以有多个连续的“隐藏点”,如黑色,所以我没有考虑智能算法。但即使是盲目的,蛮力也是可行的。每个点都可以被接受或拒绝(布尔值),因此如果你的凹形船体中有 N 正确排序的候选点,那么只有2 ^ N 可以检查。这样,方式比原始排列问题的蛮力更少的可能性,这将有SUM of (n!/(n-k)!) for k=1:(n-1)
种可能性(请原谅我的注释)。因此,该算法显着缩小了您的问题。
我认为这是要走的路。
答案 1 :(得分:2)
不是一个完全充实的想法,但无论如何:假设你开始使用圆形扫描算法来进行凸包(你在那里排序,然后按照它们从中心点的角度处理点)。如果所有的点都在这个船体中,你就完成了。如果没有,那么你必须“收紧”船体以包括这些点。这些点中的每一个都是凸包的候选者,并且因为它们打破了凸起而被移除。有时(与第一个例子中的顶部紫色点一样),我们可以简单地将它们留在。我们不能的地方,因为船体的新段穿过一段(从底部绿色到底部紫色)第一个例子,假设底部浅绿色点在绿色之前得到处理),修复更多一些(并且我没有充实的部分,并且是问题的最新编辑中提到的部分)。