# 核心算法设计 ## 目录 1. [Delaunay 三角剖分(2D)](#1-delaunay-三角剖分2d) 2. [NURBS 曲线求值](#2-nurbs-曲线求值) 3. [GJK 碰撞检测](#3-gjk-碰撞检测) 4. [网格简化(QEM)](#4-网格简化qem) 5. [BVH 构建(SAH)](#5-bvh-构建sah) 6. [2D 多边形布尔运算](#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 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. 支持任意多边形(含自交、带孔) ```