思路:
.step
文件.step
文件,进行网格划分,导出.stl
文件.stl
文件进行网格划分(三角面片,可以大大降低渲染成本)mesh.py
import os
import sys
import gmsh
from OCC.Core.IFSelect import IFSelect_RetDone
from OCC.Core.STEPControl import STEPControl_AsIs, STEPControl_Writer
from OCC.Core.TopoDS import TopoDS_Shape
def export_step(shape: TopoDS_Shape, path: str) -> None:
"""将OCC形状导出为STEP文件。
参数
----------
shape : TopoDS_Shape
要导出的OCC形状对象。
path : str
输出的STEP文件路径(例如:"model.step")。
"""
writer = STEPControl_Writer()
writer.Transfer(shape, STEPControl_AsIs)
status = writer.Write(path)
if status != IFSelect_RetDone:
raise RuntimeError("STEP export failed")
print(f"[OK] STEP -> {os.path.abspath(path)}")
def gmsh_mesh_step(
step_path: str,
msh_path: str,
stl_path: str,
lc_min: float = 3.0,
lc_max: float = 8.0,
optimize: bool = True,
) -> None:
"""使用Gmsh对STEP文件进行网格划分(生成3D四面体网格)并导出.msh文件和边界.stl文件。
参数
----------
step_path : str
输入的STEP文件路径。
msh_path : str
输出的Gmsh网格文件路径(例如:"model.msh")。
stl_path : str
用于表面可视化的边界STL文件输出路径(例如:"surf.stl")。
lc_min : float
网格划分的最小特征长度。
lc_max : float
网格划分的最大特征长度。
optimize : bool
是否运行Gmsh网格优化(使用Netgen算法)。
"""
if not os.path.exists(step_path):
raise FileNotFoundError(step_path)
gmsh.initialize(sys.argv)
# 关闭 Gmsh 的内部 log 输出
gmsh.option.setNumber("General.Terminal", 0)
gmsh.model.add("occ_from_step")
gmsh.model.occ.importShapes(step_path)
gmsh.model.occ.synchronize()
gmsh.option.setNumber("Mesh.CharacteristicLengthMin", lc_min)
gmsh.option.setNumber("Mesh.CharacteristicLengthMax", lc_max)
gmsh.model.mesh.generate(3)
if optimize:
gmsh.option.setNumber("Mesh.Optimize", 1)
gmsh.model.mesh.optimize("Netgen")
gmsh.write(msh_path)
gmsh.write(stl_path)
print(f"[OK] MSH -> {os.path.abspath(msh_path)}")
print(f"[OK] STL -> {os.path.abspath(stl_path)}")
gmsh.finalize()
主程序:
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut
from OCC.Core.gp import gp_Ax2, gp_Dir, gp_Pnt
from OCC.Core.StlAPI import StlAPI_Reader
from OCC.Core.TopoDS import TopoDS_Shape
from OCC.Display.SimpleGui import init_display
from mesh import export_step, gmsh_mesh_step
def build_occ_shape() -> TopoDS_Shape:
"""occ构建几何"""
box = BRepPrimAPI_MakeBox(100.0, 60.0, 40.0).Shape()
axis = gp_Ax2(gp_Pnt(50.0, 30.0, 0.0), gp_Dir(0.0, 0.0, 1.0))
cyl = BRepPrimAPI_MakeCylinder(axis, 12.0, 50.0).Shape()
cut = BRepAlgoAPI_Cut(box, cyl).Shape()
return cut
def read_stl_as_shape(stl_file: str) -> TopoDS_Shape:
"""将边界STL文件加载回OCC中,用于表面可视化"""
shape = TopoDS_Shape()
ok = StlAPI_Reader().Read(shape, stl_file)
if not ok:
raise RuntimeError(f"Failed to read STL: {stl_file}")
return shape
def main() -> None:
# occ构建几何
shape = build_occ_shape()
# 导出stp文件
step_path = "model.step"
export_step(shape, step_path)
# Gmsh网格划分:STEP文件 -> model.msh(四面体网格)+ surf.stl(边界三角形面片)
gmsh_mesh_step(
step_path=step_path,
msh_path="model.msh",
stl_path="surf.stl",
lc_min=.0,
lc_max=8.0,
optimize=True,
)
# 在OCC中进行后处理:显示三角面片的表面网格
stl_shape = read_stl_as_shape("surf.stl")
display, start_display, *_ = init_display()
display.DisplayShape(stl_shape, update=True)
start_display()
if __name__ == "__main__":
main()
mesh_view.py
from OCC.Core.RWStl import rwstl
from OCC.Core.MeshDS import MeshDS_DataSource
from OCC.Core.MeshVS import (
MeshVS_Mesh,
MeshVS_MeshPrsBuilder,
MeshVS_DA_ShowEdges,
MeshVS_DA_DisplayNodes,
MeshVS_DA_InteriorColor,
MeshVS_DA_EdgeColor,
MeshVS_DA_MarkerColor,
MeshVS_DA_MarkerType,
MeshVS_DA_MarkerScale,
MeshVS_DA_FrontMaterial
)
from OCC.Core.Graphic3d import Graphic3d_MaterialAspect, Graphic3d_NOM_STEEL
from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB
import OCC.Core.Quantity as Q
from OCC.Core.Aspect import Aspect_TOM_POINT, Aspect_TypeOfMarker
def _to_qcolor(c) -> Quantity_Color:
"""将 Q 常量 / Quantity_Color / (r,g,b) 转为 Quantity_Color。"""
if isinstance(c, Quantity_Color):
return c
# Q.Quantity_NOC_* 常量
try:
return Quantity_Color(c)
except Exception:
pass
# (r, g, b)
if isinstance(c, (tuple, list)) and len(c) == 3:
return Quantity_Color(float(c[0]), float(c[1]), float(c[2]), Quantity_TOC_RGB)
raise ValueError("Unsupported color spec. Use Q.Quantity_NOC_*, Quantity_Color, or (r,g,b).")
def display_stl(
basename: str,
face_color=Q.Quantity_NOC_GRAY31,
edge_color=Q.Quantity_NOC_BLACK,
node_color=Q.Quantity_NOC_RED,
show_edges: bool = True,
show_nodes: bool = True,
marker_size: float = 5.0, # 节点大小(像素,部分版本等效为缩放)
alpha: float = 0.35, # 表面透明度(0~1,越大越透明)
marker_type: Aspect_TypeOfMarker = Aspect_TOM_POINT,
):
"""使用 MeshVS 显示 STL 网格,面/边/节点统一纯色,支持透明度与节点大小。
返回
----
mesh_vs : MeshVS_Mesh
"""
# 读取 STL → 三角网格
stl_file = f"{basename}.stl"
stl_mesh = rwstl.ReadFile(stl_file)
if stl_mesh is None:
raise RuntimeError(f"Failed to read STL as mesh: {stl_file}")
# 数据源
mesh_ds = MeshDS_DataSource(stl_mesh)
# MeshVS 对象
mesh_vs = MeshVS_Mesh()
mesh_vs.SetDataSource(mesh_ds)
# 构建器与 Drawer
prs_builder = MeshVS_MeshPrsBuilder(mesh_vs)
mat = Graphic3d_MaterialAspect(Graphic3d_NOM_STEEL)
mat.SetTransparency(float(alpha))
drawer = prs_builder.GetDrawer()
drawer.SetBoolean(MeshVS_DA_ShowEdges, bool(show_edges))
drawer.SetBoolean(MeshVS_DA_DisplayNodes, bool(show_nodes))
drawer.SetMaterial(MeshVS_DA_FrontMaterial, mat)
# 纯色设置:面、边、节点(marker)
drawer.SetColor(MeshVS_DA_InteriorColor, _to_qcolor(face_color))
drawer.SetColor(MeshVS_DA_EdgeColor, _to_qcolor(edge_color))
drawer.SetColor(MeshVS_DA_MarkerColor, _to_qcolor(node_color))
# 节点形状与大小
drawer.SetInteger(MeshVS_DA_MarkerType, int(marker_type))
drawer.SetDouble(MeshVS_DA_MarkerScale, float(marker_size))
prs_builder.SetDrawer(drawer)
mesh_vs.AddBuilder(prs_builder, True)
# 2 == MeshVS_DMF_Shading
mesh_vs.SetDisplayMode(2)
return mesh_vs
主程序调用:
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut
from OCC.Core.gp import gp_Ax2, gp_Dir, gp_Pnt
from OCC.Core.TopoDS import TopoDS_Shape
from OCC.Display.SimpleGui import init_display
import OCC.Core.Quantity as Q
from mesh import export_step, gmsh_mesh_step
from mesh_view import display_stl
def build_occ_shape() -> TopoDS_Shape:
"""OCC 构建几何:盒体 + 贯穿圆孔。"""
box = BRepPrimAPI_MakeBox(100.0, 60.0, 40.0).Shape()
axis = gp_Ax2(gp_Pnt(50.0, 30.0, 0.0), gp_Dir(0.0, 0.0, 1.0))
cyl = BRepPrimAPI_MakeCylinder(axis, 12.0, 50.0).Shape()
return BRepAlgoAPI_Cut(box, cyl).Shape()
def main() -> None:
basename = "model" # 统一文件名
view, start_display, *_ = init_display()
# 1) 几何建模
shape = build_occ_shape()
# 2) 导出 STEP
export_step(shape, basename)
# 3) Gmsh 划分体网格,导出 .msh + 边界 .stl
gmsh_mesh_step(
basename,
lc_min = 0.0,
lc_max = 8.0,
optimize = True,
)
# 4) 表面网格显示
mesh_vs = display_stl(
basename,
face_color = Q.Quantity_NOC_GRAY31,
edge_color = Q.Quantity_NOC_BLACK,
node_color = Q.Quantity_NOC_ORANGE,
show_edges = True,
show_nodes = True,
marker_size = 5.0,
alpha = 0.5,
)
view.Context.Display(mesh_vs, True)
view.FitAll()
start_display()
if __name__ == "__main__":
main()
其中材质我选择的是Graphic3d_NOM_STEEL
,不同材质的光照渲染效果不同,可选材质:
import os
from OCC.Core.Graphic3d import (
Graphic3d_NOM_BRASS,
Graphic3d_NOM_BRONZE,
Graphic3d_NOM_COPPER,
Graphic3d_NOM_GOLD,
Graphic3d_NOM_PEWTER,
Graphic3d_NOM_PLASTER,
Graphic3d_NOM_PLASTIC,
Graphic3d_NOM_SILVER,
Graphic3d_NOM_STEEL,
Graphic3d_NOM_STONE,
Graphic3d_NOM_SHINY_PLASTIC,
Graphic3d_NOM_SATIN,
Graphic3d_NOM_METALIZED,
Graphic3d_NOM_NEON_GNC,
Graphic3d_NOM_CHROME,
Graphic3d_NOM_ALUMINIUM,
Graphic3d_NOM_OBSIDIAN,
Graphic3d_NOM_NEON_PHC,
Graphic3d_NOM_JADE,
Graphic3d_NOM_CHARCOAL,
Graphic3d_NOM_WATER,
Graphic3d_NOM_GLASS,
Graphic3d_NOM_DIAMOND,
Graphic3d_NOM_TRANSPARENT,
Graphic3d_NOM_DEFAULT,
Graphic3d_NOM_UserDefined,
)
from OCC.Core.gp import gp_Vec
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeCylinder
from OCC.Extend.ShapeFactory import translate_shp
from OCC.Display.SimpleGui import init_display
display, start_display, add_menu, add_function_to_menu = init_display()
# User defined material names should be chosen among
available_materials = [
Graphic3d_NOM_BRASS,
Graphic3d_NOM_BRONZE,
Graphic3d_NOM_COPPER,
Graphic3d_NOM_GOLD,
Graphic3d_NOM_PEWTER,
Graphic3d_NOM_PLASTER,
Graphic3d_NOM_PLASTIC,
Graphic3d_NOM_SILVER,
Graphic3d_NOM_STEEL,
Graphic3d_NOM_STONE,
Graphic3d_NOM_SHINY_PLASTIC,
Graphic3d_NOM_SATIN,
Graphic3d_NOM_METALIZED,
Graphic3d_NOM_NEON_GNC,
Graphic3d_NOM_CHROME,
Graphic3d_NOM_ALUMINIUM,
Graphic3d_NOM_OBSIDIAN,
Graphic3d_NOM_NEON_PHC,
Graphic3d_NOM_JADE,
Graphic3d_NOM_CHARCOAL,
Graphic3d_NOM_WATER,
Graphic3d_NOM_GLASS,
Graphic3d_NOM_DIAMOND,
Graphic3d_NOM_TRANSPARENT,
Graphic3d_NOM_DEFAULT,
Graphic3d_NOM_UserDefined,
]
#
# Displays a cylinder with a material
#
radius = 30
s = BRepPrimAPI_MakeCylinder(radius, 200).Shape()
delta_x = 0.0
for mat in available_materials:
s2 = translate_shp(s, gp_Vec(delta_x, 0.0, 0.0))
display.DisplayShape(s2, material=mat)
delta_x += 2 * radius + 1.0
display.FitAll()
start_display()