ChatGPT解决这个技术问题 Extra ChatGPT

一种用于膨胀/放气(偏移、缓冲)多边形的算法

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

https://i.stack.imgur.com/GUzIo.gif

要求是新的(膨胀的)多边形的边缘/点与旧的(原始)多边形的距离相同(在示例图片上它们不是,因为那时它必须使用弧来膨胀顶点,但是让我们暂时忘记这一点;))。

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

我的搜索结果:

以下是一些链接:

多边形偏移策略综述

多边形偏移,问题

缓冲多边形数据

这根本不是一个微不足道的问题:如果通货紧缩/通货膨胀很小,不会发生任何严重的事情,但在某些时候,顶点会消失。可能以前已经做过,所以我想说:使用别人的算法,不要构建自己的算法。
事实上,如果你的多边形一开始是凹的(如上面的例子),你必须决定在天真的算法想要制作一个自相交的“多边形”时应该发生什么......
是的,主要问题是多边形的凹入部分,这就是复杂性所在。我仍然认为计算何时必须消除某个顶点不应该是一个问题。主要问题是这需要什么样的渐近复杂性。
您好,这也是我的问题,除了我需要在 3D 中执行此操作。是否有替代论文 arxiv.org/pdf/0805.0022.pdf 中描述的三维多面体直骨架方法的替代方法?
它们的另一个名称是平行曲线,用于偏移轮廓/轮廓:en.wikipedia.org/wiki/Parallel_curve

B
Bernhard Barker

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

虽然 Clipper 主要是为多边形裁剪操作而设计的,但它也可以进行多边形偏移。该库是用 Delphi、C++ 和 C# 编写的开源免费软件。它有一个非常不受限制的 Boost 许可证,允许它在免费软件和商业应用程序中免费使用。

可以使用三种偏移样式之一执行多边形偏移 - 方形、圆形和斜接。

https://i.stack.imgur.com/LqZeh.png


很酷! 2年前你在哪里? :) 最后我不得不实现自己的偏移逻辑(并且为此浪费了很多时间)。顺便说一句,您使用什么算法进行多边形偏移?我用的是草火。你处理多边形中的孔吗?
年前,我正在寻找一个体面的多边形剪裁解决方案,该解决方案不受棘手的许可证问题的困扰:)。边缘偏移是通过为所有边缘生成单位法线来实现的。边缘连接由我的多边形剪裁器整理,因为这些重叠交叉点的方向与多边形的方向相反。孔肯定会像自相交多边形等一样被处理。它们的类型或数量没有限制。另请参阅此处的“通过计算绕组数进行多边形偏移”:me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf
哇!不要以为这个问题是“被遗忘的”!我上周看过这里——我没想到会回到这里!非常感谢!
对于任何想要这样做的人,另一种选择是使用 GEOS,如果您使用 python,则可以使用 GEOS 的包装器 Shapely。一个非常漂亮的例子:toblerity.github.com/shapely/manual.html#object.buffer
...事实证明,在为这个问题苦苦挣扎了大约一年之后,我终于找到了谷歌搜索并找到了这个答案。我已经在使用 Clipper 库,但不知道它内置了这个功能!
B
Bernhard Barker

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

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

https://i.stack.imgur.com/vCgNf.png

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

https://i.stack.imgur.com/lETWh.png

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

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

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

CGAL manual: 2D Straight Skeleton and Polygon Offsetting


S
StayOnTarget

对于这些类型的东西,我通常使用 JTS。出于演示目的,我创建了这个使用 JSTS(JTS 的 JavaScript 端口)的 jsFiddle。您只需要将您拥有的坐标转换为 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;
}

结果是这样的:

https://i.stack.imgur.com/C7Wf8.png

附加信息:我通常使用这种类型的充气/放气(为了我的目的稍作修改)来设置地图上绘制的多边形的半径边界(使用传单或谷歌地图)。您只需将 (lat,lng) 对转换为 JSTS 坐标,其他一切都相同。例子:

https://i.stack.imgur.com/ryZNC.png


S
Sebastian.Sanchez

在我看来,你想要的是:

从一个顶点开始,沿相邻边逆时针面对。

将边缘替换为新的平行边缘,该边缘位于旧边缘“左侧”距离 d 处。

对所有边缘重复。

找到新边的交点以获得新的顶点。

检测您是否已成为交叉多边形并决定如何处理它。可能在交叉点添加一个新顶点并删除一些旧顶点。我不确定是否有更好的方法来检测这一点,而不仅仅是比较每对非相邻边以查看它们的交点是否位于两对顶点之间。

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

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


b
bgw

在 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


J
J-16 SDiZ

每条线应将平面拆分为“内部”和“轮廓”;您可以使用通常的内积方法找到这一点。

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

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

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

(a) 案例一:

 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,您保留一堆顶点。当您发现与后一行重叠的行时推送,当您获得后一行时弹出它。 - 就像你在凸包中所做的一样。


你知道这方面的任何psedo算法吗?
J
J-16 SDiZ

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

进行三角测量,不必是 delaunay - 任何三角测量都可以。膨胀每个三角形——这应该是微不足道的。如果您按逆时针顺序存储三角形,只需将线移动到右侧并进行交叉。使用修改后的 Weiler-Atherton 裁剪算法合并它们


你如何准确地给三角形充气?您的输出是否取决于三角测量?使用这种方法,您可以处理缩小多边形的情况吗?
你确定这种方法真的适用于多边形膨胀吗?当多边形的凹入部分膨胀到必须消除某些顶点的程度时会发生什么。问题是:当您查看多边形之后的三角形会发生什么时。通货膨胀,三角形没有膨胀,而是扭曲了。
Igor:Weiler-Atherton 裁剪算法可以正确处理“一些顶点必须被消除”的情况;
@balint:膨胀三角形是微不足道的:如果您以正常顺序存储顶点,则右侧总是“向外”。只需将这些线段视为线,将它们向外移动,然后找到相互作用——它们是新的顶点。对于三角剖分本身,再想一想,德劳内三角剖分可能会给出更好的结果。
我认为这种方法很容易产生不好的结果。即使对于使用对角线进行四边三角化的简单示例也是如此。对于你得到的两个放大的三角形:img200.imageshack.us/img200/2640/counterm.png 和它们的并集不是你想要的。我看不出这种方法有什么用。
P
PainElemental

非常感谢 Angus Johnson 的 Clipper 库。在 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;
    }

P
Paul R

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

poly += 2; // buffer polygon by 2

我不明白你应该如何用 boost::polygon 做任何事情,因为它只支持整数坐标?假设我有一个通用(浮点坐标)多边形,我想扩展它——我会怎么做?
@DavidDoria:这取决于坐标所需的分辨率/精度和动态范围,但您可以使用具有适当比例因子的 32 位或 64 位整数。顺便说一句,我过去(不小心)使用了带有浮点坐标的 boost::polygon,它似乎工作正常,但它可能不是 100% 健壮的(文档警告不要这样做!)。
我会同意“它在大多数情况下都可以工作”:)。我试过这个:ideone.com/XbZeBf 但它没有编译 - 有什么想法吗?
我没有看到任何明显的错误,但就我而言,我使用的是直线专业化(polygon_90),所以我不知道这是否会有所作为。自从我玩这个已经有几年了。
好的 - 它现在回到我的身边 - 您只能将 += 与多边形 set 一起使用,而不能与单个多边形一起使用。尝试使用多边形的 std::vector 。 (当然向量只需要包含一个多边形)。
C
Carl Witthoft

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


s
stephanmg

有几个可以使用的库(也可用于 3D 数据集)。

https://github.com/otherlab/openmesh https://github.com/alecjacobson/nested_cages http://homepage.tudelft.nl/h05k3/Projects/MeshThickeningProj.htm

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

最后一个具有最少的依赖关系,并且是独立的,可以读取 .obj 文件。

最好的祝愿,斯蒂芬


这适用于内部偏移多边形吗? (负距离??)
@RudyVanDrie 我会说,是的,但试一试。
u
user2800464

我使用简单的几何:向量和/或三角

在每个角找到中间向量和中间角。中间向量是由角的边缘定义的两个单位向量的算术平均值。中角是边缘定义的角度的一半。如果您需要将多边形扩展(或收缩)每条边的 d 量;您应该以 d/sin(midAngle) 的量向外 (in) 获得新的角点。对所有角落重复此操作

*** 小心你的方向。使用定义角的三个点进行 CounterClockWise 测试;找出出或入的路。


E
Emad

这是 here 中解释的算法的 C# 实现。它也使用 Unity 数学库和集合包。

public static NativeArray<float2> ExpandPoly(NativeArray<float2> oldPoints, float offset, int outer_ccw = 1)
{
    int num_points = oldPoints.Length;
    NativeArray<float2> newPoints = new NativeArray<float2>(num_points, Allocator.Temp);

    for (int curr = 0; curr < num_points; curr++)
    {
        int prev = (curr + num_points - 1) % num_points;
        int next = (curr + 1) % num_points;

        float2 vn = oldPoints[next] - oldPoints[curr];
        float2 vnn = math.normalize(vn);
        float nnnX = vnn.y;
        float nnnY = -vnn.x;

        float2 vp = oldPoints[curr] - oldPoints[prev];
        float2 vpn = math.normalize(vp);
        float npnX = vpn.y * outer_ccw;
        float npnY = -vpn.x * outer_ccw;

        float bisX = (nnnX + npnX) * outer_ccw;
        float bisY = (nnnY + npnY) * outer_ccw;

        float2 bisn = math.normalize(new float2(bisX, bisY));
        float bislen = offset / math.sqrt(1 + nnnX * npnX + nnnY * npnY);

        newPoints[curr] = new float2(oldPoints[curr].x + bislen * bisn.x, oldPoints[curr].y + bislen * bisn.y);
    }

    return newPoints;
}

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅