惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
AI
AI
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
C
Cybersecurity and Infrastructure Security Agency CISA
T
Threat Research - Cisco Blogs
T
Tenable Blog
G
GRAHAM CLULEY
腾讯CDC
L
Lohrmann on Cybersecurity
Martin Fowler
Martin Fowler
博客园 - 【当耐特】
The Cloudflare Blog
P
Proofpoint News Feed
V
Visual Studio Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
AWS News Blog
AWS News Blog
D
Docker
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Latest news
Latest news
L
LINUX DO - 热门话题
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
L
LangChain Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
W
WeLiveSecurity
Blog — PlanetScale
Blog — PlanetScale
云风的 BLOG
云风的 BLOG
P
Privacy International News Feed
Cloudbric
Cloudbric
Attack and Defense Labs
Attack and Defense Labs
量子位
爱范儿
爱范儿
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
V
Vulnerabilities – Threatpost
Schneier on Security
Schneier on Security
J
Java Code Geeks
S
Security @ Cisco Blogs
N
News and Events Feed by Topic
N
Netflix TechBlog - Medium
博客园 - 司徒正美
I
InfoQ
WordPress大学
WordPress大学
GbyAI
GbyAI
Google DeepMind News
Google DeepMind News

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第17章:与 Qt VTK Python pythonOCC 生态集成 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第18章:二次开发实战与综合案例
我才是银古 · 2026-05-06 · via 博客园 - 我才是银古

第18章:二次开发实战与综合案例

前 17 章把 OCCT 的“技术栈”一一拆解。本章以一个综合案例把它们串起来:实现一个简化版的“参数化机械零件查看器”,覆盖建模、可视化、数据交换、OCAF、Draw 测试与部署。读者可以把它当作动手项目,逐步扩展为自己的 CAD 应用。

1. 案例需求

  • 用 OCCT 构造一个参数化“法兰盘”模型;
  • 提供 Qt 视图,可旋转/缩放,支持面拾取;
  • 参数(外径、内径、孔数、孔径、厚度)可在 UI 修改;
  • 模型可导出 STEP、glTF、STL;
  • 用 OCAF 维护参数与撤销/重做;
  • 用 Draw 脚本编写自动化回归测试。

2. 工程结构

flange-app/
  CMakeLists.txt
  src/
    main.cpp
    MainWindow.cpp/.hpp
    OcctView.cpp/.hpp
    FlangeBuilder.cpp/.hpp
    FlangeDocument.cpp/.hpp
  tests/
    flange.tcl
  resources/

依赖:Qt6、OpenCASCADE 7.8+、CMake 3.22+。

3. 参数化建模(FlangeBuilder)

struct FlangeParams {
    double outerDiameter = 120.0;
    double innerDiameter = 40.0;
    double thickness = 20.0;
    int    boltCount = 6;
    double boltDiameter = 10.0;
    double boltCircle = 80.0;
};

TopoDS_Shape FlangeBuilder::Build(const FlangeParams& p) {
    gp_Ax2 ax(gp_Pnt(0,0,0), gp_Dir(0,0,1));
    TopoDS_Shape body = BRepPrimAPI_MakeCylinder(ax, p.outerDiameter/2, p.thickness).Shape();
    TopoDS_Shape hole = BRepPrimAPI_MakeCylinder(ax, p.innerDiameter/2, p.thickness + 2).Shape();
    TopoDS_Shape r1 = BRepAlgoAPI_Cut(body, hole);

    BRep_Builder bb; TopoDS_Compound bolts; bb.MakeCompound(bolts);
    for (int i = 0; i < p.boltCount; ++i) {
        double a = 2 * M_PI * i / p.boltCount;
        gp_Ax2 axb(gp_Pnt(p.boltCircle/2 * std::cos(a),
                          p.boltCircle/2 * std::sin(a), -1),
                   gp_Dir(0,0,1));
        bb.Add(bolts, BRepPrimAPI_MakeCylinder(axb, p.boltDiameter/2, p.thickness + 2).Shape());
    }
    TopoDS_Shape r2 = BRepAlgoAPI_Cut(r1, bolts);

    BRepFilletAPI_MakeFillet f(r2);
    for (TopExp_Explorer ex(r2, TopAbs_EDGE); ex.More(); ex.Next()) {
        gp_Pnt mid; double u1, u2;
        BRepAdaptor_Curve c(TopoDS::Edge(ex.Current()));
        mid = c.Value(0.5*(c.FirstParameter()+c.LastParameter()));
        if (std::abs(mid.Z() - p.thickness) < 1e-3 || std::abs(mid.Z()) < 1e-3) {
            f.Add(1.0, TopoDS::Edge(ex.Current()));
        }
    }
    f.Build();
    return f.IsDone() ? f.Shape() : r2;
}

要点:

  • 解析体优先(MakeCylinder);
  • 用 Compound 收集所有螺栓孔,一次 Cut
  • 圆角通过几何位置筛选边;
  • IsDone() 检查后退回上一步。

4. OCAF 文档(FlangeDocument)

class FlangeDocument {
public:
    void New();                // 创建空文档
    void SetParams(const FlangeParams&);
    FlangeParams GetParams() const;
    void Recompute();          // 重算并写入 NamedShape
    void Save(const TCollection_AsciiString& path);
    void Open(const TCollection_AsciiString& path);
    void Undo(); void Redo();

    Handle(TDocStd_Document) Doc() const { return myDoc; }
    TDF_Label ResultLabel() const { return myDoc->Main().FindChild(1, true); }
private:
    Handle(TDocStd_Document) myDoc;
};

实现要点:

  • TDataStd_Real 保存每个参数到独立子 label;
  • Recompute 中调用 BRepAlgoAPI 等并 TNaming_Builder::Generated(shape) 写结果;
  • NewCommand / CommitCommand 包裹每次修改;
  • BinXCAF 格式保存,便于以后扩展 XCAF 颜色/层。

5. 视图(OcctView)

继承 QWidget

class OcctView : public QWidget {
public:
    OcctView(const Handle(AIS_InteractiveContext)& ctx, QWidget* parent = nullptr);
protected:
    QPaintEngine* paintEngine() const override { return nullptr; }
    void paintEvent(QPaintEvent*) override { myView->Redraw(); }
    void resizeEvent(QResizeEvent*) override { myView->MustBeResized(); }
    void mousePressEvent(QMouseEvent*) override;
    // ... 鼠标事件
private:
    Handle(V3d_View) myView;
    Handle(AIS_InteractiveContext) myContext;
};

复用 OCCT 7.5+ 的 AIS_ViewController 来处理鼠标交互(节省大量代码)。

6. 主窗口(MainWindow)

  • 左侧 QFormLayout 展示参数控件(QDoubleSpinBox 等);
  • 中间 OcctView
  • 顶部菜单:新建、打开、保存、导出 STEP/glTF/STL、撤销、重做;
  • 状态栏:拾取面信息、几何统计;
  • 参数变化 → FlangeDocument::SetParams → Recompute → AIS Refresh

7. 数据交换菜单

void MainWindow::exportStep() {
    STEPControl_Writer w;
    w.Transfer(myDoc->Result(), STEPControl_AsIs);
    w.Write("flange.step");
}
void MainWindow::exportGltf() {
    BRepMesh_IncrementalMesh(myDoc->Result(), 0.05, false, 0.3, true);
    Handle(TDocStd_Document) tmp;
    XCAFApp_Application::GetApplication()->NewDocument("BinXCAF", tmp);
    auto st = XCAFDoc_DocumentTool::ShapeTool(tmp->Main());
    st->AddShape(myDoc->Result(), false);
    RWGltf_CafWriter w("flange.glb", true);
    TColStd_IndexedDataMapOfStringString info;
    info.Add("Generator", "FlangeApp");
    w.Perform(tmp, info, Message_ProgressRange());
}
void MainWindow::exportStl() {
    BRepMesh_IncrementalMesh(myDoc->Result(), 0.05);
    StlAPI_Writer w; w.ASCIIMode() = false;
    w.Write(myDoc->Result(), "flange.stl");
}

8. 拾取与高亮

ctx->Activate(ais, /*FACE=*/4);
// MouseRelease:
ctx->MoveTo(pos.x(), pos.y(), view, true);
ctx->Select(false);
for (ctx->InitSelected(); ctx->MoreSelected(); ctx->NextSelected()) {
    TopoDS_Shape s = ctx->SelectedShape();
    if (!s.IsNull() && s.ShapeType() == TopAbs_FACE) {
        // 显示属性、面积...
        GProp_GProps g;
        BRepGProp::SurfaceProperties(s, g);
        statusBar()->showMessage(QString("Area=%1").arg(g.Mass()));
    }
}

9. Draw 回归测试

tests/flange.tcl

pload ALL
source flange_lib.tcl
flange f 120 40 20 6 10 80
checkprops f -v 169646
checkprops f -s 38800
checkmaxtol f 1e-3

flange_lib.tcl 通过自定义 plugin 暴露 C++ FlangeBuilder::Build,或直接用 Tcl 拼装算法命令。运行:

DRAWEXE -batch -script flange.tcl

CI 中执行返回码即可作为测试通过/失败标识。

10. 性能与优化

  • 大量螺栓孔时改用一次 BOP,并 SetRunParallel(true)
  • 参数变更但拓扑不变时,只更新 Location
  • AIS 显示模式按用户视图分级:远视 wireframe,近视 shaded;
  • 启用 BRepMesh 缓存:参数变化才 BRepTools::Clean

11. 部署

  • Linux:CMake install;用 cqtdeployer 或自制脚本拷贝 OCCT、Qt、resources/
  • Windows:使用 windeployqt + 手动拷贝 TK*.dll
  • macOS:macdeployqt + install_name_tool
  • Docker:基于 ubuntu:24.04 构建,开放 X 转发或 noVNC 访问;
  • Web:可把核心算法编译为 WASM,前端用 Three.js 显示导出的 glTF。

12. 扩展方向

  • 增加更多零件库(轴承、齿轮、螺栓),每个零件单独的 IBuilder
  • TFunction 把零件和装配的依赖关系自动求解;
  • 与 ERP/PLM 集成:把 OCAF XCAF 中的元数据映射到企业系统;
  • 加入仿真前处理:基于 OCCT 网格生成 SMESH 或 CalculiX 输入;
  • 用 LLM 自动从自然语言生成 FlangeParams,再构造模型;
  • 支持插件机制:用 Plugin_Map 加载第三方零件库。

13. 学习反馈循环

完成本案例后,建议按以下循环深耕:

  1. 在仓库 tests/ 中找到与你算法相关的 grid,跑一遍熟悉行为;
  2. dox/user_guides/ 中找到对应章节,对照源码复习;
  3. 在 OCCT bugtracker / 论坛搜索类似问题;
  4. 写自己的 Draw 命令,做参数扫描;
  5. 阅读关键算法源码(BOPAlgo_PaveFillerChFi3d_BuilderBRepFill_Pipe);
  6. 把改进/修复贡献回 OCCT。

14. 结语

OCCT 是开源 CAD 内核中最完整的一个:从最底层的数学到最上层的应用框架,几乎所有 CAD 软件需要的能力它都有。本教程带你走过 18 章,从全景到底层,从底层回到综合实战。真正掌握 OCCT 需要在每个章节涉及的源码、文档、测试中反复实践——而它给你的回报是:你具备了构建任何 CAD 应用所需的几何与拓扑基础设施。

愿你在 OCCT 的世界里既能解决工程问题,也能享受几何之美。