3.7 KiB
3.7 KiB
核心算法设计
目录
1. Delaunay 三角剖分(2D)
算法:Bowyer-Watson 增量算法 复杂度:O(n log n) 期望,O(n²) 最坏
算法步骤:
1. 构造一个覆盖所有输入点的超级三角形
2. 逐点插入:
a. 找到外接圆包含新点的所有"坏"三角形
b. 提取坏三角形的边界边(只保留不重复的边)
c. 删除坏三角形
d. 用新点与每条边界边连接,形成新三角形
3. 删除包含超级三角形顶点的三角形
关键实现细节:
- 三角形预存外接圆圆心和半径,加速包含判断
- 用哈希集合高效提取边界边(出现两次的边=内部边,删除;出现一次的边=边界边,保留)
2. NURBS 曲线求值
算法:Cox-de Boor 递推公式
求值步骤:
1. find_span(t):二分查找 t 所在的节点区间
2. basis_functions(t, span):递推计算基函数 N_{i,p}
3. 加权组合:P(t) = Σ N_i·w_i·cp_i / Σ N_i·w_i
基函数递推:
N_{i,0}(t) = 1 若 knots[i] ≤ t < knots[i+1],否则 0
N_{i,p}(t) = (t - knots[i]) / (knots[i+p] - knots[i]) · N_{i,p-1}(t)
+ (knots[i+p+1] - t) / (knots[i+p+1] - knots[i+1]) · N_{i+1,p-1}(t)
复杂度:O(p²),p 为阶数
3. GJK 碰撞检测
算法:Gilbert-Johnson-Keerthi 用途:检测两个凸体是否相交
核心思想:
1. 定义 Minkowski 差:C = A ⊖ B = {a - b | a∈A, b∈B}
2. A 与 B 相交 ⇔ C 包含原点
3. 迭代构建包含原点的单纯形(点→线→三角形→四面体)
4. 每步使用 support 函数在 Minkowski 差上采样
5. 3D 中最多约 40 次迭代即可得到结果
Minkowski 差的 support 函数:
support(d) = support_A(d) - support_B(-d)
4. 网格简化(QEM)
算法:Quadric Error Metrics(Garland & Heckbert, SIGGRAPH 1997)
算法步骤:
1. 计算每个顶点的二次误差矩阵 Q(v)
2. 计算每条边的收缩代价:cost(e) = v_opt^T · (Q_v1 + Q_v2) · v_opt
3. 将所有边按代价放入最小堆
4. 贪心循环:
a. 弹出代价最小的边
b. 收缩该边到最优位置(或中点)
c. 更新所有受影响边的代价
5. 重复直到达到目标面数
Q 矩阵定义:对每个顶点 v,其 Q 矩阵为相邻面平面方程的外积和:
Q(v) = Σ K_p^T · K_p,其中 K_p = [a b c d],ax+by+cz+d=0
5. BVH 构建(SAH)
算法:表面积启发式(Surface Area Heuristic)
SAH 代价函数:
C = C_trav + (n_L·area_L + n_R·area_R) / area_total · C_isect
构建过程:
1. 对当前节点计算包围盒
2. 若图元数 ≤ leaf_size 或深度 ≥ max_depth → 创建叶节点
3. 否则:
a. 沿最长轴排序图元(按质心坐标)
b. 尝试多个分割位置,计算 SAH 代价
c. 选择代价最低的分割
d. 如果分割代价 ≥ 不分割的代价 → 创建叶节点
e. 否则递归构建左右子树
6. 2D 多边形布尔运算
算法:Sutherland-Hodgman 裁剪(当前实现) 目标算法:Vatti 扫描线算法(对标 Clipper2)
当前实现(S-H):
1. 以裁剪多边形为窗口
2. 对主题多边形的每条边逐一裁剪
3. 时间复杂度 O(n·m)
规划实现(Vatti):
1. 扫描线遍历所有边交点
2. 边分割与标记(内部/外部)
3. 提取结果轮廓
4. 支持任意多边形(含自交、带孔)