展开教程目录
1 先看接口调整了什么2 理解 Curve Edge Wire 和方向3 最短调用流程4 从几何曲线构造图中的输入5 两个参数该怎样设置6 输入约束和失败处理7 如何正确检查输出中的方向8 边被拆分以后怎样找回对应关系9 与放样 API 怎样配合10 针对整体空间走向应额外做什么11 编译运行完整示例12 常见问题13 参考资料与版本OCCT BRepFill_CompatibleWires 配图使用教程
BRepFill_CompatibleWires 用来把一组用于放样的 Wire 调整为彼此兼容的截线。它可以处理遍历方向、闭合轮廓的起点,以及必要的边分段对应。对于两条单独的空间边,可以先把每条 Edge 包成单边 Wire,再调用这个接口。
本教程从两条位置错开、遍历方向相反的空间曲线开始,依次完成建模、调向、读取输出和结果检查。示例已在 OCCT 7.8.1 上编译运行;接口与分支说明另对照官方 V7_9_0 源码。使用其他版本时,以对应版本的头文件和实际结果为准。
需要始终区分:接口能完成放样截线的兼容处理,但 IsDone() 不能作为任意两条空间曲线整段同向的证明。
1 先看接口调整了什么

蓝色边 A 的方向为左端到右端;橙色边 B 初始方向为右端到左端。两条曲线的形状相同,但 B 在空间中平移了一段距离。调用接口后,B 的起点和终点在遍历意义上交换,箭头与 A 一致,曲线的几何形状和位置保持不变。
图中小圆点表示起点,方点表示终点,大圆点表示当前行进位置。虚线只示意相同遍历进度之间的连接关系,不是接口生成的曲面,也不是实际放样后的边界网格。
打开可独立使用的交互图例。可以切换空间斜视、XY 俯视和 XZ 正视,也可以拖动行进滑块。图例按归一化参数推进,未宣称按弧长匀速;本例两条曲线同形且同参数化,因此便于直接对照。
1.1 本例的输入
A 为非平面的三次 Bézier 曲线,四个控制点如下:
| 控制点 | X | Y | Z |
|---|---|---|---|
| P0 | -5.0 | 0.0 | 0.0 |
| P1 | -2.5 | 4.2 | 1.8 |
| P2 | 2.5 | -4.2 | -1.2 |
| P3 | 5.0 | 0.0 | 0.8 |
B 使用相同的控制点形状,整体平移 (0, 4.6, 3.0),再把对应 Edge 设为 REVERSED。模型坐标单位由应用约定;接口本身没有给这些数值附加毫米或米的单位。
1.2 实际运行结果
| 检查项 | 结果 | 能说明什么 |
|---|---|---|
IsDone() |
true |
本次兼容处理完成 |
输出 BRepCheck_Analyzer::IsValid() |
两条均为 true |
输出通过该 BRep 有效性检查 |
| A 的 Edge 方向 | FORWARD → FORWARD |
A 在本例中保持原方向 |
| B 的 Edge 方向 | REVERSED → FORWARD |
B 被接口反向 |
| 每条 Wire 的边数 | 1 → 1 |
本例没有拆边 |
对应输入输出 Edge 的 IsSame() |
true |
本例复用了相同 TShape 和 Location |
| 201 点采样集的最大双向最近距离 | 约 2.14e-15 |
数值对照中没有可见几何位移 |
查看原生运行记录,查看实际采样数据。这些结果针对当前这组输入;其他轮廓可能需要改变起点或拆边,不能要求所有调用都保持 Edge 的身份与数量。
2 理解 Curve Edge Wire 和方向
2.1 输入为什么必须是 Wire
Geom_Curve 表示几何曲线,参数递增方向定义了曲线的自然方向。TopoDS_Edge 在几何曲线之上增加了边界、参数范围、位置和拓扑方向。TopoDS_Wire 则把一条或多条 Edge 组织成可遍历的线框。
这个接口的参数类型虽然是 TopTools_SequenceOfShape,其中每个普通元素仍应当是 TopoDS_Wire。不要因为容器存储的是 Shape,就直接加入 Edge 或 Geom_Curve。接口声明
Geom_Curve
↓ 构造拓扑边
TopoDS_Edge
↓ 包装成单边线框
TopoDS_Wire
↓ 按截线顺序加入
TopTools_SequenceOfShape
↓
BRepFill_CompatibleWires
↓ Shape()
处理后的 Wire 序列
2.2 FORWARD 和 REVERSED 的含义
对于普通有向 Edge,FORWARD 表示按底层曲线参数递增方向遍历,REVERSED 表示按参数递减方向遍历。反转拓扑边的方向,通常不需要改变曲线的控制点。
但两条独立 Edge 都是 FORWARD,并不保证它们在空间中同向:它们各自的底层曲线可以有相反的参数方向。Orientation() 是相对于自身几何表示的标记,不能单独承担两条任意曲线的整体比较。OCCT 建模数据中的方向概念
2.3 只反转 Wire 和读取输出的区别
不要用输入 Wire 自身的 Orientation() 是否改变来判断接口是否调向成功。算法可能重建外层 Wire,并在其中以相反顺序使用反向的 Edge。应当通过 BRepTools_WireExplorer 查看输出 Wire 实际包含的有向边及遍历顺序。
3 最短调用流程
假设已经有两条有效的 Edge:edgeA 和 edgeB。
#include <BRepFill_CompatibleWires.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <TopTools_SequenceOfShape.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Wire.hxx>
BRepBuilderAPI_MakeWire makeWireA(edgeA);
BRepBuilderAPI_MakeWire makeWireB(edgeB);
if (!makeWireA.IsDone() || !makeWireB.IsDone()) {
// 输入不能构造成 Wire,先处理输入问题。
return;
}
TopTools_SequenceOfShape sections;
sections.Append(makeWireA.Wire());
sections.Append(makeWireB.Wire());
BRepFill_CompatibleWires compatible(sections);
compatible.Perform();
if (!compatible.IsDone()) {
const auto status = compatible.GetStatus();
// 记录 status,本次不继续使用输出。
return;
}
// 复制序列,避免持有对算法对象内部序列的长期引用。
TopTools_SequenceOfShape adjusted = compatible.Shape();
TopoDS_Wire outputA = TopoDS::Wire(adjusted.Value(1));
TopoDS_Wire outputB = TopoDS::Wire(adjusted.Value(2));
这个片段放在已有的业务函数内使用。它展示正常调用顺序;程序边界仍应处理 Standard_Failure。完整程序见可编译示例。
按以下顺序理解每一步:先把 Edge 组织成 Wire,再执行兼容处理,确认状态,最后采用 Shape() 返回的 Wire。后续如果仍使用旧输入,就不会采用接口产生的调整结果。
3.1 多条截线应按实际位置顺序加入
对于 wire0、wire1、wire2,应按期望的放样推进顺序加入容器。接口不负责自动把一组打乱的空间截线排序。开口 Wire 的方向匹配从第一条有效截线开始,逐步参考前一条已处理的截线;不要假设每条都独立对照最初的第一条。兼容处理实现
3.2 重新使用对象时先 Init
compatible.Init(newSections);
// 如需自定义参数,在 Init 之后设置。
compatible.SetPercent(0.01);
compatible.Perform();
重新初始化会清理内部映射,并重设内部参数。不要对同一对象无条件连续调用 Perform() 来尝试不同配置;更清楚的做法是重新 Init,或者为每次尝试创建一个新对象。
4 从几何曲线构造图中的输入
下面的片段解释图例最关键的输入构造。控制点保存在 TColgp_Array1OfPnt 中,两个 Bézier 对象使用相同的形状,B 仅增加平移。
TColgp_Array1OfPnt polesA(1, 4), polesB(1, 4);
polesA(1) = gp_Pnt(-5.0, 0.0, 0.0);
polesA(2) = gp_Pnt(-2.5, 4.2, 1.8);
polesA(3) = gp_Pnt( 2.5, -4.2, -1.2);
polesA(4) = gp_Pnt( 5.0, 0.0, 0.8);
for (int i = 1; i <= 4; ++i) {
polesB(i) = polesA(i).Translated(gp_Vec(0.0, 4.6, 3.0));
}
Handle(Geom_BezierCurve) curveA = new Geom_BezierCurve(polesA);
Handle(Geom_BezierCurve) curveB = new Geom_BezierCurve(polesB);
BRepBuilderAPI_MakeEdge makeA(curveA), makeB(curveB);
if (!makeA.IsDone() || !makeB.IsDone()) {
return;
}
TopoDS_Edge edgeA = makeA.Edge();
TopoDS_Edge edgeB = makeB.Edge();
edgeB.Reverse();
最后一行只是在调用前故意准备一个反向输入。从 Perform() 读出结果之后,示例没有再人工反向输出 B。 因而图中的方向变化来自接口的实际返回结果。
如果已经拿到 TopoDS_Edge,直接从第 3 节开始。如果拿到的是裁剪曲线,还需要保证构造 Edge 时采用了正确的有效参数范围;不要把无限直线或无界参数区间直接代入有限曲线的采样逻辑。
5 两个参数该怎样设置
5.1 Perform 的 WithRotation
compatible.Perform(Standard_True); // 默认调用方式
compatible.Perform(Standard_False); // 不应解读为禁止所有旋转或禁止反向
WithRotation 参与特定闭合轮廓极坐标匹配分支中的临时坐标比较。源码对比较点做变换,帮助寻找轮廓对应关系;它不是把输出曲线整体搬动或旋转的通用开关。兼容处理实现
尤其需要注意:false 仍不表示完全不做比较坐标的旋转,也不阻止算法调整遍历方向。开口 Wire 的 SearchOrigin 分支没有使用这个参数。图例用的是默认 Perform();改变这个参数,不应被当作修复所有错误调向的办法。
5.2 SetPercent 不是全局精度
compatible.SetPercent(0.01);
这个值用于某些闭合轮廓对应与拆边判断,影响候选位置是否靠近现有端点的参数比例阈值。它不是“允许百分之一的曲线形状误差”,也不是曲线同向比较的角度容差。接口声明、兼容处理实现
在本次核查版本中,需要区分两种默认行为:
| 写法 | 对应值 |
|---|---|
构造或 Init 后不调用 SetPercent |
内部初始值为 0.1 |
调用无参 SetPercent() |
C++ 缺省实参为 0.01 |
调用 SetPercent(0.02) |
显式请求 0.02 |
有效设置范围为 0 < percent < 1。超出范围的传参在该实现中被忽略,因此业务层应自行拒绝错误配置。入门示例保持默认行为,以便复现已运行的图例;需要比较参数效果时,应在同一组输入上重新初始化后逐次尝试并检查结果。
6 输入约束和失败处理
6.1 调用前应检查哪些输入
普通使用场景应保证至少有两条有效截线,每个元素都是非空、连通并且能正确遍历的 Wire;非退化 Edge 应有可用的 3D 曲线。数量、空输入和不合法的拓扑不能指望由该类全面自动诊断。
常规非退化截线应当全部开口或全部闭合。一个开口线框和一个闭合线框混用,会产生拓扑不一致。首尾退化点截线是放样中的特殊情况:底层类用退化 Edge 组成的 Wire 表达,不能直接把 TopoDS_Vertex 放进序列。首次集成建议先使用普通截线,点截线优先通过更高层放样 API 的专用接口处理。
输入不要求全部处在同一个平面。当前图例正是非平面曲线;但强对称、退化、自交或很复杂的空间截线仍可能让对应关系不稳定,应在使用前单独验证。
6.2 使用 IsDone 和 GetStatus
GetStatus() 使用共用的 BRepFill_ThruSectionErrorStatus 枚举。状态定义
| 状态后缀 | 含义及处理方式 |
|---|---|
Done |
兼容处理完成,继续检查输出 |
NotDone |
尚未成功完成处理 |
NotSameTopology |
检查普通截线是否混合了开口和闭合 |
ProfilesInconsistent |
截线对应不一致,检查轮廓组织方式 |
Failed |
处理失败,保存输入以便定位 |
WrongUsage |
共用枚举中的点截线用法错误状态 |
Null3DCurve |
共用枚举中的缺少 3D 曲线状态 |
共用枚举列出这些值,不表示所有非法输入都一定被本类转换为相应状态;某些问题可能直接抛出 Standard_Failure。建议在应用边界保留异常日志和输入 BREP。
6.3 一个已运行的失败例子
随教程提供的拓扑不一致示例把一个开口线段 Wire 与一个闭合圆 Wire 传给接口,实际输出为:
IsDone=false; NotSameTopology=true
这时应停止后续使用,并修正截线组织方式。不要忽略状态后直接取 Shape();该访问函数自身不能代替状态检查。失败例子的运行记录
7 如何正确检查输出中的方向
7.1 遍历输出 Wire
#include <BRepTools_WireExplorer.hxx>
for (BRepTools_WireExplorer it(outputB); it.More(); it.Next()) {
const TopoDS_Edge& edge = it.Current();
const TopAbs_Orientation orientation = edge.Orientation();
// 这里得到的是该边在输出 Wire 中实际使用的方向。
}
对于图例这种“输入、输出仍是同一条未拆分 Edge”的情况,可以先确认 inputEdge.IsSame(outputEdge),再比较 Orientation() 是否相反。IsSame() 不检查方向;IsEqual() 还会检查方向。不要把这个局部技巧推广为拆边、多段或任意独立曲线的整体判向方法。
7.2 采样和切向必须考虑拓扑方向
BRepAdaptor_Curve 处理几何曲线与位置,但使用参数递增采样时仍需自己考虑 Edge 的遍历方向。图例中采用:
BRepAdaptor_Curve curve(edge);
double fraction = progress; // 0 到 1
if (edge.Orientation() == TopAbs_REVERSED) {
fraction = 1.0 - fraction;
}
double u = curve.FirstParameter()
+ fraction * (curve.LastParameter() - curve.FirstParameter());
gp_Pnt point = curve.Value(u);
如果还要求沿遍历方向的切向,求 D1 后对 REVERSED Edge 反转导数向量。尖点或零导数处不能强行构造 gp_Dir,应选择有定义的单侧方向,或把该位置标记为不可判定。
对于多段 Wire,还应沿连接次序处理每个分段。本例中的 201 点是绘图采样,不是严格证明整条曲线相同的连续区间检验。
8 边被拆分以后怎样找回对应关系
#include <TopTools_ListIteratorOfListOfShape.hxx>
const TopTools_ListOfShape& generated = compatible.GeneratedShapes(inputEdge);
for (TopTools_ListIteratorOfListOfShape it(generated); it.More(); it.Next()) {
TopoDS_Edge piece = TopoDS::Edge(it.Value());
// piece 是从 inputEdge 派生或沿用的结果边。
}
GeneratedShapes(inputEdge) 查询一条原边的派生边;Generated() 提供整张映射。列表未必只有一条,未绑定的原边可能返回空列表。不要假设列表中存储的方向、顺序,就等于结果 Wire 中的实际遍历方向。
如果需要给结果边着色、标注或关联业务属性,应结合派生映射和输出 Wire 的实际边出现位置进行匹配。算法对象销毁或重新 Init 前,要复制后续仍需使用的序列或映射;其中的 Shape 仍按 OCCT 的共享拓扑机制工作,并非自动深拷贝。
9 与放样 API 怎样配合
如果最终目的就是生成放样曲面或实体,可以使用 BRepOffsetAPI_ThruSections 的兼容处理开关。该高层 API 内部会在相应流程中使用本类。高层 API 实现
BRepOffsetAPI_ThruSections loft(Standard_False, Standard_False);
loft.CheckCompatibility(Standard_True);
loft.AddWire(wireA);
loft.AddWire(wireB);
loft.Build();
if (!loft.IsDone()) {
return;
}
TopoDS_Shape result = loft.Shape();
这个片段表示高层集成方式,未作为本教程的放样结果样例运行。使用它时仍需检查生成结果、预期边界及是否发生扭转。
如果已经单独调用 BRepFill_CompatibleWires 并人工确认输出对应关系,希望后续放样直接采用这些结果,可以在确认版本与业务约束后考虑关闭高层的重复兼容处理;关闭之后,输入兼容性由调用方承担。不要同时把“自动兼容”和“已验证符合具体构造意图”混为一谈。
10 针对整体空间走向应额外做什么
本接口解决的是放样截线的兼容问题。开口分支的定向规则仍主要依赖首尾信息;闭合分支通过轮廓点列等信息建立有限的对应。两条任意异位空间曲线若有内部回环、局部反向或形状差异,不能仅凭 IsDone() 判定它们全程同向。
如果业务要求的是“沿整段路径的走向是否一致”,应在接口之外定义全路径比较:保持世界坐标朝向、按需要消除平移,覆盖整段自适应采样,分别比较正序和逆序的有序匹配,并检查局部方向和匹配歧义。可以采用保留路径顺序的 Fréchet 匹配作为基础,同时设置几何误差、角度偏差及正反方案差距的阈值。Fréchet 距离说明
结果应保留 同向 / 反向 / 不可判定 三种状态。两种匹配都差或几乎难以区分时,不强行选较小者。闭合曲线还需处理起点相位,自交曲线需处理多解。这个额外判定方案尚未在本教程中实现,不能把已有的调向图例当作其验证结果。
11 编译运行完整示例
11.1 文件组织
BRepFill_CompatibleWires-tutorial/
├── tutorial.html
├── tutorial.md
├── assets/
│ ├── curve-orientation-before-after.png
│ └── interactive-example.html
├── examples/
│ ├── CMakeLists.txt
│ ├── compatible_wires_demo.cxx
│ └── incompatible_topology_demo.cxx
└── results/
├── native-run.log
├── mixed-topology-run.log
├── curve-orientation-data.json
└── 四个输入输出 BREP 文件
下载完整教程与示例包,或单独查看 CMake 配置。
11.2 构建
需要已安装的 OCCT C++ 开发环境、CMake 和支持 C++17 的编译器。在教程目录执行:
cmake -S examples -B build \
-DOpenCASCADE_DIR="/path/to/occt/lib/cmake/opencascade" \
-DCMAKE_BUILD_TYPE=Debug
cmake --build build --parallel 2
把示例路径替换为本机 OpenCASCADEConfig.cmake 所在目录。提供的 CMake 配置采用 OCCT 7.8.1 安装导出的 toolkit 目标名;其他安装包可能使用不同的目标命名方式,需要按其导出配置调整。
11.3 运行
./build/compatible_wires_demo run-results
./build/incompatible_topology_demo
第一条命令会自动创建 run-results,写入采样 JSON 和四个 BREP 文件。Windows 多配置生成器的可执行文件通常位于 build/Debug/,并带 .exe 后缀;运行时还要保证 OCCT 动态库及其依赖位于程序可搜索的路径。
本次实际构建运行记录如下:
OCCT 7.8.1
IsDone=true, valid=true
A FORWARD -> FORWARD
B REVERSED -> FORWARD
SameEdgeTShapes=true
Edges A 1 -> 1; B 1 -> 1
Demo chord dot=-1 -> 1
Sampled geometry displacement=2.14132e-15
Demo chord dot 仅用于当前已知平移同形样例的检查,不能移作一般曲线的整体判向逻辑。
11.4 最小接入检查
第一次替换为自己的模型时,保存输入与输出 BREP,画出按输出拓扑方向生成的箭头,再检查边数、派生映射、BRep 有效性和预期对应关系。若最终用于放样,还应检查实际生成的曲面或实体;这些检查分别对应不同结果,不能互相替代。
12 常见问题
已经调用成功,画面为什么没有变化? 可能仍在显示输入 Wire,也可能只画了曲线轮廓而未显示方向。采用 Shape() 返回的 Wire,并显示有向箭头。
两条 Edge 都是 FORWARD,为什么方向仍可能相反? 两条曲线各自的参数方向不同。枚举值只相对于各自的底层几何定义。
设置 Perform(false) 后为什么仍发生反向? 这个参数不禁止调整遍历方向,开口定向分支也不使用它。
能否只给两条 Edge? 先各自构造成单边 Wire,再加入序列;不要把 Edge 直接作为序列元素。
为什么有些结果不再 IsSame? 算法可能拆边或重建拓扑,应查看派生映射,而不是仅依赖原边身份。
输出有效,是否表示没有扭转、整体同向? BRep 有效性、兼容算法状态和符合业务构造意图是不同的检查。需要分别验证。
能否拿本接口自动修复任何空间曲线的方向? 适合把预计可对应的放样截线调整为兼容输入;对于任意曲线的全程方向判断,应另外实现明确的判定规则。
13 参考资料与版本
- 接口声明 V7_9_0:输入输出、参数和映射接口。
- 算法实现 V7_9_0:开闭分支、方向匹配和参数处理。
- 共用错误状态 V7_9_0:状态枚举。
- BRepOffsetAPI_ThruSections 实现 V7_9_0:高层放样与本类的关系。
- OCCT 建模数据文档:拓扑方向与几何参数方向。
- CGAL Fréchet 距离说明:保持路径顺序的曲线比较背景。
实跑环境为本机 OCCT 7.8.1。图例、成功样例、开闭混合失败样例均已执行;高层放样片段、任意曲线整体判向方案及其他版本行为不在本次运行验证范围内。