如何使两个多边形相交?
Posted
技术标签:
【中文标题】如何使两个多边形相交?【英文标题】:How to intersect two polygons? 【发布时间】:2010-12-04 07:46:43 【问题描述】:这似乎很重要(在各种论坛上被问了很多),但我绝对需要它作为更复杂算法的构建块。
输入:2D 中的 2 个多边形(A 和 B),以每个边 [(x0, y0, x1, y2), ...]
的列表形式给出。这些点由成对的double
s 表示。我不知道它们是顺时针,逆时针还是任何方向。我确实知道它们不一定是凸的。
输出:3个多边形代表A、B和相交的多边形AB。其中任何一个都可能是空 (?) 多边形,例如null
.
优化提示:这些多边形代表房间和地板的边界。所以房间边界通常会与楼层边界完全相交,除非它属于同一平面上的另一个楼层(啊!)。
我有点希望有人已经在 c# 中做到了这一点,并让我使用他们的策略/代码,因为到目前为止我在这个问题上发现的内容相当令人生畏。
编辑:所以看起来我并不完全因为害怕这样做而感到头晕目眩。我想在这里重申所需的输出,因为这是一种特殊情况,可能会使计算更简单:
输出:第一个多边形减去所有相交位,相交多边形(复数即可)。我对第二个多边形并不感兴趣,只对它与第一个多边形的交集感兴趣。
EDIT2:我目前正在使用GPC (General Polygon Clipper) 库,它让这变得非常简单!
【问题讨论】:
这比你想象的要复杂。您打算如何表示生成的形状?请记住,这两个输入可能 (a) 根本不相交,(b) 部分相交,产生凸形或凹形闭合形状,(c) 完全相交,产生具有两个边界的形状(即甜甜圈),其中你必须找到一种方法来表示形状的内部和外部。 的确,乔恩是对的。您的问题没有很好地说明;结果集可能不是多边形,因此函数的输出不应该是多边形。在生成的形状未连接的情况下,您想做什么?例如,想象一个 C 形多边形与一个 I 形多边形相交,形成一个冒号。 谢谢,将不得不认真考虑并提出解决方案。以前没想过这样... 执行您所描述的操作的大多数算法都依赖于缠绕规则以使其成为可能,因此您的第一步可能应该是将所有边缘连接成具有已知缠绕的有序点集(顺时针是最常见,但我也见过逆时针)。一旦你有一组有序的点,你可以使用点积和右手法则快速(在 O(m*n) 中)找到多边形 A 中的任何点是否在多边形 B 内。这是一个必要的先决条件确定您可以获得什么样的输出几何图形。 阅读点集理论也可能对您有用。此页面描述了 JTS 拓扑套件的实现:docs.codehaus.org/display/GEOTDOC/…。 JTS 做你想做的事,但它是用 Java 编写的。您可能想查看 GEOS(JTS 的 C++ 端口)或下面提到的 NetTopologySuite:***.com/questions/1526352/… 【参考方案1】:我认为你应该做什么
如果您能帮上忙,请不要自己尝试这样做。相反,请使用已经存在的许多可用的多边形相交算法之一。
我根据他们的演示代码的强度以及他们提到他们对大多数/所有奇怪案例的处理这一事实强烈考虑了以下代码库。如果您将其用于商业用途,则需要捐赠一定金额(由您/您的公司选择),但获得此类代码的强大版本是值得的。
http://www.cs.man.ac.uk/~toby/gpc/
我实际上所做的是使用作为 Java2D 库一部分的多边形相交算法。您可能会在 MS 自己的 C# 库中找到类似的东西来使用。
还有其他选择;寻找“多边形剪裁器”或“多边形剪裁”,因为处理多边形相交的相同基本算法也往往适用于一般剪裁情况。
一旦你真正拥有了一个多边形裁剪库,你只需要从多边形 A 中减去多边形 B 来得到你的第一个输出,然后将多边形 A 和 B 相交得到你的第二个输出。
如何自己动手,为绝望的自虐狂
当我考虑自己滚动时,我发现 Weiler-Atherton 算法在一般多边形切割方面最有潜力。我使用以下内容作为参考:
http://cs1.bradley.edu/public/jcm/weileratherton.html
http://en.wikipedia.org/wiki/Weiler-Atherton
正如他们所说,细节过于密集,无法在此处包含,但我毫不怀疑,您将能够在未来几年内找到有关 Weiler-Atherton 的参考资料。本质上,您将所有点拆分为进入最终多边形或退出最终多边形的点,然后将所有点形成一个图形,然后沿适当的方向移动图形以提取所有多边形片段想。通过改变定义和处理“进入”和“退出”多边形的方式,可以实现几种可能的多边形交集(AND、OR、XOR 等)。
它实际上是相当可实现的,但就像任何计算几何代码一样,魔鬼在退化之中。
【讨论】:
刚刚重温了这一点。我最终使用了 gpc 库(顶部链接)。太棒了。【参考方案2】:如果你敢看别人的 GPL C++ 代码,你可以看看他们在 Inkscape 中是如何做到的:
http://bazaar.launchpad.net/~inkscape.dev/inkscape/trunk/view/head:/src/2geom/shape.cpp(第 126 行)
【讨论】:
【参考方案3】:academic paper 解释了如何执行此操作。
【讨论】:
【参考方案4】:Clipper 是一个开源免费软件多边形裁剪库(用 Delphi 和 C++ 编写),它完全符合您的要求 - http://sourceforge.net/projects/polyclipping/
在我的测试中,Clipper 比 GPC 快得多,而且更不容易出错(在此处查看更详细的比较 - http://www.angusj.com/delphi/clipper.php#features)。此外,虽然有 Delphi 和 C++ 的源代码,但 Clipper 库还包含一个已编译的 DLL,以便在其他 (Windows) 语言中使用剪辑功能也非常容易。
【讨论】:
【参考方案5】:如果您在 .NET Framework 中进行编程,您可能需要查看 .NET 程序集中可用的 SqlGeometry 类,该类为 Microsoft SQL Server System CLR Types
SqlGeometry 类提供STIntersection 方法
SqlGeometry g1 = SqlGeometry.Parse("POLYGON ((...))");
SqlGeometry g2 = SqlGeometry.Parse("POLYGON ((...))");
SqlGeometry intersection = g1.STIntersection(g2);
【讨论】:
这个答案不正确,所以它被否决了吗?如果是,请指出错误在哪里。 我同意!这个解决方案有什么问题?我个人在数据库中完成我所有的空间工作......但如果你需要在代码中完成,请利用相同的代码:)【参考方案6】:尝试为此使用 GIS 工具,例如 ArcObjects、TopologySuite、GEOS、OGR 等。我不确定所有这些发行版是否都可用于 .net,但它们都一样。
【讨论】:
仅供参考,OGR(来自 GDAL/OGR)使用 GEOS (trac.osgeo.org/geos) 库,因此这两者在计算几何的实现方面没有区别。【参考方案7】:多边形由点的有序列表 (P1, P2, ..., Pn) 完全描述。边缘是 (P1 - P2), (P2 - P3), ..., (Pn - P1)。如果您有两个重叠的多边形 A 和 B,则将有一个点 An(来自描述多边形 A 的点列表)位于多边形 B 包围的区域内,反之亦然(B 的一个点位于 A 中)。如果没有找到这样的点,则多边形不会重叠。如果找到这样的点(即 Ai),请检查多边形 A(i-1) 和 A(i+1) 的相邻点。重复直到找到该区域外的一个点或检查所有点(然后第一个多边形完全位于第二个多边形内)。如果你在外面找到一个点,那么你可以计算交叉点。找到多边形 B 的相应边,然后使用重新设置的角色 = 现在检查多边形 B 的点是否位于多边形 A 内。这样您就可以构建一个描述重叠多边形的点列表。如果需要,您应该检查多边形是否相同,(P1, P2, P3) === (P2, P3, P1)。
这只是一个想法,也许还有更好的方法。如果您找到一个可行且经过测试的解决方案,我建议您使用它...
清醒
【讨论】:
【参考方案8】:Arash Partow 的FastGEO 库包含计算几何中许多有趣算法的实现。多边形相交就是其中之一。它是用 Pascal 编写的,但它只是实现数学,所以它非常易读。请注意,您肯定需要对边缘进行一些预处理,以使它们按顺时针或逆时针顺序排列。
ETA:但实际上,最好的办法就是不这样做。找到另一种不涉及任意多边形交叉点的方法来解决您的问题。
【讨论】:
如果您这样做,请格外小心,不要更改原始代码中的任何算法。如果可能,请获取 Pascal-to-C 编译器或将其编译到您可以使用的库中,而不是尝试自己翻译代码。【参考方案9】:您可能还想查看NetTopologySuite,甚至尝试将其导入 Sql server 2008 及其空间工具。
【讨论】:
为此 +1。我一直在寻找 JTS 的 .NET 端口,这看起来符合要求。以上是关于如何使两个多边形相交?的主要内容,如果未能解决你的问题,请参考以下文章