如何相交两个多边形?

32

这似乎不是一个简单的问题(在各种论坛上都被问得很多),但我绝对需要它作为更复杂算法的构建模块。

输入:两个二维多边形(A和B),分别表示为由每条边[(x0, y0, x1, y2), ...]组成的列表。点由double对表示。我不知道它们是顺时针给出,还是逆时针给出,或者根本没有方向。我知道它们不一定是凸多边形。

输出:表示A、B和相交多边形AB的3个多边形。其中任何一个都可能是空(?)多边形,例如null

优化提示:这些多边形表示房间和楼层边界。因此,房间边界通常会完全与地板边界相交,除非它属于同一平面上的另一个楼层(啊!)。

我希望有人已经用C#完成了这个任务,并允许我使用他们的策略/代码,因为我到目前为止在这个问题上找到的东西相当令人望而生畏。

编辑:看来我并不是完全懦弱不敢面对这个问题。我想在这里重新说明所需的输出,因为这是一个特殊情况,可能会简化计算:

输出:第一个多边形减去所有相交的部分,交集多边形(复数也可以)。我实际上并不关心第二个多边形,只关心它与第一个多边形的交集。

编辑2:我目前正在使用GPC(通用多边形剪辑器)库,这使得它非常容易!


2
这比你想象的要复杂。你打算如何表示结果形状?请记住,两个输入可能会(a)完全不相交,(b)部分相交,导致凸或凹的闭合形状,(c)完全相交,导致具有两个边界的形状(即甜甜圈),您必须找到一种方法来表示形状的内部和外部。 - Jon Seigel
3
确实,Jon是正确的。你的问题陈述不清楚;生成的集合可能不是一个多边形,因此函数的输出不应该是一个多边形。如果结果形状不连通,你想要做什么?例如,在一个C形多边形与一个I形多边形相交的情况下,会产生一个冒号形。 - Eric Lippert
1
大多数实现你所描述的算法都依赖于绕组规则,因此你的第一步应该是将所有边连接成一个有序点集,并知道其绕组方向(顺时针最常见,但我也见过逆时针)。一旦你有了一个有序点集,你可以使用点积和右手定则快速地(在O(m*n)时间内)找出多边形A中的任何一个点是否在多边形B内。这是确定输出几何图形类型的必要前提。 - Daniel Pryden
1
阅读点集理论可能对您有所帮助。此页面描述了JTS拓扑套件的实现:http://docs.codehaus.org/display/GEOTDOC/Point+Set+Theory+and+the+DE-9IM+Matrix 。JTS可以满足您的需求,但是它是用Java编写的。您可能想查看GEOS(JTS的C++移植版)或下面提到的NetTopologySuite:https://dev59.com/questions/gnI_5IYBdhLWcg3wF_F3#1527105。 - Daniel Pryden
这篇学术论文解释了如何对凸多边形和非凸多边形进行布尔运算:http://www.gvu.gatech.edu/~jarek/graphics/papers/04PolygonBooleansMargalit.pdf - Charles Bretana
显示剩余3条评论
9个回答

17
Arash Partow的FastGEO库包含了许多有趣的计算几何算法实现,其中之一是多边形相交。它是用Pascal编写的,但它只是实现数学,因此非常易读。请注意,您肯定需要对边缘进行一些预处理,以使它们按顺时针或逆时针顺序排列。
预计完成时间:但是,真正解决问题的最佳方法是不要这样做。找到另一种方法来解决涉及任意多边形相交的问题。

7
如果你要这样做,请非常小心,不要改变原始代码中的任何算法。如果可能的话,获取一个Pascal-to-C编译器或者将其编译成一个库来使用,而不是试图自己翻译代码。 - jprete

14
如果你正在使用.NET Framework编程,你可能需要查看.NET程序集中提供的Microsoft SQL Server System CLR Types,其中包含了SqlGeometry类。
SqlGeometry类提供了STIntersection方法。
SqlGeometry g1 = SqlGeometry.Parse("POLYGON ((...))");
SqlGeometry g2 = SqlGeometry.Parse("POLYGON ((...))");
SqlGeometry intersection = g1.STIntersection(g2);

3
这个答案是否因为错误而遭到了负评?如果是的话,请指出哪里有错误。 - mloskot
1
我同意! 这个解决方案有什么问题吗?我个人会把所有的空间处理都放在数据库中...但是如果你需要在代码中完成它,可以利用相同的代码 :) - Pure.Krome

12

我认为你应该做什么

如果可能的话,不要自己尝试编写多边形相交算法,而是使用已经存在的许多可用的多边形相交算法之一。

我强烈考虑以下代码库,因为它们的演示代码和他们提到的大多数/所有怪异案例的处理能力。如果您在商业上使用它,则需要捐赠一定金额(由您/您的公司选择),但是这值得为了获得此类代码的稳健版本。

http://www.cs.man.ac.uk/~toby/gpc/

我实际上所做的是使用Java2D库中的多边形相交算法。您可以在微软自己的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库(顶部链接)。它非常棒。 - Daren Thomas

3
Clipper是一个开源免费的多边形剪裁库(使用Delphi和C++编写),正好可以满足您的需求 - http://sourceforge.net/projects/polyclipping/
在我的测试中,Clipper比GPC快得多,而且出错的可能性也要小得多(更详细的比较请参见此处 - http://www.angusj.com/delphi/clipper.php#features)。此外,虽然有Delphi和C++的源代码,但Clipper库还包括一个已编译的DLL,使得在其他(Windows)语言中使用剪裁函数非常容易。

3

您可能还想看看 NetTopologySuite,甚至可以尝试将其导入到Sql server 2008及其空间工具中。


我正在寻找一个 JTS 的 .NET 版本,这看起来很符合要求。 - Daniel Pryden

2

尝试使用GIS工具,例如ArcObjects、TopologySuite、GEOS、OGR等。我不确定这些发行版是否都可用于.NET,但它们都可以实现相同的功能。


1
FYI,OGR(来自GDAL/OGR)使用GEOS(http://trac.osgeo.org/geos/)库,因此在这两者之间的计算几何实现方面没有区别。 - mloskot

2

2
一个多边形由有序点列表(P1,P2,...,Pn)完全描述。 边缘是(P1-P2),(P2-P3),...,(Pn-P1)。 如果您有两个重叠的多边形A和B,则会有一个点An(来自描述多边形A的点列表)位于由多边形B或反之亦然所包围的区域内。 如果找不到这样的点,则多边形不重叠。 如果找到这样的点(即Ai),则检查多边形A(i-1)和A(i+1)的相邻点。 重复此过程,直到找到一个在区域外部的点或检查所有点(然后第一个多边形完全位于第二个多边形内)。 如果找到了一个在区域外部的点,则可以计算交点。 找到多边形B的相应边并使用反向角色跟随它 = 现在检查多边形B的点是否位于多边形A内。 这样,您可以构建描述重叠多边形的点列表。 如有必要,应检查多边形是否相同,(P1,P2,P3)===(P2,P3,P1)。
这只是一个想法,可能还有更好的方法。 如果您找到了可行且经过测试的解决方案,我建议您使用它...
narozed

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接