在 CAD 建模和几何处理中,经常需要从复杂形状中提取外轮廓。本文将深入探讨如何使用 OpenCASCADE 库在 C++ 中高效提取布尔运算后平面图形的外轮廓,并提供完整可运行的代码示例。
理论基础
平面图形的数学表示
在计算几何中,一个平面图形可以表示为:
S={(x,y)∈R2∣f(x,y)≥0}S = \{ (x,y) \in \mathbb{R}^2 | f(x,y) \geq 0 \}S={(x,y)∈R2∣f(x,y)≥0}
其中 f(x,y)f(x,y)f(x,y) 是定义图形边界的隐式函数。对于通过布尔运算获得的图形,其边界由多个分段曲线组成:
∂S=⋃i=1nCi(t),t∈[ai,bi]\partial S = \bigcup_{i=1}^{n} C_i(t), \quad t \in [a_i, b_i]∂S=i=1⋃nCi(t),t∈[ai,bi]
其中 Ci(t)C_i(t)Ci(t) 是参数化的曲线段。
OpenCASCADE 拓扑结构
OpenCASCADE 使用 BREP(边界表示)模型表示几何形状,其拓扑结构包括:
- 顶点 (Vertex):0维几何元素
- 边 (Edge):1维几何元素,由曲线定义
- 线框 (Wire):边的有序连接
- 面 (Face):由线框界定的2维曲面
- 壳 (Shell):面的集合
- 实体 (Solid):封闭壳
对于平面图形,我们关注的是其外线框 (Outer Wire),即定义图形最外边界的线框。
完整代码实现
示例 1:基本外轮廓提取
cpp
#include <BRepTools.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Wire.hxx>
#include <gp_Circ.hxx>
#include <gp_Pln.hxx>
#include <gp_Ax2.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <iostream>
// 创建圆形平面
TopoDS_Shape CreateCircleFace(double radius) {
gp_Pln plane(gp::XOY());
gp_Circ circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), radius);
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(circle);
TopoDS_Wire wire = BRepBuilderAPI_MakeWire(edge);
return BRepBuilderAPI_MakeFace(wire);
}
// 创建矩形平面
TopoDS_Shape CreateRectangleFace(double width, double height) {
gp_Pnt p1(-width/2, -height/2, 0);
gp_Pnt p2(width/2, -height/2, 0);
gp_Pnt p3(width/2, height/2, 0);
gp_Pnt p4(-width/2, height/2, 0);
TopoDS_Edge e1 = BRepBuilderAPI_MakeEdge(p1, p2);
TopoDS_Edge e2 = BRepBuilderAPI_MakeEdge(p2, p3);
TopoDS_Edge e3 = BRepBuilderAPI_MakeEdge(p3, p4);
TopoDS_Edge e4 = BRepBuilderAPI_MakeEdge(p4, p1);
TopoDS_Wire wire = BRepBuilderAPI_MakeWire(e1, e2, e3, e4);
return BRepBuilderAPI_MakeFace(wire);
}
// 获取形状的外轮廓
TopoDS_Wire GetOuterWire(const TopoDS_Shape& shape) {
if (shape.IsNull()) {
throw std::runtime_error("输入形状为空");
}
TopExp_Explorer faceExplorer(shape, TopAbs_FACE);
if (!faceExplorer.More()) {
throw std::runtime_error("形状中未找到面");
}
const TopoDS_Face& face = TopoDS::Face(faceExplorer.Current());
TopoDS_Wire outerWire = BRepTools::OuterWire(face);
if (outerWire.IsNull()) {
throw std::runtime_error("提取外轮廓失败");
}
return outerWire;
}
int main() {
try {
// 创建几何图形
TopoDS_Shape circle = CreateCircleFace(50.0);
TopoDS_Shape rectangle = CreateRectangleFace(40.0, 40.0);
// 执行布尔运算
BRepAlgoAPI_Cut cutter(circle, rectangle);
cutter.Build();
if (!cutter.IsDone()) {
throw std::runtime_error("布尔运算失败");
}
// 获取结果形状
TopoDS_Shape resultShape = cutter.Shape();
// 提取外轮廓
TopoDS_Wire outerWire = GetOuterWire(resultShape);
// 输出结果
std::cout << "成功提取外轮廓" << std::endl;
std::cout << "轮廓包含 " << outerWire.NbChildren() << " 条边" << std::endl;
} catch (const std::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
return 1;
}
return 0;
}
示例 2:高级外轮廓处理

cpp
#include <BRepTools.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Wire.hxx>
#include <TopTools_ListOfShape.hxx>
#include <gp_Circ.hxx>
#include <gp_Pln.hxx>
#include <gp_Ax2.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <BRepCheck_Analyzer.hxx>
#include <iostream>
#include <vector>
// 创建圆形平面
TopoDS_Shape CreateCircleFace(double radius) {
gp_Pln plane(gp::XOY());
gp_Circ circle(gp_Ax2(gp_Pnt(0, 0, 0), gp_Dir(0, 0, 1)), radius);
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(circle);
TopoDS_Wire wire = BRepBuilderAPI_MakeWire(edge);
return BRepBuilderAPI_MakeFace(wire);
}
// 创建多边形平面
TopoDS_Shape CreatePolygonFace(const std::vector<gp_Pnt>& points) {
if (points.size() < 3) {
throw std::runtime_error("多边形至少需要3个点");
}
BRepBuilderAPI_MakeWire wireMaker;
for (size_t i = 0; i < points.size(); i++) {
size_t next = (i + 1) % points.size();
TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(points[i], points[next]);
wireMaker.Add(edge);
}
if (!wireMaker.IsDone()) {
throw std::runtime_error("创建线框失败");
}
return BRepBuilderAPI_MakeFace(wireMaker.Wire());
}
// 获取所有外轮廓
TopTools_ListOfShape GetAllOuterWires(const TopoDS_Shape& shape) {
TopTools_ListOfShape resultWires;
// 验证形状有效性
BRepCheck_Analyzer analyzer(shape);
if (!analyzer.IsValid()) {
throw std::runtime_error("形状拓扑结构无效");
}
// 遍历所有面
for (TopExp_Explorer exp(shape, TopAbs_FACE); exp.More(); exp.Next()) {
TopoDS_Face face = TopoDS::Face(exp.Current());
// 获取面的外轮廓
TopoDS_Wire outerWire = BRepTools::OuterWire(face);
if (!outerWire.IsNull()) {
resultWires.Append(outerWire);
}
// 可选:获取内轮廓(孔洞)
/*
TopExp_Explorer wireExp(face, TopAbs_WIRE);
for (; wireExp.More(); wireExp.Next()) {
TopoDS_Wire wire = TopoDS::Wire(wireExp.Current());
if (!wire.IsSame(outerWire)) {
// 处理内轮廓
}
}
*/
}
return resultWires;
}
int main() {
try {
// 创建复杂几何图形
std::vector<gp_Pnt> hexPoints = {
gp_Pnt(0, 0, 0),
gp_Pnt(30, 0, 0),
gp_Pnt(45, 25, 0),
gp_Pnt(30, 50, 0),
gp_Pnt(0, 50, 0),
gp_Pnt(-15, 25, 0)
};
TopoDS_Shape hexagon = CreatePolygonFace(hexPoints);
TopoDS_Shape circle = CreateCircleFace(25.0);
// 执行布尔并集运算
BRepAlgoAPI_Fuse fuser(hexagon, circle);
fuser.Build();
if (!fuser.IsDone()) {
throw std::runtime_error("布尔并集运算失败");
}
// 获取结果形状
TopoDS_Shape fusedShape = fuser.Shape();
// 提取所有外轮廓
TopTools_ListOfShape outerWires = GetAllOuterWires(fusedShape);
// 输出结果
std::cout << "找到 " << outerWires.Extent() << " 个外轮廓" << std::endl;
// 遍历所有外轮廓
int wireCount = 1;
for (TopTools_ListIteratorOfListOfShape it(outerWires); it.More(); it.Next()) {
TopoDS_Wire wire = TopoDS::Wire(it.Value());
std::cout << "轮廓 " << wireCount++ << " 包含 "
<< wire.NbChildren() << " 条边" << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
return 1;
}
return 0;
}