检测段和连接器集合中所有闭合路径的最有效方法是啥?

Posted

技术标签:

【中文标题】检测段和连接器集合中所有闭合路径的最有效方法是啥?【英文标题】:What would be the most efficient way to detect all closed paths in a collection of segments and connectors?检测段和连接器集合中所有闭合路径的最有效方法是什么? 【发布时间】:2016-01-24 22:25:31 【问题描述】:

我们有一个由连接器和段组成的数据集。每个段正好有两个连接器,但每个连接器可以属于零个或多个段(即下图中左图中的连接器“A”没有段,而连接器“M”有三个,MR、ML 和 MN。)

据了解,任何线相交或相交的地方都会有一个连接器,因此我们不必担心偶数/奇数规则、重叠或部分封闭的多边形等,因为它们不适用。

简而言之,我们正在尝试识别所有创建的多边形(右图中的彩色形状)。我相信这可以分两步完成。

第 1 部分:删除多余的项目

独立连接器(此处为连接器“A”)可以简单地移除,因为它们不能成为形状轮廓的一部分。

引用单个段(连接器“B”和“E”)的浮动端点也可以被删除,因为它们也不能成为形状轮廓的一部分。这也将删除它们引用的片段(B-C 和 E-D)。

递归执行上述操作将接下来将“C”标识为端点(因为“B”和 B-C 已被删除),因此它及其剩余的段 C-D 也可以被删除。在下一次递归过程中,连接器“D”和段 D-F 也将被删除,等等。

但是,我还没有找到识别细分 H-I 的好方法。也就是说,我认为这可以在多边形检测期间实现,因为这些片段只会是复合路径的结果,并且会在一个形状的检测过程中在两个方向上进行跟踪。 (更多内容见下文。)

第 2 步:多边形检测

可以在两个方向上跟踪每个段。例如,连接“O”和“P”的段可以是 O-P 或 P-O。选取顺时针方向的轨迹,O-P 属于多边形 O-P-Q-N,而 P-O 属于多边形 P-O-I。

以下逻辑假设跟踪方向为顺时针。

从任何线段开始,当追踪时,如果你回到你的起点,你已经确定了一个潜在的多边形。通过在跟踪时保持航向角度的运行增量(这是航向转动的角度,不要与简单地添加线段之间的角度混淆),完成后,如果该角度为正,您已经检测到有效的多边形。如果它是否定的,则您检测到“包含”多边形,即包含一个或多个“有效”多边形的多边形。整个形状(或形状)的外周边都包含多边形。

考虑一个正方形的情况,对角线分成两个三角形。跟踪每个线段两次——每个方向一次——你最终会得到三个可能有效的多边形:一个正方形和两个三角形。三角形将有一个正的角度增量,告诉您它们是有效的,但正方形的角度增量是负的,告诉您这是包含多边形。

注意:包含多边形也可以等于有效多边形。它只会向相反的方向“缠绕”。

考虑一个简单的三角形。顺时针跟踪将产生有效的多边形。第二次尝试顺时针追踪实际上会产生一个逆时针追踪,这会给你一个负角增量,告诉你这实际上是形状的轮廓。

注意:您还必须测试沿途遇到的其他多边形,方法是在形状检测期间测试每个点是否有任何先前遇到的点。如果您发现您重新访问了同一点,请保存自第一次遇到该点以来创建的多边形,检查它的角度。如果它是正的,它是一个有效的多边形(并且你实际上正在追踪一个包含的多边形。)如果它是负的,你已经检测到一个包含的多边形(在这种情况下,你当前正在追踪一个有效的多边形。)最后,删除所有将累积堆栈上的段返回到上次遇到该点的第一个实例,然后继续进行检测。

例如,如果您从“J”开始并逆时针追踪,您将经过“I”、“H”,然后是“G”,然后是“F”,然后您会回到“H” '。您刚刚找到了一个多边形 H-G-F,它有一个负角,所以您知道它是一个包含多边形。从堆栈中删除这三个段并继续。现在你将再次点击“我”。在这种情况下,您已经在此过程中访问了同一段,但是在另一个方向,所以只需从堆栈中完全删除该段并继续,在“O”旁边,然后是“N”等。你最终会回到'J'。

当一个片段在两个方向上都被追踪时,它可以被认为是“使用过的”,并且不需要对该片段进行进一步的处理。继续处理所有未使用的段。一旦在两个方向上跟踪了所有线段,您就可以确定所有多边形(有效的和包含的)都已找到。

最后,检查每个包含的多边形,看看它是否在任何有效的多边形内。如果是这样,请将其从创建复合路径的有效多边形中排除。在此处的示例中,包含多边形 H-G-F 包含在有效的青色多边形中,因此应将其排除。注意还有一个有效的 H-F-G 多边形,这里用红色标记。

无论如何,这就是我想出的,但我想知道是否有更好/更简单的方法。想法?

【问题讨论】:

我费了一番周折才决定明白您的要求。为了看看我是否正确,让我用稍微数学的术语来表达:给定一个平面图,是否有一种算法可以选择一组最大的多边形(当然,其边是从图中绘制的)使得每个点在该平面要么在多边形边界上,要么恰好包含在一个多边形中?这似乎是对您的问题的公平重述? 谷歌搜索后:看起来 boost 有planar_face_traversal,它的功能与您想要的类似(尽管可能不完全相等)。您可能会查看他们的实现以获取一些想法 - 或者甚至可以按原样使用它。 HI 的情况尚不清楚。如果你把它拿掉,脸 KJINML 就会有一个洞。这允许吗? @DanielWagner 的链接为双连通图提供了 O(n+m) 时间的算法。您所要做的就是首先删除所有悬垂边(很容易找到——一个端点的度数为 1)和桥(删除会断开图形的边)——还有相当简单、有效的算法。然后你可以寻找完全包含其他多边形的多边形——这应该相当快,因为​​你可以先减少面积来排序,然后你只需要检查一个较早的多边形是否包含后面的多边形,测试一个点是够了。 @Yves,是的,这是允许的。请参阅右图的标题。 【参考方案1】:

提示

您的问题具有几何方面(不是纯粹的连通性),因为面可能不重叠并且已知很简单。我会推荐一种扫描线方法。

第一次清理以丢弃所有浮动端点。

然后考虑一条从上到下逐个顶点移动的水平线。在渗透线的每个位置,它都包含或相交多个线段。从左到右对所有顶点/交点进行排序,您会得到不重叠的线段。

诀窍是随着扫描线的进行跟踪端点,以便找到区域的左右边界。

在给定的示例中,您将依次考虑点

R  K        J
RM KL G     JI
 M  L GF GH JI
 MN    F GH JI
 MN       H JI
  N  O       I
  NQ   P
   Q

(对表示交叉点)。

由此,您应该能够从连接性考虑重建左/右轮廓

R M | K L
K L M N | G F H | G H | J I (and embedded G F H | G H)
N Q | O P Q
O P | I P

这是您通过将现有边的端点和交点从扫描线链接到扫描线而获得的图表。

并且在清理之后,移除中间顶点:

【讨论】:

我遵循了前半部分,但没有遵循第二个“重建”部分。你能更清楚地解释一下你是如何得到这些项目的吗?不过看起来很有前途! 我没有完全扩展它,我不想这样做,抱歉。主要思想是考虑梯形(梯形?),如 KJ-JI-KL、KL-C-CL-L、C'-JI-JI-C'H...(青色区域)并将它们合并,只需使用标签信息。您在扫描时执行此操作。你应该能够管理这个。真正重要的想法是从左到右对段进行排序以避免重叠并确定平面细分。 IMO 在扫描期间最好将像 C 这样的向上顶点拆分为两个代表 (CC') 以保持一致性:您始终处理具有两个端点的线段,即使有时有些端点可能相同位置。 (您可以稍后清理。) 嗨。您说的是“C”,但您的意思是“G”吗?此外,我不会再添加“G”,只是为了稍后将其删除。如果是这样,为什么要添加它?我非常感谢您添加解释(尤其是因为您说您不想这样做),但我仍然没有完全遵循这一点。 没有 C,只是覆盖了 G(你自己的符号!)每行保持偶数点会更容易。

以上是关于检测段和连接器集合中所有闭合路径的最有效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL 中找到两个集合的最紧凑和最有效的方法是啥? [复制]

修改 Eloquent 查询以使其始终返回空集合的最有效方法是啥?

从 HMT 连接表中获取属性的最有效方法是啥?

在 Python 中查找数字的所有因数的最有效方法是啥?

计算图像中给定矩形内所有像素总和的最有效方法是啥?

在不对所有记录执行循环的情况下从数据库中检索特定数据的最有效方法是啥?