【发布时间】:2014-11-01 01:17:10
【问题描述】:
我正在尝试使用广为人知的 2 遍扫描线算法来实现多边形三角剖分,该算法在第一个扫描线遍中将多边形细分为单调子组件,然后在第二遍中对这些单调组件进行三角剖分。我当前的实现适用于一般情况,但我一生无法弄清楚如何调整它以处理包含多个重合边缘段的输入(从左到右扫描时具有相等 x 坐标的段,或从右到左扫过时,y 坐标相等)。
编辑: 我刚刚意识到我提出这个问题的方式让它变得非常冗长和冗长,所以这里有一个快速的 TL;DR;对于任何了解多边形三角剖分但不想阅读整个内容的人:以下形状是三角剖分算法第二遍的有效输入吗?如果是:如何调整第二遍来处理它,如果不是:我如何调整第一遍,使其产生单调的子组件,可以进入第二遍:
http://www.wouterbijlsma.nl/~wouter/tmp/RTdr6rET9.png
下面这个问题的长版本;-)
算法的快速概述:
第一遍将输入多边形细分为“单调子组件”。单调子组件是一个多边形,可以分成 2 个连接的链,其坐标从左到右排序(当使用垂直扫描线实现算法时)或从上到下排序(当使用水平扫描时)线)。假设我们使用一条垂直扫描线:然后每个单调子分量可以分成一个上链和一个下链,在最小和最大 x 坐标处连接,当扫描任一链的顶点时,x 坐标为增加。如果子组件严格单调,则上下链不能有x坐标相同的边段(即:垂直边段)。
第二遍扫描单调子组件并通过添加内部边缘将它们细分为三角形。这个想法是每个子组件从左到右扫描,并且在扫描线撞击的每个顶点处,可能发生以下两种情况之一:a)扫描线左侧的未三角区域可以通过添加对角线进行三角剖分,或 b) 当前顶点不能“看到”任何先前扫过的,但在扫掠线左侧的未三角化区域中未处理的顶点。在情况 b) 中,顶点被推入堆栈(“反射链”),并且通过构造,在某些情况下,情况 a) 将发生,并且反射链的顶点将一个接一个地弹出并与与最后一个扫描线顶点的对角线。
上面的描述缺少一些细节,但我假设任何知道如何回答我的问题的人都已经了解算法,所以我不会在这里详细说明。
我遇到的问题如下:假设我有一个多边形代表一个指向左侧的箭头,例如这样:
http://www.wouterbijlsma.nl/~wouter/tmp/RTdr6rET9.png
当我将这个形状输入到我的算法中时,单调细分通道保持形状不变:其中有垂直边缘,所以它不是严格单调的,但它是单调的,并且只要我了解该算法,它不必在您可以对其进行三角剖分之前进行细分(也许这是我出错的地方,因为我的假设是错误的)。
现在假设我将(未修改的)箭头多边形输入第二遍以对其进行三角测量。如何处理箭头底部的 2 个垂直边缘段?扫描线算法要求多边形的顶点从左到右排序,因此您会假设答案将归结为如何对具有相同 x 坐标的顶点进行排序(例如,按链顺序或 y 坐标,或在多边形边界索引上),但无论我使用什么排序,三角剖分总是会失败。
我们将最左边的顶点称为顶点 0,并按逆时针顺序排列顶点。这意味着箭头底部的 4 个顶点是顶点 1、2、5 和 6。我们有三个排序选项:
我用来实现算法的一些源材料说“在增加 y 坐标时对具有相等 x 坐标的顶点进行排序”,即:1、2、5、6。如果我这样做并扫描它们,第一个三角形出来没问题 (0, 1, 2),但之后算法会添加一条边 (5, 2),这会创建一个 4 顶点分量 (0, 2, 5, 6)。没有添加边 (0, 5),因为三角剖分算法规定将边添加到反射链上所有先前未三角剖分的顶点除了第一个(更改这将打破一般情况)。虽然由 4 个顶点界定的多边形区域是三角形的,但它显然不是三角形,因为它有 4 个点,而且根据大多数定义,它也不是有效的多边形,因为它有共线的边。
-
我读到的另一篇论文说“打破联系,以保持链顺序”。这意味着我的示例中的 4 个顶点将按 1、2、6、5 排序,因为下链和上链都是从左到右运行的。如果我按这个顺序扫描它们,我会再次得到一个三角形 (0, 1, 2),但是下一个扫描的顶点 (6) 将创建一个多边形 (0, 1, 6),这比第一种情况更糟糕,因为它创建了一条边 (1, 6),它在顶点 5 上运行但不包含它。扫描顶点 5 会完全弄乱算法的状态,因为它会创建一个退化三角形 (1, 5, 6),一条线,并且扫描尾部顶点将无法对多边形的其余部分进行三角剖分
李> 按原始多边形顶点顺序(沿边界,逆时针)排序:在这种情况下,这将产生与情况 1) 相同的结果,即:1、2、5、6,这已经显示为失败。
我开始认为,也许像这样的箭头形状应该被认为是非单调的,或者(即使我从未在算法的任何描述中看到过这一点)单调三角测量算法要求输入为 严格单调。这可能是我想念的东西吗?如果是的话,我需要如何调整单调细分通道来处理(多个,重合的)垂直边缘段?我使用的源材料在细分期间将所有顶点分类为“开始”、“结束”、“合并”、“拆分”或“常规”(下/上),但在垂直段的情况下,这些分类是不明确的:根据对于这些顶点类的定义,垂直线段的顶点部分可以被认为是开始/结束顶点,也可以被认为是分割或合并顶点。或者也许我确实必须在它们的 y 坐标上对 4 个顶点进行排序,然后创建一个具有 2 个共线边的无效 4 顶点三角形组件,然后通过删除共线边?
我用来实现该算法的主要来源是介绍三角剖分算法的原始 GJPT-78 论文,它不是公开的(付费专区),所以我无法在此处链接,但有很多 CS 课程资料可用在线描述算法的,我也使用了这些例如:
http://www.cs.ucsb.edu/~suri/cs235/Triangulation.pdf https://www.cs.umd.edu/class/spring2012/cmsc754/Lects/cmsc754-lects.pdf(参见“第 6 讲”章节)
我已经阅读了很多这些。几乎每一组幻灯片、论文、博客或算法的任何其他描述具体都提到了具有相等 x 坐标(或 y 坐标,如果使用水平扫描线)的顶点,但它们所有人 只是说“我们假设没有相等的 x 坐标”,并且这个限制“很容易固定,仅用于简化表示”或“不是算法的基础”或其他任何内容。奇怪的是,没有他们关心详细说明支持这种情况所需的更改或解决方法,或者它们包含一些关于以某种方式对相等的 x 顶点进行排序的模糊陈述,但实际上并不能解决问题。
也许我只是有点愚蠢或遗漏了一些非常明显的东西,但我已经搞砸了好几天试图修复这个角落案例但没有结果,它开始变得非常令人沮丧。我假设为基本案例实现算法(包括编写 DCEL 数据结构、扫描线算法、排序边缘图、确定内角和可见性所需的三角函数、有效存储和查找顶点分类的数据结构等) 几乎是所有工作,之后修复垂直边缘问题将是微不足道的。现在我花了更多时间尝试修复垂直边缘,而不是我需要让算法适用于一般情况的所有其他东西(只要它不适合我扔给它的任何多边形,它都可以完美工作' t 有垂直边缘)。
谢谢!沃特
【问题讨论】:
-
经过轻微剪切变换的箭头是否正确(即 (x, y) |-> (x + epsilon y, y))?
-
我问是因为处理退化 w.r.t 的常用技巧之一。扫描线是进行无限小的符号旋转,这相当于“按 y 排序”,如果 y 几乎平行的扫描线与点 (x, y) 相交,那么“左边”的点是那些按字典顺序小于 (x, y) 的点,并且“右边”的点更大。但是,如果剪切箭头导致退化的三角剖分,那么共线段就会出现更根本的问题。
-
@David Eisenstat:对具有相同 x 坐标的每个点应用轻微的剪切变换确实会导致“有效”三角剖分并防止算法进入无效状态。实际上,我至少曾经将其视为一种解决方案。我看到的问题是,这会添加额外的面积非常小的三角形。在“箭头”的情况下,这将是一个三角形 (2, 5, 6)。当然可以从结果中过滤掉这样的三角形,但我不确定是否有更好的解决方案可以直接产生适当的三角剖分。
-
受约束的 Delaunay 三角剖分是一种可能,但也许比我更了解计算几何的人可能会提出一种替代方案,以重用您现有的更多代码库(平面图算法是我的专长)。
标签: algorithm graphics polygon computational-geometry triangulation