【问题标题】:How to create closed areas (convex polygons) from set of line segments?如何从一组线段创建封闭区域(凸多边形)?
【发布时间】:2011-03-05 09:32:37
【问题描述】:

以下问题是二维的,所以建议答案时可以做一些简化。

我需要从一组点/线段创建封闭区域(由线段或仅由一组点定义 - 凸多边形)。

基本上我使用 Voronoi 来生成“道路”。然后我改变了一些数据。现在我需要一种方法来遍历该数据(仍然是线段,但不再符合 Voronoi)并生成与“道路”接壤的“社区”。

我看了一些图表和最短路径理论,但我无法弄清楚。

从逻辑上讲,可以通过从一个点从左边缘开始,使用可用线的最短路径(仅使用顺时针方向)找到返回该点的路。然后将这条线标记下来并从数据中删除。然后你可以重复同样的过程,得到所有类似的区域。

我试图实现它,但它没有让我到任何地方,因为我想不出一种方法来编写可以做到这一点的 C++ 代码。问题在于从特定点的可用线中选择最逆时针的线。我所做的所有基于角度的数学都给出了错误的答案,因为 sin/cos 是在 c++ 中实现的。

所以总结一下 - 如果你能帮助我用一种全新的方法来解决这个问题,那很好,如果不能,你能帮助我找到一种方法来编写找到返回起点的最短顺时针路径的代码部分将线段设置为返回路径。

编辑:添加了一张图片来说明我想要做什么。

在此处查看图片 -(需要 10 个声望才能在此处发布:P)

我有一组点(紫色小点)。另一个数组定义了哪些点组成了一条线(道路)。我想要一种方法来定义被道路包围的区域,这样我就可以在其中放置建筑物或较小的道路并针对边缘进行测试,以便将每个区域分开。希望这能为您提供有关如何解决此问题的更多信息。

感谢您的帮助!

【问题讨论】:

  • 你能发一张图片吗?有时它比一堵文字墙要好,而且大多数时候会激起读者阅读文字墙的兴趣:)
  • @Jacob - 完成了,现在你可以看到我想要做的事情的图像了 :)
  • 您找到答案了吗?能否请您再次添加图片链接?

标签: c++ graph geometry polygon


【解决方案1】:

根据您的说明:

也许你可以试试这个:

由于Voronoi,您可能已经拥有任何给定purple蓝点的“相邻”点列表。现在给定一个紫色点 P,有一个邻居 Q,您可以考虑与线段 PQ 相交的道路。所有这样的道路(即 P 的邻居之间的不同 Q)可能会形成 P 周围的封闭区域。

即使您没有“邻居”信息,您也可以尝试所有可能的 purple 蓝点对,看看哪些线段正好与一条道路相交。对于给定的点,这些道路的集合将围绕它形成一个封闭区域。

这可能不是最佳的,但可能有效,尽管我没有尝试证明它。


抱歉,您的问题不是很清楚,但我认为Convex Hull 会派上用场。计算船体时可以使用段的端点。

如果您想要多个不相交的“区域”,您可以尝试找到一条分隔线并分别运行凸包。

【讨论】:

  • 这没有多大帮助,因为我在选择哪些点(或线)构成“区域”之一时遇到了问题。我需要收集一组点来定义城市路线图中的一个“地区”。之后,我可以对每个区域重复此操作,因此我最终得到了一组点(线)集,这些点(线)集定义了每个城市单元以填充建筑物。希望这是有道理的,因为它很难解释;)
  • 不知道它是否会起作用,因为线之间的每个交点都是一个紫色点。这意味着没有一条线与两个紫色点之间的线段相交。还是我误会了你?
  • @Marten:在图中,我所说的点在我看来是蓝色的。还有一些橙色和红色。这些是你最初用来形成 Voronoi 图的点,我想它们完全被黑色的道路包围了。
  • 是的,这看起来更合乎逻辑;)但问题是我使用了 Voronoi 线并将它们分割成更小的部分,以使道路不那么直,这意味着我可以找到一些路段,例如那但不是全部。
  • @Marten:如果你有关于紫色点的数据,也许你可以考虑两个紫色点之间的“虚拟”道路,在虚拟道路上运行上述方法并返回真实道路,使用相关的紫色端点。
【解决方案2】:

您沿着最短路径沿着线段走的想法(例如,如果您有多个选择,则沿着最顺时针方向走)是好的。

你可以在没有任何 sin/cos 调用的情况下找到这条线。

这个想法是这样的:

假设您有两条线可供选择。调用线与 A 相交的点(例如您当前的位置)。两条线的端点称为 B 和 C。

这三个点构成一个三角形。现在看看这三个点是如何定位的。如果您按照从 A 到 B 到 C 的点,方向将是顺时针或逆时针。显然,如果顺序是顺时针的,那么从 A 到 C 的线将是顺时针最多的那条。否则就是从 A 到 B 的线。

如果您有超过两条线可供选择,只需选择前两条,请丢弃进入错误方向的线并进行相同的测试,直到最终得到一条线。那是最顺时针的。

现在关于数学:如何在不调用 sin/cos 甚至更糟的情况下找出三点的缠绕顺序:atan:

您可以使用叉积来做到这一点。首先构建两个向量,分别给出从 A 到 B 和从 A 到 C 的方向:

   u.x = B.x - A.x
   u.y = B.y - A.y

   v.x = C.x - A.x
   v.y = C.y - A.y

现在我们可以计算这两个向量生成的平行四边形的有符号面积:

   signed_area = (u.x * v.y) - (u.y * v.x);

蜿蜒是该地区的标志。例如

  if (signed_area > 0)
  { 
     // order is clockwise. Pick Line B
  }
  else if (signed_area < 0)
  { 
     // order is counter-clockwise. Pick Line A
  }
  else 
  {
    // the lines are colinear.
  }

注意:我没有绑定代码,而对标志的决定可能正好相反。这是我永远不会想到的数学细节。我总是必须使用已知数据进行尝试。

【讨论】:

  • 我会在第一时间尝试您的解决方案,并告诉您它是如何工作的。谢谢!
  • 叉积会告诉你左或右,但不会直接告诉你“左更锐”或“右更浅”。您需要对输入向量进行归一化,然后将点积计算为straightness = (u.x * v.x) + (u.y * v.y);,因为叉积对于 45 度弯曲返回的值与对于 135 度弯曲返回的值相同(它反映在垂直线附近,而点产品反映在平行)。 “左或右”的叉积和转弯角度的点积。
【解决方案3】:

听起来您想将某个区域划分为不重叠的凸多边形。如果我的理解正确,您不能在使用它们形成多边形之后再折腾线段,因为每个内部线段将与两个多边形接壤。

相反,您应该为每个线段设置两个标志,用于判断您是否在线段的“左”和“右”构建了一个多边形。如果您有一个边界区域,则边界段需要将其“外”侧标志设置为开始,因为您不想在多边形中使用该侧。然后找到未设置任一标志的段,并使用 Nils 的答案围绕多边形工作。一些段将被“反转”;如果你顺时针方向,你想在“前进”段上设置“左”标志,在“后”段上设置“右”标志,反之亦然。 (您可以按一个顺序构建所有多边形,您选择哪个并不重要。)请注意,第一段可以解释为取决于您需要设置的哪个标志。当所有路段都在两个方向上标记后,您就完成了。

如果您要分割平面而不是有界区域,则某些线段将是射线;您还需要一些特殊的代码来将按斜率排序时相邻的光线连接成假的“多边形”。

有界情况的伪代码:

foreach seg in boundary segments {
    if left of seg is outside region {
         seg.leftDone = true
    } else {
         seg.rightDone = true
    }
}
while any seg.leftDone or seg.rightDone is false {
    seg = pick a segment with either flag unset
    start = seg
    polygon = new Polygon()
    reversed = not start.rightDone
    do {
        if reversed {
            seg.rightDone = true
            endpoint = seg.start
            polygon.addSegment(seg.reverse())
        } else {
            seg.leftDone = true
            endpoint = seg.end
            polygon.addSegment(seg)
        }

        next = findNextClockwiseSeg(seg, endpoint); // Nils's answer works

        seg = next
        reversed = (seg.end == endpoint)
    } while start != seg;
    result.addPolygon(polygon)
}

【讨论】:

  • 据我了解,我想在有界区域上做到这一点。但是,如果我不知道它们是哪些边界边缘标志集,我该如何标记它们。我只有一组点和说明哪些点连接形成一条线的信息。
  • 你的区域是凸的吗?如果是这样,您可以从最小 X 的任何点开始,然后沿着标记外部区域的“最顺时针”边缘。这就像标记一个内部多边形,但触及相反的标志。
  • 抱歉有点笨,但不知道它是否凸出。我想我可能会添加 x/y 为最小值/最大值的线段,以将所有边缘线也连接到封闭区域(我只是不绘制它们,而是用来计算面积)。
  • 抱歉耽搁了,我没有看到您的评论通知。在这种情况下,您可以在点上运行凸包并根据需要添加假“道路”。运行上述算法,然后丢弃边界中有任何假道路的区域。
猜你喜欢
  • 1970-01-01
  • 2011-01-02
  • 2015-05-12
  • 1970-01-01
  • 2014-11-11
  • 2013-09-07
  • 1970-01-01
  • 1970-01-01
  • 2018-11-10
相关资源
最近更新 更多