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

发布于 2024-09-06 03:44:35 字数 869 浏览 6 评论 0原文

以下问题是二维的,因此在建议答案时可以进行一些简化。

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

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

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

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

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

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

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

检查此处的图像 - (需要 10 个信誉才能将其发布到此处:P)

alt text

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

感谢您的帮助!

The following problem is in 2D, so some simplifications can be made when suggesting answers.

I need to create closed areas (defined either by line segments or just set of points - convex polygon) from a set of points/line segments.

Basically I used Voronoi to generate "roads". Then I changed some of the data. Now I need a way to loop through that data (which is still line segments but doesn't comply with Voronoi anymore) and generate "neigbourhoods" that are bordered with the "roads".

I looked at some graph diagrams and shortest path theories, but I could not figure it out.

Logically it could be done by starting at left edge from one point, finding the way back to that point using the shortest path with available lines (using only clockwise directions). Then mark this line set down and remove from the data. Then you can repeat the same process and get all the areas like that.

I tried to implement that but it did not get me anywhere as I could not figure out a way to write a C++ code that could do that. Problem was with choosing the most counterclockwise line from available lines from a specific point. All angle based math I did gave wrong answers because the way sin/cos are implemented in c++.

So to summarize - if you can help me with a totally new approach to the problem its good, if not could you help me find a way to write the part of the code that finds the shortest clockwise path back to the beginning point using the line segment set as paths back.

EDIT: Added a picture to illustrate what I want to do.

Check the image here - (need 10 reputations before I can post it here :P)

alt text

I have a set of points (purple small dots). Another array defines what points make up a line (road). I want a way to define the area that is surrounded by roads so I can put buildings or smaller roads inside that and test against the edges so every region is separated. Hope this gives you more info on how to solve this problem.

Thank you for your help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

自在安然 2024-09-13 03:44:35

根据您的澄清:

也许您可以尝试以下操作:

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

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

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


<罢工>
抱歉,您的问题不太清楚,但我认为 凸包 会派上用场。您可以在计算外壳时使用线段的端点。

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

Basded on your clarification:

Perhaps you can try this:

You probably already have a list of 'neighbouring' points for any given purple blue point because of the Voronoi. Now given a purple point P, with a neighbour Q, you can consider the road which intersects line segment PQ. All such roads (i.e. vary Q among the neighbours of P) will likely form the closed region around P.

Even if you didn't have the 'neighbour' information, you can probably try all possible pairs of purple blue points and see which line segments are intersected by exactly one road. For a given point, the set of such roads will form a closed region around it.

This might not be optimal, but probably works, though I haven't tried proving it.



Sorry your question is not really clear, but I presume Convex Hull will come in handy. You can use the endpoints of a segment when calculating the hull.

If you want mutiple disjoint 'areas' you could try finding a separating line and running convex hull separately.

安静被遗忘 2024-09-13 03:44:35

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

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

这个想法是这样的:

假设你有两条线可供选择。将线与 A 相交的点称为 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.
  }

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

Your idea to follow the line segments along the shortest path (e.g. follow the most clockwise one if you have multiple choices) is good.

And you can find this line without any sin/cos calls.

The idea goes like this:

Assume that you have two lines to choose from. Call the point where the lines meet A (e.g. your current position). The endpoints of your two lines are called B and C.

These three points build a triangle. Now take a look how the three points are oriented. If you follow the points from A to B to C the direction will either be clockwise or counter-clockwise. Obviously if the order is clockwise, the line that goes from A to C will be the most clockwise one. Otherwise it's the line that goes from A to B.

If you have more than two lines to choose from just pick the first two, discard the line that goes into the wrong direction and do the same test until you end up with a single line. That's the most clockwise one.

Now about the math: how do you find out the winding-order of three points without calling sin/cos or even worse: atan:

You can do this with the cross product. First build two vectors that give you the direction from A to B and from A to 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

Now we can calculate the signed area of the parallelogram spawned by these two vectors:

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

The winding is the sign of the area. 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.
  }

Note: I haven't tied the code and the decision on the sign could be exactly the other way around. That's a detail of the math that I will never get into my head. I always have to try it out with known data.

风向决定发型 2024-09-13 03:44:35

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

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

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

有界情况的伪代码:

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

It sounds like you want to partition the some region into non-overlapping convex polygons. If I'm understanding you correctly, you can't just toss segments after using them to form a polygon, because each internal segment will border on two polygons.

Instead, you should have two flags for each segment, for whether you have built a polygon to the segment's "left" and "right". If you have a bordered area, the border segments need to have their "outer" side flag set to start with since you don't want to use that side in a polygon. Then find segments with either flag unset, and use Nils's answer to work your way around the polygon. Some segments will be "reversed"; if you're going clockwise, you want to set the 'left' flag on the 'forward' segments and the 'right' flag on the 'backward' ones, and vice versa. (You can build all the polygons in one order, it just doesn't matter which you choose.) Note that the first segment can be interpreted as either depending on which of its flags you need to set. When all segments are flagged in both directions, you are done.

If you're partitioning the plane rather than a bounded region, some segments will be rays; you'll also need some special code to connect rays that are adjacent when sorted by slope into fake "polygons".

Pseudocode for the bounded case:

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)
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文