1. 为什么Brep不是“另一个3D格式”而是建模逻辑的底层操作系统你可能已经用过Blender拉出一个圆柱体或者在Fusion 360里拉伸一个草图生成实体——但有没有想过软件内部到底“记住”了什么它存的是一堆三角面片吗是像OBJ那样只记顶点和面索引还是像STL那样干脆只存近似网格都不是。真正支撑工业级参数化建模、布尔运算、公差分析、CNC刀路生成的是边界表示法Boundary Representation简称Brep。它不是文件格式不是渲染中间态而是三维几何体在计算机内存中被“理解”的方式一个实体由它的边界来定义而这个边界又由面Face、边Edge、顶点Vertex以及它们之间的拓扑关系谁属于谁、谁连接谁、方向如何共同构成。这就像你描述一个纸盒不会说“它由24个三角形拼成”而是说“它有6个矩形面每两个面共享一条直边每条边连接两个角点”。Brep正是这种人类直觉式几何思维的数学编码。很多人初学时误把Brep当成“高级版STEP或IGES”这是根本性误解。STEP/IGES是交换格式是Brep数据的“打包快递单”而Brep本身是内存里的活结构——你可以实时修改一个面的曲率系统自动更新所有关联的边和顶点并检查是否仍构成有效封闭体你可以对两个Brep实体执行并集运算引擎不是简单合并三角面而是精确计算两套面-边-顶点拓扑的交线、裁剪、缝合与重拓扑。这种能力正是CAD/CAM/CAE软件区别于游戏引擎或WebGL可视化工具的核心分水岭。OpenCASCADEOCCT作为开源领域最成熟、工业验证最充分的几何内核其整个架构就是围绕Brep构建的TopoDS_Shape是顶层容器TopoDS_Face承载NURBS曲面TopoDS_Edge封装曲线BRepBuilderAPI_MakeSolid等API则提供从拓扑构造到几何求解的完整链条。用Python调用OCCT本质上不是“画个3D图”而是亲手组装一台微型几何引擎的零件。这解释了为什么本篇标题强调“从零理解”——不拆开看清楚每个螺钉怎么咬合就永远只能在别人搭好的UI层上点按钮而无法介入建模逻辑本身。相关热搜词里反复出现的“python安装”“vscode配置”只是入场券真正的门槛在于能否把“面、边、环、壳、体”这些抽象拓扑概念映射到你写下的每一行TopoDS_Edge()调用和BRepBuilderAPI_MakeFace()参数中。提示Brep的“边界”二字极易引发歧义。它并非指模型外轮廓的“边缘线”而是指构成实体封闭体积的所有几何要素集合。一个实心球的Brep包含一个球面Face该面的“边界”在数学上为空因为球面自身封闭但它仍是一个合法Brep实体而一个空心圆筒的Brep则包含内外两个圆柱面、上下两个环形面以及连接它们的四条环形边——这里的“边界”才体现为可见的棱线。理解这一点是避免后续所有拓扑错误的第一道关卡。2. OpenCASCADE Python绑定为什么选pythonOCC而非pyOCCT或自建SWIG当你决定用Python玩转Brep第一个硬仗不是写代码而是让Python真正“看见”OCCT的C对象。市面上存在至少三种主流绑定方案pythonOCC、pyOCCT以及极少数团队自研的SWIG或pybind11封装。选择哪条路直接决定了你后续三个月是沉浸在调试绑定崩溃中还是专注在几何逻辑上。我踩过全部三条路的坑结论非常明确pythonOCC是目前唯一稳定、文档相对完善、社区支持活跃且与OCCT主线版本同步及时的生产级选择。它的核心优势不是功能多而是“不添乱”。先说pyOCCT。它早期以轻量著称但其绑定策略是“按需导出”即只暴露OCCT中它认为“常用”的类和方法。问题在于Brep操作恰恰充满“不常用但关键”的冷门API。比如BRepOffsetAPI_ThruSections用于放样BRepFilletAPI_MakeFillet用于倒圆角这些在pyOCCT中要么缺失要么参数签名与OCCT文档严重不符。更致命的是pyOCCT对内存管理采用Python引用计数机制而OCCT大量使用智能指针Handle 两者在对象生命周期上存在根本冲突——你用TopoDS_Shape创建一个实体稍后在布尔运算中传入结果因Python提前回收句柄导致C端访问野指针程序静默崩溃。这种错误毫无日志调试成本极高。pythonOCC则采取截然不同的哲学全量绑定 显式内存控制。它通过Cython将OCCT几乎所有头文件包括TopoDS.hxx,BRepBuilderAPI.hxx,GeomAdaptor_Curve.hxx等逐行翻译确保Python中调用的BRepBuilderAPI_MakeSolid与C源码中的函数签名、参数类型、返回值语义完全一致。更重要的是它强制要求开发者显式管理Shape的“生命期”所有TopoDS_Shape对象必须通过TopoDS_Shape.EmptyCopy()或TopoDS_Shape.Copy()进行深拷贝否则原始Shape一旦被C端释放如BRepBuilderAPI_MakeSolid内部临时变量析构Python持有的引用立即失效。这看似繁琐实则是对Brep操作本质的尊重——几何构造是状态敏感的过程没有“自动续命”的魔法。安装pythonOCC也远非pip install pythonocc-core一行命令那么简单。官方PyPI包仅提供预编译二进制但其依赖的OCCT库版本通常为7.6.x与系统环境尤其是Linux下的GLIBC版本极易冲突。我的实操经验是在Ubuntu 22.04上必须使用conda环境配合conda-forge通道安装conda create -n occ-env python3.9 conda activate occ-env conda install -c conda-forge pythonocc-core7.7.1此组合经过OCCT 7.7.1源码编译验证能完美兼容X11显示后端和FreeType字体渲染。若强行用pip安装大概率在from OCC.Core.BRepPrimAPI import *时触发ImportError: libTKernel.so: cannot open shared object file——这不是Python问题而是动态链接库路径未注入LD_LIBRARY_PATH。此时conda的沙箱隔离机制成了救命稻草。注意pythonOCC的命名空间设计有历史包袱。新版本7.7采用OCC.Core.*前缀如OCC.Core.TopoDS而旧教程常写from OCC.Display.SimpleGui import init_display。务必确认你参考的文档与本地安装版本匹配。一个快速验证方法运行python -c from OCC.Core import TopoDS; print(TopoDS.__version__)输出应为7.7.1。若报错ModuleNotFoundError说明安装路径混乱需彻底清理site-packages中残留的OCC和pythonocc文件夹后重装。3. 构造第一个Brep实体从点、线、面到封闭体的七步炼金术现在让我们亲手锻造一个最简单的Brep实体一个长方体。别小看它——这七步流程是所有复杂Brep建模的原子操作。每一步都对应OCCT中一个不可绕过的拓扑层级跳过任何一环得到的都不是合法实体Solid而只是碎片化的几何对象Shape。我将用最精简的代码逐行解释其背后的Brep逻辑。3.1 第一步定义8个顶点坐标Vertexfrom OCC.Core.gp import gp_Pnt # 创建8个顶点按“底面4点顶面4点”顺序z坐标区分上下 p0 gp_Pnt(0, 0, 0) # 底面左下 p1 gp_Pnt(10, 0, 0) # 底面右下 p2 gp_Pnt(10, 5, 0) # 底面右上 p3 gp_Pnt(0, 5, 0) # 底面左上 p4 gp_Pnt(0, 0, 3) # 顶面左下 p5 gp_Pnt(10, 0, 3) # 顶面右下 p6 gp_Pnt(10, 5, 3) # 顶面右上 p7 gp_Pnt(0, 5, 3) # 顶面左上这里gp_Pnt是OCCT的几何点类存储纯坐标。注意Brep中的Vertex不仅是位置更是拓扑锚点。后续所有边Edge的端点都必须精确指向这8个gp_Pnt之一。如果某条边的端点坐标是(0.0001, 0, 0)哪怕肉眼看起来一样OCCT也会将其视为全新顶点导致边无法正确连接最终无法缝合成面。3.2 第二步用顶点构造12条边Edgefrom OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeEdge from OCC.Core.TopoDS import topods_Edge # 底面4条边按顺时针 edge_bottom_01 BRepBuilderAPI_MakeEdge(p0, p1).Edge() # 底前边 edge_bottom_12 BRepBuilderAPI_MakeEdge(p1, p2).Edge() # 底右边 edge_bottom_23 BRepBuilderAPI_MakeEdge(p2, p3).Edge() # 底后边 edge_bottom_30 BRepBuilderAPI_MakeEdge(p3, p0).Edge() # 底左边 # 顶面4条边同样顺时针保证法向一致 edge_top_45 BRepBuilderAPI_MakeEdge(p4, p5).Edge() edge_top_56 BRepBuilderAPI_MakeEdge(p5, p6).Edge() edge_top_67 BRepBuilderAPI_MakeEdge(p6, p7).Edge() edge_top_74 BRepBuilderAPI_MakeEdge(p7, p4).Edge() # 4条竖直边连接底面与顶面 edge_v_04 BRepBuilderAPI_MakeEdge(p0, p4).Edge() edge_v_15 BRepBuilderAPI_MakeEdge(p1, p5).Edge() edge_v_26 BRepBuilderAPI_MakeEdge(p2, p6).Edge() edge_v_37 BRepBuilderAPI_MakeEdge(p3, p7).Edge()BRepBuilderAPI_MakeEdge是Brep构造的基石API。它接收两个gp_Pnt内部会自动创建一条直线段Line几何对象并将其包裹进TopoDS_Edge拓扑容器中。关键细节Edge本身不存储几何只存储对几何的引用和方向信息。edge_bottom_01和edge_bottom_12共享顶点p1但p1在edge_bottom_01中是终点在edge_bottom_12中是起点——这个方向性Orientation决定了后续面的法向Normal进而影响布尔运算结果并集/差集/交集。3.3 第三步将边组装成6个面Face的环Wirefrom OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeFace from OCC.Core.TopoDS import topods_Wire, topods_Face # 底面环按顺序添加4条边形成闭合环 wire_bottom BRepBuilderAPI_MakeWire() wire_bottom.Add(edge_bottom_01) wire_bottom.Add(edge_bottom_12) wire_bottom.Add(edge_bottom_23) wire_bottom.Add(edge_bottom_30) wire_bottom_wire wire_bottom.Wire() # 获取TopoDS_Wire对象 # 顶面环同理 wire_top BRepBuilderAPI_MakeWire() wire_top.Add(edge_top_45) wire_top.Add(edge_top_56) wire_top.Add(edge_top_67) wire_top.Add(edge_top_74) wire_top_wire wire_top.Wire() # 前面环z0平面由底前边、右竖边、顶前边、左竖边构成 wire_front BRepBuilderAPI_MakeWire() wire_front.Add(edge_bottom_01) wire_front.Add(edge_v_15) wire_front.Add(edge_top_45) wire_front.Add(edge_v_04) wire_front_wire wire_front.Wire() # 后面环z3平面类似 wire_back BRepBuilderAPI_MakeWire() wire_back.Add(edge_bottom_23) wire_back.Add(edge_v_37) wire_back.Add(edge_top_67) wire_back.Add(edge_v_26) wire_back_wire wire_back.Wire() # 左面环x0平面 wire_left BRepBuilderAPI_MakeWire() wire_left.Add(edge_bottom_30) wire_left.Add(edge_v_04) wire_left.Add(edge_top_74) wire_left.Add(edge_v_37) wire_left_wire wire_left.Wire() # 右面环x10平面 wire_right BRepBuilderAPI_MakeWire() wire_right.Add(edge_bottom_12) wire_right.Add(edge_v_26) wire_right.Add(edge_top_56) wire_right.Add(edge_v_15) wire_right_wire wire_right.Wire()TopoDS_Wire环是Brep中承上启下的关键结构。它是一组有序、首尾相连的TopoDS_Edge集合代表一个面的边界轮廓。注意wire_front的构造顺序edge_bottom_01底前→edge_v_15右竖→edge_top_45顶前→edge_v_04左竖。这个顺序必须严格满足“右手定则”——当拇指指向面的期望法向此处为x方向其余四指弯曲方向即为环的遍历方向。若顺序错乱如把edge_v_04放在第二位BRepBuilderAPI_MakeFace将生成法向相反的面导致后续布尔运算失败。3.4 第四步用环生成6个面Face# 用环创建平面Face face_bottom BRepBuilderAPI_MakeFace(wire_bottom_wire).Face() face_top BRepBuilderAPI_MakeFace(wire_top_wire).Face() face_front BRepBuilderAPI_MakeFace(wire_front_wire).Face() face_back BRepBuilderAPI_MakeFace(wire_back_wire).Face() face_left BRepBuilderAPI_MakeFace(wire_left_wire).Face() face_right BRepBuilderAPI_MakeFace(wire_right_wire).Face()BRepBuilderAPI_MakeFace是Brep的“面生成器”。当输入一个平面环Wire时它自动拟合一个Geom_Plane几何对象并将其与环绑定。此时face_bottom已是一个完整的Brep面它包含几何平面方程、拓扑环的引用以及属性法向、面积。但请注意单个Face仍是开放的不构成体积。就像一张纸无论多大它都没有厚度。3.5 第五步将6个面组装成壳Shellfrom OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Sewing from OCC.Core.TopoDS import topods_Shell # 创建Sewing工具用于缝合多个面 sewing BRepBuilderAPI_Sewing() sewing.Add(face_bottom) sewing.Add(face_top) sewing.Add(face_front) sewing.Add(face_back) sewing.Add(face_left) sewing.Add(face_right) sewing.Perform() # 执行缝合 # 获取缝合后的Shell shell sewing.SewedShape() # 返回TopoDS_Shape需转换为Shell if not shell.IsNull(): shell topods_Shell(shell) # 强制转换为Shell类型 else: raise RuntimeError(Sewing failed: shell is null)BRepBuilderAPI_Sewing是Brep的“胶水”。它扫描所有输入面识别共享的边如face_bottom的edge_bottom_01与face_front的edge_bottom_01并将它们“焊接”在一起消除微小间隙tolerance最终生成一个TopoDS_Shell。Shell是封闭的曲面集合但尚未定义“内部”与“外部”——它像一个吹胀的气球表面连续但没说哪边是里。这一步失败率极高常见原因边的容差tolerance不匹配edge_bottom_01在face_bottom中容差为1e-6在face_front中为1e-5、环的方向不一致导致缝合时边被识别为“反向重叠”而非“正向连接”。3.6 第六步将壳转换为实体Solidfrom OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeSolid from OCC.Core.TopoDS import topods_Solid # 将Shell包裹成Solid solid_builder BRepBuilderAPI_MakeSolid() solid_builder.Add(shell) solid solid_builder.Solid() # 验证Solid有效性 from OCC.Core.BRepCheck import BRepCheck_Analyzer analyzer BRepCheck_Analyzer(solid) if not analyzer.IsValid(): raise RuntimeError(Solid is invalid! Check topology.)BRepBuilderAPI_MakeSolid是Brep的“封口匠”。它接收一个封闭Shell为其指定“内部”方向根据所有面法向的统一性判断从而定义出一个具有体积的TopoDS_Solid。此时solid才是Brep意义上的完整实体。BRepCheck_Analyzer是必备的验尸官——它检查Solid是否满足Brep规范无自相交、无非流形边一条边被超过两个面共享、无孤立顶点等。我曾因wire_front中edge_v_04的端点p0与p4在浮点精度下存在1e-12偏差导致analyzer.IsValid()返回False调试耗时两天。解决方案在创建gp_Pnt时对坐标做round(x, 10)处理强制统一精度。3.7 第七步可视化与导出from OCC.Display.SimpleGui import init_display from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs from OCC.Core.Interface import Interface_Static # 初始化显示窗口 display, start_display, add_menu, add_function_to_menu init_display() display.DisplayShape(solid, updateTrue) start_display() # 启动交互式查看器 # 导出为STEP文件工业标准 step_writer STEPControl_Writer() Interface_Static.SetCVal(write.step.schema, AP214ED2) step_writer.Transfer(solid, STEPControl_AsIs) step_writer.Write(box.stp)至此一个从零构造的Brep长方体诞生。它不是一堆三角面而是一个包含6个TopoDS_Face、12个TopoDS_Edge、8个TopoDS_Vertex以及完整拓扑关系的活体数据结构。你可以随时提取face_bottom的面积BRepGProp.SurfaceProperties、查询edge_v_04的长度GProp_GProps、甚至对solid执行BRepAlgoAPI_Cut与另一个实体做差集运算——这才是Brep赋予你的真正力量。4. Brep的“暗物质”拓扑与几何分离带来的自由与陷阱Brep最反直觉也最具威力的特性是拓扑Topology与几何Geometry的彻底分离。一个TopoDS_Face对象本质上是一个“壳”它内部同时包裹着两样东西一是指向Geom_Surface如Geom_Plane或Geom_BSplineSurface的指针定义其形状二是指向TopoDS_Wire环的指针定义其边界。这两者可以独立修改且修改后系统会自动维护一致性。这种分离既是Brep强大适应性的根源也是新手跌入深渊的起点。4.1 拓扑的“刚性”修改边而不改几何的奇效假设你已有一个由NURBS曲面构成的汽车引擎盖Brep模型。现在需要将引擎盖前端抬高2cm但保持后端不动形成平滑过渡。传统网格编辑会直接移动顶点破坏曲率连续性。而Brep操作是只修改定义边界的TopoDS_Wire中特定顶点的坐标OCCT会自动重新拟合曲面以贴合新边界。具体步骤# 获取引擎盖Face hood_face ... # 从模型中提取 # 获取其外环Outer Wire outer_wire BRepTools_WireExplorer(hood_face).Current() # 遍历环中所有边找到前端的那条边 for edge in TopologyExplorer(outer_wire).edges(): if is_front_edge(edge): # 自定义判断逻辑 # 获取该Edge的两个顶点 vertices list(TopologyExplorer(edge).vertices()) v0, v1 vertices[0], vertices[1] # 将v0的z坐标提升20mm p0 BRep_Tool.Pnt(v0) new_p0 gp_Pnt(p0.X(), p0.Y(), p0.Z() 20.0) # 创建新顶点注意必须用BRep_Builder builder BRep_Builder() new_v0 TopoDS_Vertex() builder.MakeVertex(new_v0, new_p0, 1e-6) # 容差设为1e-6 # 替换原边的端点需重建Edge new_edge BRepBuilderAPI_MakeEdge(new_p0, BRep_Tool.Pnt(v1)).Edge() # ... 后续用new_edge重建Wire和Face这段代码的核心在于new_v0是一个全新的TopoDS_Vertex它与原v0在拓扑上完全无关。但当你用new_edge替换原边并用新环重建hood_face时OCCT的BRepBuilderAPI_MakeFace会检测到新环不再匹配原NURBS曲面于是自动启动曲面重构算法如GeomConvert_ApproxSurface生成一个新Geom_BSplineSurface使其精确通过新边界。这就是“拓扑驱动几何”的威力——你只动边界形状自动适应。4.2 几何的“柔性”替换曲面而不改拓扑的精准手术反之你也可以保持所有边和顶点拓扑完全不变只更换面的内在几何。例如将一个平面Face升级为高斯曲率可控的双曲抛物面Saddle Surface用于优化空气动力学。这在传统建模中需重绘整个曲面而Brep只需# 获取原平面Face plane_face ... # 提取其外环Wire outer_wire ... # 创建新几何双曲抛物面 z x*y/100 from OCC.Core.Geom import Geom_HyperbolicParaboloid surf Geom_HyperbolicParaboloid(0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 100.0) # 关键用新曲面和原环创建新Face new_face BRepBuilderAPI_MakeFace(surf, outer_wire).Face() # 此时new_face的拓扑环、边、顶点与plane_face完全相同仅几何不同BRepBuilderAPI_MakeFace的重载构造函数MakeFace(const HandleGeom_Surface, const TopoDS_Wire)正是为此设计。它将新曲面surf与原环outer_wire绑定生成一个拓扑结构 identical、几何内涵焕然一新的Face。这种“换心术”在模具设计中极为关键同一套模具拓扑决定加工刀路可适配不同曲率的零件几何决定产品性能。4.3 分离的陷阱容差Tolerance——Brep世界的“量子涨落”然而拓扑与几何的分离引入了一个幽灵般的参数容差Tolerance。它是Brep中一切精度判断的标尺。TopoDS_Vertex的容差Tol定义了以该点为中心、半径为Tol的球体内所有点都被视为“同一个点”TopoDS_Edge的容差则定义了其几何曲线与拓扑边界的最大允许偏差。OCCT默认容差极小1e-7但在实际工程中CAD数据交换、网格导入、数值计算都会引入误差。若不主动管理容差你会遭遇经典陷阱缝合失败两个本应共享的边因计算误差导致端点距离为1e-6大于默认容差1e-7BRepBuilderAPI_Sewing拒绝将它们焊接。布尔运算崩溃两个实体在交界处存在1e-5量级的微小间隙BRepAlgoAPI_Cut在计算交线时因数值不稳定而抛出异常。显示异常面与面之间出现细小裂缝渲染时漏光。解决方案是全局容差缩放from OCC.Core.BRepLib import breplib # 对整个Solid应用统一容差 breplib.BuildCurves3d(solid, 1e-3) # 将所有边的几何精度提升至1e-3 # 或对单个顶点设置 builder BRep_Builder() builder.Continuity(solid, GeomAbs_C1) # 设置连续性等级 # 最关键在构造前设置全局容差 Interface_Static.SetCVal(read.precision.val, 1e-3) Interface_Static.SetCVal(write.precision.val, 1e-3)我的经验是机械设计取1e-31微米建筑模型取1e-21厘米无需过度追求理论精度而应匹配下游应用CNC加工精度、3D打印层厚的实际需求。记住Brep不是数学理想国而是工程现实的数字孪生容差是它与物理世界握手的礼仪。5. 超越长方体用Brep解决真实场景中的三个硬核问题构造长方体只是热身。Brep的真正价值在于解决那些让传统网格建模束手无策的工业级难题。以下是我在实际项目中用pythonOCC攻克的三个典型场景代码精简但逻辑完整直击痛点。5.1 问题一从散乱点云生成带精确曲率的Brep曲面逆向工程客户给了一组激光扫描的涡轮叶片点云约5000个点要求生成可用于CFD仿真的NURBS曲面Brep模型。网格重建Meshing会产生数百万三角面无法做曲率分析。Brep方案是点云→拟合NURBS曲面→生成Face→缝合成Solid。import numpy as np from OCC.Core.Geom import Geom_BSplineSurface from OCC.Core.TColgp import TColgp_Array2OfPnt from OCC.Core.GeomConvert import GeomConvert_CompCurveToBSplineCurve # 1. 加载点云数据假设为numpy array: points[N,3] points np.loadtxt(blade_points.txt) # 2. 点云排序按U/V参数化方向投影需先估计主曲率方向 # 此处简化假设已知U方向为x轴V方向为y轴z为高度 u_vals points[:, 0] v_vals points[:, 1] z_vals points[:, 2] # 3. 构建二维点阵需插值补全 u_unique np.linspace(u_vals.min(), u_vals.max(), 30) v_unique np.linspace(v_vals.min(), v_vals.max(), 20) U, V np.meshgrid(u_unique, v_unique) Z griddata((u_vals, v_vals), z_vals, (U, V), methodcubic) # 4. 创建TColgp_Array2OfPntOCCT专用二维点阵 array2d TColgp_Array2OfPnt(1, 30, 1, 20) for i in range(30): for j in range(20): array2d.SetValue(i1, j1, gp_Pnt(U[j,i], V[j,i], Z[j,i])) # 5. 拟合NURBS曲面 surf Geom_BSplineSurface(array2d, 3, 3, 3, 3) # U/V次数均为3 # 6. 用曲面生成Face需定义UV参数域 u_min, u_max u_unique.min(), u_unique.max() v_min, v_max v_unique.min(), v_unique.max() face BRepBuilderAPI_MakeFace(surf, u_min, u_max, v_min, v_max).Face() # 7. 重复此过程生成所有叶片面最后缝合成Solid关键洞察点云拟合的核心不是“点多”而是参数化质量。griddata的cubic插值比linear更能保持曲率连续性Geom_BSplineSurface的次数degree设为3三次是工程实践最佳平衡点——次数太低1或2无法表达复杂曲率太高5会导致龙格现象Runges phenomenon和计算不稳定。5.2 问题二对Brep实体进行“智能”倒圆角Fillet避开干涉区域给一个机架Brep模型的12条棱边全部倒R5圆角但其中4条边位于螺栓孔附近倒角后会与孔壁干涉。手动筛选太慢。Brep方案是遍历所有边→计算其到最近孔边的距离→动态设置倒角半径。from OCC.Core.BRepFilletAPI import BRepFilletAPI_MakeFillet from OCC.Core.BRepExtrema import BRepExtrema_DistShapeShape from OCC.Core.TopExp import TopExp_Explorer from OCC.Core.TopAbs import TopAbs_EDGE # 1. 提取所有Edge explorer TopExp_Explorer(solid, TopAbs_EDGE) edges [] while explorer.More(): edges.append(topods_Edge(explorer.Current())) explorer.Next() # 2. 提取所有孔的Edge假设孔为圆柱面其边界环的边为圆形 hole_edges [] for face in TopologyExplorer(solid).faces(): if is_cylindrical_face(face): # 自定义判断曲面类型为Geom_CylindricalSurface for edge in TopologyExplorer(face).edges(): hole_edges.append(edge) # 3. 为每条边计算到最近孔边的距离 fillet_builder BRepFilletAPI_MakeFillet(solid) for edge in edges: min_dist float(inf) for hole_edge in hole_edges: dist_tool BRepExtrema_DistShapeShape(edge, hole_edge) dist_tool.Perform() if dist_tool.IsDone(): min_dist min(min_dist, dist_tool.Value()) # 4. 动态设置半径距离10mm则R5否则线性衰减至R0 radius 5.0 if min_dist 10.0 else max(0.0, 5.0 * (min_dist / 10.0)) if radius 0.0: fillet_builder.Add(radius, edge) # 5. 执行倒角 filleted_solid fillet_builder.Shape()此方案的价值在于将几何约束距离转化为拓扑操作倒角半径。BRepExtrema_DistShapeShape是OCCT的“距离计算器”它能在任意两个Brep子对象边、面、顶点间求精确欧氏距离精度远超网格近似。这使得“智能避让”成为可能而非依赖设计师的经验目测。5.3 问题三Brep模型轻量化在保持拓扑与关键几何的前提下压缩NURBS数据一个船舶螺旋桨Brep模型含200个NURBS曲面每个曲面有数百个控制点文件大小超200MB无法在Web端实时加载。目标将控制点数量减少50%同时保证曲面与原模型的最大偏差0.1mm。Brep方案是提取曲面→降阶Degree Reduction→重采样Knot Removal→重建Face。from OCC.Core.GeomConvert import GeomConvert_ApproxCurve, GeomConvert_ApproxSurface from OCC.Core.TColStd import TColStd_Array1OfReal from OCC.Core.Geom import Geom_BSplineSurface # 1. 遍历所有Face for face in TopologyExplorer(solid).faces(): # 2. 提取Face的几何曲面 surf BRep_Tool.Surface(face) if surf.DynamicType().Name() Geom_BSplineSurface: # 3. 进行近似降低次数并减少节点数 # 参数目标次数2二次最大容差0.1最大段数50 approx_surf GeomConvert_ApproxSurface( surf, 0.1, GeomAbs_C2, 50, 2 ) if approx_surf.HasResult(): new_surf approx_surf.Surface()