OpenCascade 倒角(Chamfer)算法完整逻辑解析

OpenCascade(OCCT)的倒角功能由 BRepFilletAPI_MakeChamfer 对外提供,底层复用了与圆角(Fillet)完全相同的算法框架 ChFi3d_Builder,仅在几何曲面生成、拐角过渡两个环节存在差异。整体采用「几何求交先行、拓扑重构后置」的流水线设计,所有计算先写入统一的干涉数据库,最终一次性完成 BRep 拓扑切割与缝合。


倒角前:

倒角后:

一、整体架构:四层设计与核心类

1.1 四层架构

层级 职责 核心类
API 层 对外接口,封装参数设置、结果查询 BRepFilletAPI_MakeChamfer
算法调度层 流程总控、拐角分发、几何求交 ChFi3d_Builder
数据结构层 存储倒角带、脊线、曲面、干涉关系 ChFiDS_Stripe / ChFiDS_Spine / TopOpeBRepDS_DataStructure
拓扑重建层 通用曲面切割、拓扑缝合、实体组装 ChFiDS_FilDS / TopOpeBRepBuild_Coupler

1.2 核心数据概念

  • 倒角带(Stripe) :一条待倒角棱对应一个 ChFiDS_Stripe,是算法调度的最小单元,包含脊线、支撑面、倒角参数、生成的倒角曲面。
  • 脊线(Spine) :待倒角的原始棱边的参数化表达,ChFiDS_ChamfSpine 为倒角专用子类,存储对称/双距离/角度三种倒角模式。
  • 支撑面(Support Face):倒角棱相邻的两个原始实体面,倒角平面沿支撑面偏置生成。
  • DS 干涉数据库TopOpeBRepDS_DataStructure,是 OCCT 布尔/倒角通用的中间数据结构,存储所有原始形状、新生几何、相交关系、拓扑映射。

二、完整执行流水线

以下流程对应 ChFi3d_Builder::Compute() 的执行顺序,也是倒角算法的核心主干。

阶段1:输入初始化与倒角带构建

1.1 上层 Add() 构建倒角带

调用 BRepFilletAPI_MakeChamfer::Add(dist1, dist2, edge, face) 时,内部完成三件事:

  1. 校验棱边与支撑面的拓扑邻接关系;
  2. 解析倒角参数(对称 C 角、双距离不等角、距离+角度);
  3. 创建 ChFiDS_Stripe,构建 ChFiDS_Spine 脊线,加入 myListStripe 总列表。

关键细节:必须显式传入支撑面,算法依靠支撑面区分倒角的「切除侧」;仅传边会导致方向歧义,批量倒角时容易出现向内切穿实体的错误。

1.2 脊线端点状态判定

对每条脊线的两个端点,标记三种状态:

  • ChFiDS_FreeBoundary:自由边界,该端点没有其他相邻倒角棱;
  • ChFiDS_BreakPoint:交汇顶点,至少还有另一条倒角棱在此顶点汇聚;
  • ChFiDS_OnSame:共面端点,两条倒角棱共享同一侧面。
1.3 构建顶点-倒角带映射
cpp 复制代码
// 遍历所有 Stripe,将交汇顶点存入 myVDataMap
for (itel.Initialize(myListStripe); itel.More(); itel.Next()) {
  if (端点状态 == BreakPoint)
    myVDataMap.Add(顶点, Stripe);
  else if (端点状态 == FreeBoundary)
    ExtentOneCorner(顶点, Stripe);
}
  • myVDataMap:Key 为顶点,Value 为汇聚在该顶点的所有倒角带列表;
  • 自由边界端点直接执行单拐角预延伸,不进入映射表;
  • 此映射是后续拐角求解的索引基础。

阶段2:预延伸分析(ExtentAnalyse)

2.1 为什么必须预延伸

倒角曲面默认严格贴合原始棱的参数范围,在顶点处多个倒角曲面仅「点对点接触」,没有重叠区域,后续求交无法找到交线,拐角会出现破洞。预延伸的核心目标是:让相邻倒角曲面产生足够重叠,为拐角求交创造几何条件

2.2 四类拐角的延伸策略
cpp 复制代码
switch (顶点汇聚的倒角带数量) {
  case 1: ExtentOneCorner();   break; // 单棱端头
  case 2: ExtentTwoCorner();   break; // 两棱交汇
  case 3: ExtentThreeCorner(); break; // 三棱交汇(立方体顶点)
  default: 不预延伸; break;          // 四棱及以上,动态求解
}
  • ExtentOneCorner:固定系数延伸,将脊线参数区间向外扩展 50% 原始长度,适用于孤立端头。
  • ExtentTwoCorner / ExtentThreeCorner:自适应延伸,先找到相邻倒角带的公共支撑面,再根据倒角宽度计算最小必要延伸量,避免过度延伸导致自交。

阶段3:逐条生成倒角曲面(PerformSetOfSurf)

cpp 复制代码
for (每条 Stripe) {
  try { PerformSetOfSurf(stripe); }
  catch (...) { badstripes.Append(stripe); }
}
done = badstripes.IsEmpty();
3.1 核心几何计算

根据倒角参数模式,生成对应的倒角平面:

  • 对称倒角:两侧支撑面偏置距离相等,倒角平面与两面呈 45°;
  • 双距离倒角:两侧偏置距离不同,按比例生成斜面;
  • 距离+角度倒角:由一侧距离与角度换算出另一侧等效距离,再生成平面。

与圆角的核心分叉点:圆角此处生成圆环面(Torus),倒角生成平面(Plane);上层调度、拐角处理、拓扑重构逻辑完全复用。

3.2 失败容错机制

单条倒角带曲面生成失败时,加入 badstripes 列表,不直接崩溃;全部生成成功才进入下一阶段。

阶段4:顶点拐角求解(PerformFilletOnVertex)

遍历 myVDataMap 中所有交汇顶点,根据汇聚倒角带数量分发到不同求解器,这是倒角算法最复杂的部分。

4.1 拐角分类与求解器
拐角类型 求解器 典型场景
单拐角 PerformOneCorner 棱的端头倒角,无相邻倒角
两拐角 PerformTwoCorner 两条相邻棱同时倒角
三拐角 PerformThreeCorner 立方体顶点三条棱倒角
多棱拐角 PerformMoreThreeCorner 四棱及以上交汇的复杂顶点
奇异拐角 PerformSingularCorner 倒角曲面边界退化重合的极端拓扑
4.2 单拐角(PerformOneCorner)深度解析

这是最基础、最常用的拐角类型,核心目标是生成倒角端头的封口交线,封闭倒角斜面

  1. 定位曲面与边界点:找到靠近顶点的倒角曲面片,提取两侧边界特征点。
  2. 拓扑分支判定
    • OnSame = true:封闭实体端头,倒角端面贴合实体端面,走精确封口逻辑;
    • OnSame = false:开放边界,调用 FindFace 搜索承接面,找不到则降级为通用端点求交。
  3. 封口交线计算 :调用 ChFi3d_ComputeCurves 求解倒角平面与端面的交线,同时输出 3D 曲线与两个面上的 2D 参数曲线。
  4. 缝合边相交处理:若交线与实体缝合边(Sewing Edge)相交,将交线切割为两段,分别登记入 DS。
  5. 自相交校验:检查当前封口曲线与其他倒角带的参数曲线是否相交,相交则判定「倒角尺寸过大」,抛出异常终止。

阶段5:倒角带间干涉检测

cpp 复制代码
// 双重循环,两两倒角带求交(i2>i1 避免重复)
ChFi3d_StripeEdgeInter(st, aCheckStripe, DStr, tol2d);

这是 OCC-119 版本加入的关键修复:

  • 检测不同倒角带的曲面是否发生穿透干涉;
  • 若求交失败或存在非法干涉,标记当前倒角带为失败,终止流水线;
  • 解决了早期版本大倒角、多拐角时出现曲面互穿、实体破碎的问题。

阶段6:倒角拓扑数据填充(ChFi3d_FilDS)

cpp 复制代码
ChFi3d_FilDS(solidindex, st, DStr, myRegul, tolesp, tol2d);

这是从「几何计算」转向「拓扑构建」的枢纽:

  1. 将生成的倒角平面、边界曲线、顶点,以「形状+干涉关系」的形式写入 DS 数据库;
  2. 建立倒角面与原始支撑面的邻接关系;
  3. 记录拓扑分割所需的所有交线、点干涉信息。

注意:此阶段仍不直接修改原始 BRep,所有变更全部缓存到 DS 中。

阶段7:全局 BRep 拓扑重构(核心重组阶段)

这是真正执行「BRep 重新组织」的环节,分为三步。

7.1 容差同步(两次修正)

倒角分割会产生大量新的边与顶点,容差不匹配是拓扑缝合失败的首要原因。代码中做了两轮容差对齐:

  1. 第一轮:遍历 DS 中所有曲线,同步曲线与关联顶点的容差,顶点容差取相邻边最大值;
  2. 第二轮:拓扑分割完成后,遍历所有新生边,再次放大两端顶点容差至边容差水平。
7.2 Coupler 执行拓扑切割与缝合
cpp 复制代码
myCoup->Perform(myDS);

myCoupTopOpeBRepBuild_Coupler 实例,OCCT 通用布尔分割引擎:

  1. 读取 DS 中所有原始面、交线、干涉关系;
  2. 用交线切割原始支撑面,切除被倒角去掉的尖角区域;
  3. 将倒角面与切割后的基体面进行拓扑缝合,建立共享边与共享顶点;
  4. 维护「原始形状 → 分割后新形状」的映射关系,支持通过 Splits() / Merged() 查询。

核心结论:BRep 拓扑的切割、重组、缝合,90% 以上的工作由 Coupler::Perform 完成;此前所有阶段都只是在为它准备输入数据。

7.3 登记参与合并的实体

将所有原始 Solid / Shell 注册给 Coupler,指定合并方向为 TopAbs_IN,让切割器知道哪些实体需要最终缝合为闭合实体。

阶段8:结果组装与后处理

8.1 结果组装
  • 成功分支 :创建 TopoDS_Compound,遍历所有原始实体;若实体被分割则加入分割后的新实体,未分割则直接加入原始实体。
  • 失败分支 :将中间结果存入 badShape,供调试定位失败位置;IsDone() 返回 false。
8.2 后处理
  1. SetRegul:拓扑规整,消除退化边、重复顶点,修正非流形拓扑;
  2. BRepLib::SameParameter:统一曲面参数化,保证边在两侧面上的参数曲线一致,避免后续布尔、网格化出现几何不连续。

三、倒角 vs 圆角:架构复用与核心差异

OCCT 用同一套 ChFi3d_Builder 框架同时支持倒角和圆角,仅两处核心分叉:

  1. 曲面生成:倒角生成平面,圆角生成圆环面/滚动球面;
  2. 拐角过渡:三拐角处倒角是平面相交形成的折线拐角,圆角是球面/环面顺滑过渡;多拐角求解器逻辑不同。

其余的脊线管理、预延伸、DS 构建、Coupler 拓扑缝合、容差处理完全复用,这也是 OCCT 几何内核「算法分层、能力复用」的典型设计。


四、工程视角:倒角失败常见原因与定位

结合算法流程,可以快速定位绝大多数倒角失败的根源:

  1. 输入拓扑错误:原始模型存在自由边、非流形顶点、面法向混乱 → 阶段1 构建 Stripe 失败;
  2. 支撑面选择错误:批量倒角随机取第一个相邻面,导致向内倒角、切穿实体 → 阶段3 曲面生成异常;
  3. 倒角尺寸过大:倒角距离超过支撑面宽度,或相邻倒角互相穿透 → 阶段5 干涉检测抛出异常;
  4. 多棱顶点收敛失败:四棱及以上复杂顶点,拐角求交迭代不收敛 → 阶段4 PerformMoreThreeCorner 失败;
  5. 容差不匹配:原始模型容差混乱,切割后拓扑缝合失败 → 阶段7 Coupler 报错;
  6. 延伸不足:特殊角度棱的预延伸量不够,拐角处曲面无重叠 → 拐角求交找不到交线,出现破洞。

五、全流程数据流总结

复制代码
输入:Solid + {Edge, 距离, 支撑面}
    ↓
构建 ChFiDS_Stripe 列表 + 脊线 Spine
    ↓
填充 myVDataMap 顶点映射 + 自由边界直接预延伸
    ↓
ExtentAnalyse:按拐角类型协同延伸脊线参数
    ↓
PerformSetOfSurf:逐条生成倒角平面
    ↓
PerformFilletOnVertex:多类拐角求交、生成封口边界
    ↓
ChFi3d_StripeEdgeInter:倒角带两两干涉检测
    ↓
ChFi3d_FilDS:倒角几何写入 DS 干涉数据库
    ↓
容差同步 → Coupler::Perform 执行 BRep 切割与缝合
    ↓
拓扑规整 + 参数统一 → 组装输出结果 Solid/Compound
相关推荐
c238561 小时前
《枚举算法 “翻译官”:用 enum class 给 “游游的礼盒” 算分》
c语言·c++·算法
noipp1 小时前
推荐题目:洛谷 P5843 [SCOI2012] Blinker 的噩梦
c语言·数据结构·c++·算法·游戏·洛谷·luogu
冻柠檬飞冰走茶2 小时前
PTA基础编程题目集 7-20 打印九九口诀表(C语言实现)
c语言·开发语言·数据结构·算法
鱼子星_2 小时前
《算 · 法》题目解析篇(1):最近公共祖先问题,线段树,最长回文子序列
c++·算法·动态规划·递归
雾喔2 小时前
算法练习5
算法·代理模式
问商十三载2 小时前
RAG 检索召回越多越好?2026 信噪比模型深度解析
人工智能·算法·机器学习
额恩662 小时前
ResearchPilot 第三阶段总结报告
python·算法
Hi李耶2 小时前
【LeetCode】9-回文数
算法·leetcode·职场和发展
红叶舞2 小时前
基于线段树的数据结构
数据结构·python·算法