Files
ViewDesignEngine/docs/03-详细设计/02-核心算法设计.md
T

3.7 KiB
Raw Blame History

核心算法设计

目录

  1. Delaunay 三角剖分(2D
  2. NURBS 曲线求值
  3. GJK 碰撞检测
  4. 网格简化(QEM
  5. BVH 构建(SAH
  6. 2D 多边形布尔运算

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 MetricsGarland & 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. 支持任意多边形(含自交、带孔)