我出于好奇而问这个问题,出于性能原因,在使用GLU之前首先尝试实施这样的算法。
我研究了常见的算法(经常提到Delaunay,Ear Clipping),但我似乎无法理解GLU如何一直如此好地工作。
你们有没有关于这个问题的有趣论文或文章?
答案 0 :(得分:14)
这只是一个非常简短的概述。有很多 源代码本身的其他文档。
稳健的细分目标
细分算法基本上是2D算法。我们 最初将所有数据投影到一个平面上;我们的目标是坚定不移 详细说明预计的数据。同样的拓扑结构是 然后应用于输入数据。
拓扑上,输出应始终是一个细分。如果 输入甚至是略微非平面的,然后是一些三角形 从某些角度来看,必然是背对面的,但目标 是为了尽量减少这种影响。
该算法需要一些清理输入数据的能力 以及自己计算中的数值误差。一种方法 这是指定上面定义的公差,并清理 线扫描过程中的输入和输出。至少, 算法必须处理重合顶点,顶点入射到一个顶点 边缘和重合边缘。
算法的阶段
- 找到多边形法线N。
- 将顶点数据投影到平面上。它不需要垂直于法线,例如。我们可以投射到飞机上 垂直于坐标轴,其点积为N 最大。
- 使用线扫描算法,将平面划分为x单调区域。任何垂直线与at中的x-单调区域相交 最多一个间隔。
- 对x单调区域进行三角测量。
- 将三角形分为条状和扇形。
醇>查找法线向量
查找多边形法线的常用方法是计算有符号区域 当多边形沿三个坐标轴投影时。我们 不能做到这一点,因为轮廓可以没有区域 退化(例如领结)。
我们将平面拟合到顶点数据,忽略它们的连接方式 进入轮廓。理想情况下,这将是最小二乘拟合;但是对于 我们的目的是正常的准确性并不重要。相反,我们 找到三个分开的顶点,并计算法线 他们形成的三角形。选择顶点使得 三角形的面积至少是最大面积的1 / sqrt(3)倍 使用输入顶点形成的三角形。
轮廓确实会影响正常的方向;经过计算 正常,我们检查签名轮廓区域的总和是 非阴性,必要时逆转正常。
投影顶点
我们将顶点投影到垂直于三者之一的平面上 坐标轴。这通过删除a来帮助提高数值 原始输入数据和数据之间的转换步骤 由算法处理。投影也会压缩输入 数据;投影后顶点之间的2D距离可以更小 比原来的2D距离。但是通过选择坐标 与正常的点积最大的轴,压缩 因子最多为1 / sqrt(3)。
即使正常的准确度并不那么重要(因为 我们正在垂直于坐标轴投影) 计算的鲁棒性很重要。例如,如果有许多顶点几乎沿着一条线和一个顶点V 这是与行分开的,然后我们正常的计算 应该涉及V否则结果将是垃圾。
垂直于多边形法线投影的优点是 计算出的交点将尽可能接近 他们理想的位置。要获得此行为,请定义TRUE_PROJECT。
线扫描
有三种数据结构:网格,事件队列和 边缘字典。
网格是一个四边形"记录拓扑的数据结构 目前的分解;有关详细信息,请参阅包含文件" mesh.h"。
事件队列只包含所有顶点(原始顶点和计算顶点) 组织,以便我们可以快速提取顶点 最小x-coord(其中,最小y-coord)。
边缘字典描述了扫描的当前交集 与多边形区域对齐。这只是一个排序 与扫描线相交的边,按其当前顺序排序 路口。对于每对边缘,我们存储一些信息 他们之间的单调区域 - 这些是呼叫"活跃区域" (因为它们被当前的扫描线穿过)。
基本算法是从左向右扫描,处理每个 顶点。网格的处理部分(扫描线的左侧)是 平面分解。当我们穿过每个顶点时,我们更新网格 和边缘字典,然后我们检查任何新邻近的对 边缘,看它们是否相交。
顶点可以有任意数量的边。具有许多边的顶点可以 在合并顶点和交叉点时创建 计算。对于未处理的顶点(扫描线的右侧),这些 边缘在顶点周围没有特定的顺序;用于处理 顶点,拓扑排序应与几何相匹配 排序
顶点处理分两个阶段进行:首先我们处理的是 左边缘(所有这些边缘当前都在边缘 字典)。这包括:
- 从字典中删除左边缘;
- 根据需要重新链接网格,以使事件顶点周围的这些边的顺序与字典中的顺序匹配;
- 将任何终止区域(位于两个左边缘之间的区域)标记为"内部"或"外面"根据 他们的蜿蜒数字。
当没有左边缘时,事件顶点在 "室内"区域,我们需要添加一条边(将区域分割成 单调的片断)。为此,我们只需将事件顶点连接到 包含的上边缘或下边缘的最右端点 区域。
然后我们处理正确的边缘。这包括:
- 在边缘词典中插入边缘;
- 计算任何新创建的活动区域的匝数。我们可以使用每个边缘的绕组来逐步计算 当我们走进字典时,我们就越过了。
- 根据需要重新链接网格,以使事件顶点周围的这些边的顺序与字典中的顺序匹配;
- 检查任何新近相邻的边以进行交叉和/或合并。
如果没有右边缘,我们需要再加一个来分割 含有区域成单调的碎片。在我们的例子中它是最多的 方便的是将边添加到任一个的最左端点 包含边缘;但是我们可能需要稍后改变它(见 代码详情)。
不变量
这些是扫描期间保持的最重要的不变量。 我们定义了一个函数VertLeq(v1,v2),它定义了它的顺序 顶点穿过扫描线,并有一个函数EdgeLeq(e1,e2; loc) 这表示在扫描事件位置e1是否低于e2" loc"。 此功能仅在扫描事件位置定义 在最右边的左端点{e1,e2}和最左边的端点之间 {e1,e2}的终点。
边缘词典的不变量。
- 每对相邻边e2 = Succ(e1)在扫描事件的任何有效位置满足EdgeLeq(e1,e2)。
- 如果EdgeLeq(e2,e1)也是(在任何有效的扫描事件中),那么e1和e2共享一个公共端点。
- 对于字典中的每个e,e-> Dst已经处理但不是e-> Org。
- 每个边e满足VertLeq(e-> Dst,event)&& VertLeq(event,e-> Org)其中" event"是当前的扫描线 事件
- 没有边e的长度为零。
没有两条边具有相同的左右端点。网格的不变量(已处理部分)。
扫描线左侧的网格部分是平面图,即。有一些方法将它嵌入到平面中。
- 没有处理过的边长度为零。
- 没有两个处理过的顶点具有相同的坐标。
- 每个"内部"区域是单调的,即。可以根据VertLeq(v1,v2)分成两个单调增加顶点的链
- 非不变量:这些链可能会(略微)相交 数值误差,但这不会影响算法的运算。
扫描的不变量。
- 如果顶点有任何左边缘,那么这些顶点处理时必须在边缘字典中。
- 如果标记边缘" fixUpperEdge" (这是ConnectRightVertex引入的临时优势),那么它是唯一正确的 边缘来自其关联的顶点。 (这说明存在这些边缘 只有在必要的时候。)
鲁棒性
算法的鲁棒性的关键是保持 上面的不变量,特别是边的正确排序 字典。我们通过以下方式实现这一目标:
编写数值计算以获得最大精度 比最高速度。
根本没有对边缘结果做出任何假设 交叉计算 - 对于充分退化的输入, 计算出的位置并不比随机数好多了。
- 醇>
当数字错误违反不变量时,请恢复它们 通过在必要时进行拓扑更改(即重新链接) 网格结构)。
三角测量和分组
我们在进行任何三角测量之前完成线扫描。这是 因为即使在单调区域完成之后,也可以进一步发展 由于进一步的顶点合并,它的顶点数据发生了变化。
在对所有单调区域进行三角测量之后,我们想要对这些区域进行分组 三角形成扇形和条形。我们使用贪婪的方法来做到这一点。 三角测量本身并未优化以减少数量 元;我们只是试图得到一个合理的分解 计算三角测量。