如何从一组线段创建闭合区域(凸多边形)?

时间:2010-06-18 11:37:28

标签: c++ graph geometry polygon

以下问题在2D中,因此在建议答案时可以进行一些简化。

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

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

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

逻辑上,它可以通过从一个点的左边缘开始,使用具有可用线的最短路径(仅使用顺时针方向)找回返回该点的路径。然后标记此行设置并从数据中删除。然后你可以重复相同的过程并获得所有这些区域。

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

总而言之 - 如果你可以帮助我解决这个问题的全新方法那么好,如果没有,你能不能帮我找到一种方法来编写代码的一部分,找到最短的顺时针路径回到起点使用设置为路径的线段。

编辑:添加了一张图片来说明我想要做的事情。

在这里检查图像 - (在我发布之前需要10个声明:P)

alt text

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

感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

基于您的澄清:

也许你可以试试这个:

由于Voronoi,您可能已经有任何给定 purple 蓝点的'邻近'点列表。现在给出一个紫色点P,与邻居Q,你可以考虑与线段PQ相交的道路。所有这些道路(即P的邻居之间的Q变化)很可能形成P周围的封闭区域。

即使您没有“邻居”信息,您也可以尝试所有可能的紫色蓝点对,并查看哪条线路恰好与一条道路相交。对于给定的点,这组道路将形成一个封闭的区域。

这可能不是最佳的,但可能有效,但我还没有尝试过证明。


<击> 对不起你的问题不是很清楚,但我认为Convex Hull会派上用场。您可以在计算船体时使用段的端点。

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

答案 1 :(得分:0)

您想要沿着最短路径跟随线段(例如,如果您有多个选择,请遵循最顺时针方向的线段)是好的。

你可以在没有任何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);

绕组是该地区的标志。 e.g。

  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.
  }

注意:我没有绑定代码,而对标志的决定可能完全相反。这是我永远不会想到的数学细节。我总是要用已知的数据来试试。

答案 2 :(得分:0)

听起来您想要将某些区域划分为非重叠的凸多边形。如果我正确理解你,你不能只是在使用它们形成一个多边形后抛出分段,因为每个内部分段都会在两个多边形上相邻。

相反,对于每个段,您应该有两个标志,用于是否为段的“左”和“右”构建了多边形。如果您有一个边界区域,边界线段需要将其“外侧”标记设置为开始,因为您不希望在多边形中使用该边。然后找到任何一个标志未设置的段,并使用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)
}