使用 OpenCASCADE 提取布尔运算后平面图形的外轮廓

在 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;
}
相关推荐
深耕AI13 小时前
MFC + OpenCV 图像预览显示不全中断问题解决:GDI行填充详解
c++·opencv·mfc
余辉zmh13 小时前
【C++篇】:ServiceBus RPC 分布式服务总线框架项目
开发语言·c++·rpc
水饺编程13 小时前
第3章,[标签 Win32] :窗口类03,窗口过程函数字段
c语言·c++·windows·visual studio
千里马-horse13 小时前
在android中 spdlog库的log如何在控制台上输出
android·c++·spdlog
aramae14 小时前
详细分析平衡树--红黑树(万字长文/图文详解)
开发语言·数据结构·c++·笔记·算法
再卷也是菜14 小时前
C++篇(13)计算器实现
c++·算法
_w_z_j_15 小时前
C++----变量存储空间
开发语言·c++
lingran__15 小时前
算法沉淀第五天(Registration System 和 Obsession with Robots)
c++·算法
莱茶荼菜15 小时前
一个坐标转换
c++·算法
guguhaohao16 小时前
list,咕咕咕!
数据结构·c++·list