Technical note
OCCT Sewing 的能力边界
记录 BRepBuilderAPI_Sewing 在 CAD/CAE 几何修复中的适用场景和工程边界:它适合连接已有面,但不是补面算法,也不能替代完整修复流程。
背景
在 OCCT 的几何修复接口里,BRepBuilderAPI_Sewing 是一个很常用的工具。
做 CAD/CAE 模型导入、局部修复、补面后处理或者 shell 重组时,经常会遇到这样的情况:几个面在几何上看起来贴在一起,但拓扑上并没有真正共享边。模型显示出来可能问题不明显,但后续做网格、布尔、封闭体识别时,就会暴露出 free edge、open shell 或 shell 不闭合的问题。
这时候 Sewing 往往是最先想到的办法。
它的作用可以简单理解为:
在给定容差范围内,把已有 face 之间能够连接的边界重新缝合起来。
这个能力很有用,但它也很容易被误解成“自动修复模型”或者“自动补面”。
实际工程里,Sewing 的边界非常明确:它适合连接已有面,但不负责理解模型语义,也不负责凭空生成缺失面。
这篇文章主要记录我对 BRepBuilderAPI_Sewing 的工程理解:它能解决什么,不能解决什么,以及怎么用才更可控。
它解决什么
在 BRep 里,两个面看起来贴在一起,并不代表它们在拓扑上真正连接。
例如两个相邻面各自有一条边,这两条边几何位置几乎重合,但它们不是同一条 TopoDS_Edge。从显示角度看,它们可能是一条连续边;但从拓扑角度看,它们仍然是两套独立边界。
这种情况在导入模型里很常见,尤其是 STEP、IGES 或其他中间格式转换后,面之间的几何连续性和拓扑连接关系不一定完全保留下来。
Sewing 主要处理的就是这一类问题:
已有面之间几何上接近;
边界在容差范围内可以匹配;
但拓扑上没有连接;
需要重新组成 shell 或 compound。
一个最简单的调用方式大概是:
BRepBuilderAPI_Sewing sewing(tolerance);
sewing.Add(shape);
sewing.Perform();
TopoDS_Shape sewedShape = sewing.SewedShape();
从工程角度看,Sewing 更像是一个拓扑重组工具,而不是几何建模工具。
它不会主动理解这组面应该组成什么产品结构,也不会知道哪些边应该保留、哪些边可以合并。它只是根据输入面和容差,尝试把可以缝合的边界连接起来。
适合的场景
导入后的轻量整理
导入模型时,有些数据并不是一个干净的 solid,而是一组 face 或 shell。这些 face 可能在几何上能组成一个外壳,但拓扑关系没有完全建立。
例如导入后发现:
shape 是 compound;
内部包含多个 face;
face 之间看起来相邻;
但 shell 没有闭合;
存在若干 free edge。
这时可以尝试对局部 face 集合或整个 shape 做一次 Sewing,然后检查结果是否更接近预期。
但导入阶段的 Sewing 应该保守。
导入阶段的目标是稳定读入模型,而不是替用户做激进修复。如果一上来就使用较大容差对整个模型做 Sewing,可能会把原本不应该连接的边界缝在一起,改变模型语义。
更稳妥的做法是:
小容差;
轻量尝试;
失败不强制;
修复前后做检查;
保留原始 shape 或问题记录。
如果模型存在复杂 open shell、缺面、局部缝隙或近接触风险,最好不要在导入阶段直接强修,而是交给专门的几何修复流程处理。
补面后的重新缝合
Sewing 不会自动补面,但它经常出现在补面之后。
例如模型缺了一块面,系统通过边界生成了一张新的 patch face。这个新面生成出来以后,还不是最终修复结果。它必须和周围原有面建立拓扑连接,否则自由边仍然存在,shell 仍然可能没有闭合。
这个时候 Sewing 就很合适:
找到缺口边界;
构造修补面;
把新面和周围局部面放到一起;
执行 Sewing;
检查 free edge 是否减少;
把修复后的局部 patch 放回模型。
这里 Sewing 的作用不是“补面”,而是“把补出来的面和旧面缝起来”。
所以在局部修复流程里,它通常是一个后处理步骤:
Filling / MakeFace 负责生成新面;
Sewing 负责连接新面和已有面;
Analyzer / FreeBounds 负责验证修复效果。
如果只做 Filling,不做 Sewing,可能会得到一张孤立的新面。
如果只做 Sewing,不做 Filling,又无法处理真正缺面的情况。
交互式局部缝合
交互式修复里,用户可能会选择几张面,希望系统把它们缝合成一个局部 shell。
这类场景比导入阶段更适合做 Sewing,因为用户已经提供了局部上下文。系统不需要对整个模型做全局猜测,只需要在用户选中的范围内尝试修复。
一个比较合理的流程是:
用户选择若干 face;
系统提取这些 face;
统计修复前 free edge;
执行 Sewing;
统计修复后 free edge;
如果问题下降,则生成预览或替换局部 shape;
如果没有改善,则提示 Sewing 不适合这个问题。
这里的重点不是 Sewing 本身,而是局部范围和结果验证。
如果没有局部范围,Sewing 容易误伤其他区域。
如果没有结果验证,Sewing 执行完成也不能说明修复成功。
我更倾向于把交互式 Sewing 设计成一个可解释命令,而不是一个静默按钮。用户至少应该知道:
参与缝合的面有多少;
修复前自由边有多少;
修复后自由边有没有减少;
是否生成了新的 shell;
是否还有未缝合边界;
是否需要进一步补面。
这样用户才能判断这次修复是否可信。
它不能解决什么
不能自动补面
Sewing 最容易被误用的地方,是把它当成补面算法。
如果模型缺了一块面,周围只剩下一圈自由边,Sewing 不会自动创建这张缺失面。它最多只能把已有面之间能够连接的边界缝合起来。
例如:
四周面都存在;
中间缺少一张盖面;
周围形成 open boundary;
shell 无法闭合。
这时候只调用 Sewing,大概率无法真正闭合模型。
这种情况需要补面流程:
识别缺口边界;
构造闭合 wire;
使用 MakeFace 或 Filling 生成新 face;
再把新 face 和周围面 Sewing;
最后检查 shell 是否闭合。
也就是说,Sewing 是补面流程的一部分,但不是补面本身。
如果看到 free edge 就直接 Sewing,很容易出现一种假象:代码执行了,结果也生成了,但问题并没有减少。
所以工程上应该先判断 free edge 的类型:
如果是相邻面拓扑未连接,可以尝试 Sewing;
如果是缺面,应该进入 patch 或 filling 流程;
如果是开放曲面边界,可能根本不应该修。
不能判断模型语义
Sewing 只看输入 shape 和容差,不理解业务语义。
它无法判断:
两条接近的边是否真的应该连接;
某个开口是不是设计上故意保留;
两个相邻面属于同一实体还是不同零件;
缝合后是否会影响材料区域;
缝合后是否会影响边界条件选择;
缝合后是否会影响后续共形网格。
在 CAD/CAE 工程里,这个问题很现实。
有些面靠得很近,但它们代表不同零件之间的接触关系,不应该被直接缝成同一个 shell。
有些开口看起来像缺陷,但可能是用户有意保留的边界。
有些小缝从几何角度可以缝合,但缝合后会改变后续仿真区域。
所以 Sewing 不能脱离上下文使用。
它更适合在明确范围内使用:
导入阶段的小容差轻量整理;
用户选择范围内的局部修复;
补面之后的新旧面连接;
已经分类为拓扑未连接的问题;
修复策略明确允许的局部区域。
不适合这样使用:
对所有导入模型默认大容差 Sewing;
对整个装配体无差别 Sewing;
把 near contact 当成普通面缝合处理;
不检查结果直接替换原 shape;
失败后继续进入后续流程。
两个关键参数
容差
Sewing 的效果很大程度取决于容差。
容差太小,原本应该连接的边界缝不上。
容差太大,不该连接的边界可能被错误缝合。
这在工程里是最难处理的部分之一。因为不同模型的尺度、导入来源、建模精度都不一样。一个固定容差不可能适配所有场景。
例如一个毫米级小零件和一个几十米尺度的大模型,对容差的敏感程度完全不同。如果使用同一个绝对容差,小模型可能过度缝合,大模型可能完全缝不上。
所以 Sewing 容差应该来自上下文,而不是随手写一个常量。
比较稳妥的做法是:
有默认容差;
支持用户或策略传入容差;
容差和模型尺度有关;
高风险场景限制最大容差;
修复后用结果验证来判断是否接受。
但即使这样,也不要把“调大容差”当成万能办法。
有时候 Sewing 失败,不是容差不够,而是问题类型不对。例如缺面、边界不闭合、局部自交、面方向混乱、几何质量太差,这些都不是简单调大容差就能解决的。
如果每次失败都自动增大容差,最后很容易把模型修坏。
输入范围
Sewing 的输入范围直接决定修复风险。
输入范围太小,可能缺少必要邻域,导致该缝的地方缝不上。
输入范围太大,可能把不相关的面也纳入 Sewing,带来误缝合风险。
所以在工程里,真正难的不是写:
sewing.Add(shape);
sewing.Perform();
而是决定:
应该把哪些 face 放进去;
是否只处理用户选择的局部区域;
是否需要加入相邻面作为上下文;
是否应该排除不同零件之间的接触面;
输出结果如何映射回原 shape。
如果是整个导入模型,直接对全局 shape Sewing 可能方便,但风险更高。
如果是局部修复,输入范围应该尽量围绕问题区域构造,例如选中面、相邻面、补面结果和必要边界。
我现在更倾向于把 Sewing 放在局部 patch 流程里,而不是到处对全局 shape 做无差别处理。
局部 Sewing 的好处是:
影响范围小;
结果更容易验证;
失败后更容易回滚;
用户更容易理解;
不会误改整个模型。
当然,局部 Sewing 也有成本:需要先定位问题区域,并且需要处理局部结果和原模型的重新组装。但对于复杂 CAD/CAE 模型来说,这个成本是值得的。
Sewing 后怎么验收
Sewing 执行完成以后,不能只看 SewedShape() 是否为空。
更重要的是检查修复前后自由边有没有变化。
一个更可靠的流程是:
统计修复前 free edge;
执行 Sewing;
获取 Sewing 结果;
检查输出 shape 是否有效;
统计修复后 free edge;
判断问题数量是否下降;
决定接受结果还是回滚。
如果修复前有 20 条自由边,修复后还是 20 条,那这次 Sewing 对目标问题可能没有帮助。
如果修复后自由边减少,但仍然没有完全闭合,也不能简单判定失败。它可能是一次部分有效修复,需要后续补面或其他策略继续处理。
所以结果判断不应该只有 true / false,而应该有更多信息:
是否执行成功;
free edge 是否减少;
是否生成新的 shell;
是否仍存在 open boundary;
是否需要进入补面流程;
是否建议用户继续手动处理。
除了 free edge,还要检查 shape validity。
自由边减少并不代表 shape 一定有效。有时候 Sewing 后拓扑连接变好了,但局部几何或面方向仍然有问题。也可能因为容差或输入范围不合适,引入新的拓扑风险。
所以 Sewing 后至少要检查:
输出 shape 是否为空;
输出 shape 类型是否符合预期;
基础有效性是否通过;
是否引入 non-manifold;
是否出现异常小边或退化边;
是否影响后续操作。
在 CAD/CAE 系统里,还可能需要进一步检查:
后续网格是否能生成;
接触关系是否被破坏;
用户选择语义是否仍然可识别;
局部拓扑是否和修复前报告一致。
Sewing 只是修复步骤,不是验收标准。
真正能决定结果是否可用的,是修复后的验证流程。
和 ShapeFix、Filling 的关系
Sewing 经常会和 ShapeFix、BRepFill_Filling 一起出现,但它们解决的问题不同。
ShapeFix 更偏基础修复和规整,例如 face、wire、edge 级别的问题处理。
Sewing 更偏把已有面之间的边界连接起来。
BRepFill_Filling 更偏从边界生成一张新面。
可以简单理解成:
ShapeFix 负责基础规整;
Filling 负责生成新面;
Sewing 负责把已有面连接起来。
一个比较常见的流程是:
先做基础检查;
必要时做轻量 ShapeFix;
识别自由边和 open boundary;
判断是否属于拓扑未连接;
对局部面集合做 Sewing;
如果确实缺面,再进入 Filling;
Filling 后再次 Sewing;
最后检查 free edge 和 shape validity。
这里要避免一个坏习惯:不断叠加 ShapeFix 和 Sewing,希望多跑几次就能修好。
有些模型确实多跑几轮会改善,但也可能逐步改变拓扑语义。没有检测和报告的多轮修复,很难解释每一轮到底产生了什么影响。
结果应该返回什么
如果在工程里封装 Sewing,我不建议只返回 TopoDS_Shape。
更好的返回结果应该包含:
修复是否成功;
输出 shape;
修复前 free edge 数量;
修复后 free edge 数量;
是否减少 open boundary;
是否生成 shell;
基础有效性是否通过;
是否建议接受结果;
失败原因或警告信息。
例如可以抽象成:
struct SewingResult
{
bool success = false;
bool valid = false;
bool improved = false;
int freeEdgeCountBefore = 0;
int freeEdgeCountAfter = 0;
TopoDS_Shape resultShape;
std::string message;
};
这只是示意,不是固定设计。
重点是:Sewing 的结果不应该只是一份 shape,而应该是一份带判断依据的修复结果。
上层策略可以根据这些信息决定:
接受 Sewing 结果;
进入补面流程;
提示用户选择更多面;
调整容差后重试;
回滚到原始 shape;
只输出问题报告。
这样 Sewing 才能成为修复系统的一部分,而不是一个孤立 API 调用。
什么时候放弃
工程里还有一个重要判断:不是所有问题都应该继续 Sewing。
如果遇到下面几类情况,就应该考虑停止,而不是不断调大容差重试:
自由边没有减少;
输出 shape 无效;
出现不期望的拓扑合并;
输入边界明显缺面;
模型是开放曲面;
问题区域跨越不同零件;
需要保持接触关系而不是缝合成一体。
有些问题应该转入补面。
有些问题应该转入局部拓扑编辑。
有些问题应该只记录,不自动修。
还有一些问题可能需要用户重新选择区域。
放弃 Sewing 不是失败,而是策略判断的一部分。真正危险的是明知道 Sewing 不适合,还继续增大容差反复尝试。
小结
如果把 Sewing 放到整个几何修复流程里,我现在会这样定位它:
Sewing 是连接已有面的工具;
它适合处理拓扑未连接和局部 open shell;
它不适合自动补面;
它不能判断模型语义;
它对容差非常敏感;
它必须配合 free edge 检查和 shape validity 验证;
它更适合在明确范围内使用,而不是无差别全局强修复。
所以在 CAD/CAE 几何修复里,Sewing 应该是一个受控步骤,而不是万能按钮。
它可以出现在导入阶段,但应该轻量保守。
它更适合出现在交互式修复、局部 patch 修复、补面后处理和面缝隙修复流程里。
更重要的是,Sewing 前要知道问题是什么,Sewing 后要知道结果有没有变好。
BRepBuilderAPI_Sewing 是一个非常实用的接口,但它不是自动补面算法,也不是模型语义判断器,更不是完整几何修复系统。
它真正适合的位置,是几何修复系统里的一个基础执行器。它前面需要检测和分类,后面需要验证和报告。只有放在这样的流程里,Sewing 才是可控的工程修复工具,而不是一次不可解释的黑盒修改。