用于膨胀/收缩(抵消,缓冲)多边形的算法

时间:2009-07-10 13:32:47

标签: algorithm geometry polygon computational-geometry buffering

我如何“膨胀”多边形?也就是说,我想做类似的事情:

alt text

要求是新的(膨胀的)多边形的边/点与旧的(原始)多边形处于相同的恒定距离(在示例图片上它们不是,因为那时它必须使用弧来填充顶点,但是现在让我们忘掉它;))。

我正在寻找的数学术语实际上是向内/向外多边形偏离。 +1指向balint指出这一点。替代命名是多边形缓冲

我的搜索结果:

以下是一些链接:

12 个答案:

答案 0 :(得分:128)

我想我可能会简单提一下我自己的多边形裁剪和偏移库 - Clipper

虽然Clipper主要是为多边形裁剪操作而设计的,但它也会进行多边形偏移。该库是用 Delphi,C ++和C#编写的开源免费软件。它具有非常无阻碍的Boost许可证,允许它免费用于免费软件和商业应用程序。

可以使用三种偏移样式中的一种执行多边形偏移 - 平方,圆形和斜接。

Polygon offsetting styles

答案 1 :(得分:38)

您正在寻找的多边形在计算几何中称为向内/向外偏移多边形,它与straight skeleton密切相关。

这是复杂多边形的几个偏移多边形:

这是另一个多边形的直骨架:

正如其他评论中所指出的那样,根据您计划“膨胀/收缩”多边形的程度,您最终可能会得到不同的输出连接。

从计算的角度来看:一旦你有了直骨架,就应该能够相对容易地构造偏移多边形。开源和(免费用于非商业)CGAL库有一个实现这些结构的包。请参阅this code example以使用CGAL计算偏移多边形。

即使您不打算使用CGAL,package manual也应该为您提供如何构建这些结构的良好起点,并且包含对具有数学定义和属性的论文的引用:

CGAL manual: 2D Straight Skeleton and Polygon Offsetting

答案 2 :(得分:8)

听起来像你想要的是:

  • 从顶点开始,沿相邻边缘逆时针方向。
  • 使用距离d距离旧“平行边缘”的新平行边缘替换边缘。
  • 重复所有边缘。
  • 找到新边的交点以获取新顶点。
  • 检测您是否已成为交叉多项式并决定如何处理它。可能在交叉点添加一个新的顶点并摆脱一些旧的顶点。我不确定是否有更好的方法来检测这个,而不仅仅是比较每对非相邻边缘,看看它们的交点是否位于两对顶点之间。

生成的多边形位于距离顶点“足够远”的旧多边形所需的距离处。在顶点附近,与旧多边形相距距离d的点集合,正如您所说,不是多边形,因此无法满足所述要求。

我不知道这个算法是否有名称,网络上的示例代码或恶魔优化,但我认为它描述了你想要的内容。

答案 3 :(得分:8)

对于这些类型的东西,我通常使用JTS。出于演示目的,我创建了使用jsFiddle(JTS的JavaScript端口)的JSTS。您只需将您拥有的坐标转换为JSTS坐标:

function vectorCoordinates2JTS (polygon) {
  var coordinates = [];
  for (var i = 0; i < polygon.length; i++) {
    coordinates.push(new jsts.geom.Coordinate(polygon[i].x, polygon[i].y));
  }
  return coordinates;
}

结果是这样的:

enter image description here

其他信息:我通常使用这种类型的充气/放气(为我的目的稍作修改)来设置在地图上绘制的多边形上的半径边界(使用Leaflet或Google地图) 。您只需将(lat,lng)对转换为JSTS坐标,其他所有内容都相同。例如:

enter image description here

答案 4 :(得分:5)

每条线应将平面分成“内部”和“轮廓”;你可以用通常的内在产品方法找到它。

将所有线向外移动一段距离。

考虑所有相邻线(线,而不是线段),找到交点。这些是新的顶点。

通过删除任何相交的部分来清理新的顶点。 - 我们这里有几个案例

(a)案例1:

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

如果你把它花费一个,你得到了这个:

0----a----3
|    |    |
|    |    |
|    b    |
|         |
|         |
1---------2

7和4重叠..如果你看到这个,你删除这一点和它们之间的所有点。

(b)案例2

 0--7  4--3
 |  |  |  |
 |  6--5  |
 |        |
 1--------2

如果你把它花费两倍,你得到了这个:

0----47----3
|    ||    |
|    ||    |
|    ||    |
|    56    |
|          |
|          |
|          |
1----------2

要解决此问题,对于每个线段,您必须检查它是否与后面的线段重叠。

(c)案例3

       4--3
 0--X9 |  |
 |  78 |  |
 |  6--5  |
 |        |
 1--------2

花费1.这是案例1的更一般情况。

(d)案例4

与case3相同,但是两次消耗。

实际上,如果你可以处理案例4.所有其他情况只是它的特殊情况,有一些线或顶点重叠。

要做案例4,你要保留一堆顶点..当你找到与后一行重叠的行时你会推动,当你得到后一行时弹出它。 - 就像你在凸壳中做的那样。

答案 5 :(得分:5)

这是另一种解决方案,看看你是否更喜欢这个。

  1. 做一个triangulation,它不必是delaunay - 任何三角测量都可以。

  2. 给每个三角形充气 - 这应该是微不足道的。如果以逆时针顺序存储三角形,只需将线条移动到右侧并进行交叉。

  3. 使用修改后的Weiler-Atherton clipping algorithm

  4. 合并它们

答案 6 :(得分:5)

在GIS世界中,人们使用负缓冲来完成这项任务: http://www-users.cs.umn.edu/~npramod/enc_pdf.pdf

JTS library应该为你做这件事。请参阅缓冲区操作的文档:http://tsusiatsoftware.net/jts/javadoc/com/vividsolutions/jts/operation/buffer/package-summary.html

有关概述,请参阅开发人员指南: http://www.vividsolutions.com/jts/bin/JTS%20Developer%20Guide.pdf

答案 7 :(得分:3)

非常感谢Angus Johnson的剪辑库。 在http://www.angusj.com/delphi/clipper.php#code的限幅器主页上有很好的代码示例用于剪辑内容 但我没有看到多边形偏移的例子。所以我想如果我发布我的代码可能对某人有用:

    public static List<Point> GetOffsetPolygon(List<Point> originalPath, double offset)
    {
        List<Point> resultOffsetPath = new List<Point>();

        List<ClipperLib.IntPoint> polygon = new List<ClipperLib.IntPoint>();
        foreach (var point in originalPath)
        {
            polygon.Add(new ClipperLib.IntPoint(point.X, point.Y));
        }

        ClipperLib.ClipperOffset co = new ClipperLib.ClipperOffset();
        co.AddPath(polygon, ClipperLib.JoinType.jtRound, ClipperLib.EndType.etClosedPolygon);

        List<List<ClipperLib.IntPoint>> solution = new List<List<ClipperLib.IntPoint>>();
        co.Execute(ref solution, offset);

        foreach (var offsetPath in solution)
        {
            foreach (var offsetPathPoint in offsetPath)
            {
                resultOffsetPath.Add(new Point(Convert.ToInt32(offsetPathPoint.X), Convert.ToInt32(offsetPathPoint.Y)));
            }
        }

        return resultOffsetPath;
    }

答案 8 :(得分:1)

根据来自@ JoshO'Brian的建议,rGeos语言中的R包似乎实现了此算法。请参阅rGeos::gBuffer

答案 9 :(得分:1)

另一个选项是使用boost::polygon - 文档有点缺乏,但您应该找到方法resizebloat,以及重载的+=运算符,实际上实现缓冲。因此,例如,将多边形(或一组多边形)的大小增加一些值可以简单如下:

poly += 2; // buffer polygon by 2

答案 10 :(得分:0)

有两个库可供使用(也可用于3D数据集)。

  1. setTimeout
  2. https://github.com/otherlab/openmesh
  3. https://github.com/alecjacobson/nested_cages

还可以找到这些库的相应出版物,以更详细地了解算法。

最后一个具有最小的依赖性,并且是自包含的,可以读取.obj文件。

最良好的祝愿, 斯蒂芬

答案 11 :(得分:0)

我使用简单的几何体:矢量和/或三角学

  1. 在每个角处找到中间向量和中间角度。中间向量是由拐角边缘定义的两个单位向量的算术平均值。中间角度是边缘定义的角度的一半。

  2. 如果需要按每个边的d量来扩展(或收缩)多边形;您应该以d / sin(midAngle)的量进入(进入)以获得新的拐角点。

  3. 在所有角落重复此操作

***请注意您的方向。使用定义拐角的三个点进行CounterClockWise测试;找出出路或进路。