PythonOCC 经验
https://blog.csdn.net/m0_65073612/article/details/138942290
https://dev.opencascade.org/doc/refman/html/classes.html
https://blog.csdn.net/weixin_42755384/article/details/87893697
第一步,从 step 拿到 TopoDS_Shape ,需要用到 STEPControl_Reader
from OCC.Core.STEPControl import STEPControl_Reader
filename = "xxx.step"
reader = STEPControl_Reader()
reader.ReadFile(filename)
reader.TransferRoots()
a_shape = reader.OneShape()
# oneshape 时,物体被整合,某些 step 中存在的面可能消失第二步,获取边和面 TopoDS_Edge TopoDS_Face ,需要用到 TopologyExplorer
from OCC.Extend.TopologyUtils import TopologyExplorer
topo = TopologyExplorer(a_shape)
faces = list(topo.faces())
edges = list(topo.edges())
# 此处 edge 并不是双向的,而是无向的,即正方体有 12 个第三步,获取面和边的信息
1、面积、边长、体积
from OCC.Core.BRepGProp import (brepgprop_LinearProperties,
brepgprop_SurfaceProperties,
brepgprop_VolumeProperties)
from OCC.Core.GProp import GProp_GProps
from OCC.Core.BRepGProp import brepgpropGProp 指的是 geometry_properties 几何属性,计算形状的物理属性,如体积、表面积。
求面积等都有两种类似的算法
def area_feature(face): # 求面积
gp = GProp_GProps()
brepgprop_SurfaceProperties(face, gp)
return gp.Mass()
def area_feature2(face): # 求面积
gp = GProp_GProps()
brepgprop.SurfaceProperties(face, gp)
return geometry_properties.Mass()
def length_feature(edge): # 求边长
geometry_properties = GProp_GProps()
brepgprop_LinearProperties(edge, geometry_properties)
return geometry_properties.Mass()
def volume_feature(shape): # 求体积
geometry_properties = GProp_GProps()
brepgprop_VolumeProperties(shape, geometry_properties)
return geometry_properties.Mass()2、求面包含的边,边所在的面
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopAbs import TopAbs_EDGETopExp 遍历工具,TopExp_Explorer 可以遍历形状的子元素
def get_edges_from_face(face: TopoDS_Face) -> list:
"""
求面所包含的所有边
"""
edges = []
explorer = TopExp_Explorer(face, TopAbs_EDGE)
while explorer.More():
edge = explorer.Current()
edges.append(edge)
explorer.Next()
return edges看到这里,我们知道,从体上取面,和从面上取边,有两种做法:
第一种是用 TopologyExplorer 获取 topo,然后直接取得列表
第二种是通过 TopExp_Explorer 的对象的 Current() 方法来获取
如果要求边所在的面(一般是两个),这个操作是比较传统的遍历所有面,再遍历面上所有边
from OCC.Core.TopoDS import topods
from OCC.Core.TopAbs import TopAbs_EDGE, TopAbs_FACEdef find_faces_containing_edge(edge: TopoDS_Edge, shape) -> list:
"""
给定一个边,返回包含此边的面(列表)
"""
faces_containing_edge = []
# 创建 TopExp_Explorer 来遍历所有面
explorer = TopExp_Explorer(shape, TopAbs_FACE)
while explorer.More():
face = topods.Face(explorer.Current())
# 创建一个探索器来遍历该面上的所有边
edge_explorer = TopExp_Explorer(face, TopAbs_EDGE)
while edge_explorer.More():
current_edge = topods.Edge(edge_explorer.Current())
# current_edge = edge_explorer.Current()
# 比较当前边和给定的边
if current_edge.IsSame(edge):
faces_containing_edge.append(face)
break # 如果找到包含该边的面,就不再需要遍历该面的其他边
edge_explorer.Next()
explorer.Next()
return faces_containing_edge这里,我们观察到一个方法 current_edge.IsSame(edge)
这个方法可以判断 边和边 面和面 是否是同一个,而不用在乎是否是同一个对象
即用 topo.edges() 产生的 edge 和用 edge_explorer.Current() 产生的 edge,在这里可以相等
关于用不用 topods.Edge 显示构造的问题,虽然 Current 函数的返回值是 TopoDS_Shape,但是不写应该也行
3、取得 边的两个端点,进而取得中点,取得面的中心点,再求切向量
from OCC.Core.BRepAdaptor import BRepAdaptor_CurveBRepAdaptor 里面是适配器,使得可以以基本几何曲线一样操作拓扑边
curve_adaptor = BRepAdaptor_Curve(edge)
first_vertex = curve_adaptor.FirstParameter()
last_vertex = curve_adaptor.LastParameter()
start_point = curve_adaptor.Value(first_vertex)
end_point = curve_adaptor.Value(last_vertex)BRepAdaptor_Curve 适配了底层曲线,这些曲线有一些参数,你取出端点参数,再构造 gp_Pnt 即可,这个参数对你而言无意义
构造中点时,可以通过参数,一半的参数来构建,也可以直接计算坐标(直线型)
midpoint_param = (first_vertex + last_vertex) / 2
midpoint = curve_adaptor.Value(midpoint_param)mid_x = (point1.X() + point2.X()) / 2.0
mid_y = (point1.Y() + point2.Y()) / 2.0
mid_z = (point1.Z() + point2.Z()) / 2.0
return gp_Pnt(mid_x, mid_y, mid_z)求出中点以后,可以求切向量
tangent_vec = curve_adaptor.Tangent(midpoint_param).Direction()
curve_adaptor.D1(midpoint_param, midpoint, tangent_vec)得到边中点和方向的函数
def get_edge_feature(edge: TopoDS_Edge):
"""
返回一个边的特征,中点坐标以及切向量
形状:一维数组,长 6
"""
curve_adaptor = BRepAdaptor_Curve(edge)
first_vertex = curve_adaptor.FirstParameter()
last_vertex = curve_adaptor.LastParameter()
midpoint_param = (first_vertex + last_vertex) / 2
midpoint = curve_adaptor.Value(midpoint_param)
tangent_vec = curve_adaptor.Tangent(midpoint_param).Direction()
curve_adaptor.D1(midpoint_param, midpoint, tangent_vec)
return [midpoint.X(),midpoint.Y(),midpoint.Z(),tangent_vec.X(),tangent_vec.Y(),tangent_vec.Z()](注意 X()是方法调用,不是成员变量)
获取面的中心点,不能直接取得,要通过 GProp 几何属性取得质心
from OCC.Core.gp import gp_Pntdef get_face_center(face: TopoDS_Face) -> gp_Pnt:
gprops = GProp_GProps()
brepgprop.SurfaceProperties(face, gprops)
center_of_mass = gprops.CentreOfMass()
return center_of_mass
def get_edge_center(edge: TopoDS_Edge) -> gp_Pnt:
gprops = GProp_GProps()
brepgprop.LinearProperties(edge, gprops)
center_of_mass = gprops.CentreOfMass()
return center_of_mass如果你是一个半圆弧,你的质心是在圆心,但是中心在圆弧上(中心总是在曲线上,毕竟是通过参数方程求出来的)
两个点相比较 midpoint1.IsEqual(midpoint12,0.0001)
gp_Pnt.Coord() 方法返回一个三元组,即坐标。 Coordinate:并列的
4、生成 TopoDS_Shape 并保存为 step 文件
BRepPrimAPI 用于生成基本的几何体,如立方体、球体、圆柱等
BrepAlgoAPI 用于执行布尔运算,将多个体转换为一个新体
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBoxbox = BRepPrimAPI_MakeBox(50+a, 10+b, 30+c, gp_Pnt(0, 0, 0)).shape
# 直接可得到 TopoDS_Shape作布尔运算,Fuse 融合,cut 剪切
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Fuset_shape = BRepAlgoAPI_Fuse(base_box, top_box).Shape()利用 STEPControl 写入文件,提供了 STEPControl_Reader,STEPControl_Writer
from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_Reader, STEPControl_AsIs本文一开始的方法已经足够常见,这里再列举一次
def read_step_file(file_path: str) -> TopoDS_Shape:
reader = STEPControl_Reader()
reader.ReadFile(file_path)
reader.TransferRoots()
shape = reader.OneShape()
return shape
def write_step_file(shape: TopoDS_Shape, file_path: str):
writer = STEPControl_Writer()
writer.Transfer(shape, STEPControl_AsIs)
writer.Write(file_path)
#均未做任何错误处理5、获取面的法向量
通过 BRepAdaptor_Surface 类将平面映射到 UV 空间内
gp_Dir 表示单位向量
from OCC.Core.gp import gp_Pnt, gp_Vec, gp_Dir
from OCC.Core.BRep import BRep_Tool_Surface
from OCC.Core.TopAbs import TopAbs_REVERSED
from OCC.Core.GeomLProp import GeomLProp_SLProps
from OCC.Core.BRepTools import breptools_UVBoundsdef get_face_normal(face: TopoDS_Face) -> gp_Dir:
"""
获取给定面的法向量
"""
# 获取面的 UV 参数范围
u_min, u_max, v_min, v_max = breptools_UVBounds(face)
u_mid = (u_min + u_max) / 2.0
v_mid = (v_min + v_max) / 2.0
# 获取法向量
surf = BRep_Tool_Surface(face)
normal = GeomLProp_SLProps(surf, u_mid, v_mid, 1, 1e-4).Normal()
if face.Orientation() == TopAbs_REVERSED:
normal.Reverse()
return normal让我们回顾一下“几何属性”相关类
from OCC.Core.BRepGProp import brepgprop
from OCC.Core.BRepGProp import (brepgprop_LinearProperties,
brepgprop_SurfaceProperties,
brepgprop_VolumeProperties)这个是求,Brep 形状的全局属性的,比如线的长度
from OCC.Core.GeomLProp import GeomLProp_SLProps
from OCC.Core.GeomLProp import GeomLProp_CLProps求一个几何对象的局部属性,S 是面,C 是线
from OCC.Core.GProp import GProp_GPropsGprop 也是求属性的,与第一个类似
6、求给定面,通过某个边的相邻面
from OCC.Core.TopTools import TopTools_IndexedDataMapOfShapeListOfShape
from OCC.Core.TopExp import topexp_MapShapesAndAncestors
from OCC.Core.TopOpeBRepBuild import TopOpeBRepBuild_Toolsdef get_face_adjacent(shape, face, edge) -> TopoDS_Face:
efmap = TopTools_IndexedDataMapOfShapeListOfShape()
topexp_MapShapesAndAncestors(shape, TopAbs_EDGE, TopAbs_FACE, efmap)
adjface = TopoDS_Face()
if TopOpeBRepBuild_Tools.GetAdjacentFace(face, edge, efmap, adjface):
return adjface
else:
return None7、读写标签
不解释了
import os
import re
from OCC.Core.STEPControl import STEPControl_Reader
from OCC.Extend.TopologyUtils import TopologyExplorer
from OCC.Core.StepRepr import StepRepr_RepresentationItem
def read_step_with_face_labels_with_lines(filename):
"""
阅读 step 文件 ADVANCED_FACE 中
形如 “X__Y” 的注释中的标签
返回按 topo.faces() 列表顺序遍历面的标签列表
"""
if not os.path.exists(filename):
print(filename, ' not exists')
return
reader = STEPControl_Reader()
reader.ReadFile(filename)
reader.TransferRoots()
shape = reader.OneShape()
treader = reader.WS().TransferReader()
topo = TopologyExplorer(shape)
faces = list(topo.faces())
labels=[]
for face in faces:
item = treader.EntityFromShapeResult(face, -1)
if item is None:
# print(filename, face)
continue
item = StepRepr_RepresentationItem.DownCast(item)
name = item.Name().ToCString()
# print(name)
# name 是单引号中间那个字符串
if name:
matches = re.findall(r'\d+', name)
if len(matches) >= 2:
nameid = matches[1]
else:
nameid = None
labels.append(int(nameid))
return labels
def write_step_labels_by_string(filename):
"""
通过字符串的方式,为 step 文件写入预设标签
写入顺序按照 ADVANCED_FACE 出现顺序
会直接覆盖掉原有''中内容
"""
prefix = "ADVANCED_FACE"
# prefix = "EDGE_CURVE"
with open(filename, 'r') as file:
lines = file.readlines()
index = 0
pattern = r"({}\(')[^']*(')".format(prefix)
for i, line in enumerate(lines):
new_line = re.sub(pattern, f"{prefix}(\'{index}__{index}\'", line)
if new_line != line:
lines[i] = new_line
index+=1
with open(filename, 'w') as file:
file.writelines(lines)8、显示并上色
from OCC.Display.SimpleGui import init_display
display, start_display, add_menu, add_function_to_menu = init_display()init_display 负责初始化一个图形窗口用于显示,但并不立刻弹出
使用 DisplayShape 放入物体,start_display() 方法显示
# 擦除
display.EraseAll()
display.DisplayShape(shape, update=True) #True 表示立即更新看到结果
start_display()上色,引入 AIS 模块,Application Interactive Service
管理对象的显示状态,包括显示/隐藏、颜色、透明度、材质属性等
其中的 AIS_Shape 专门哟用于显示 TopoDS_Shape 对象
所以我们要做一下内容:
1、将所需要展示的面,转换为 AIS_Shape 2、调用其 SerColor 方法 3、放到 display 里面(如果不是所有面都调用 display.Context.Display(),则不会显示所有面,因为你可能没有写显示整个 Shape 的方法)
from OCC.Core.AIS import AIS_Shape
from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB topo = TopologyExplorer(shape)
faces = list(topo.faces())
edges = list(topo.edges())
display, start_display, add_menu, add_function_to_menu = init_display()
color1 = Quantity_Color(1.0, 0.0, 0.0, Quantity_TOC_RGB)
color2 = Quantity_Color(0.0, 1.0, 0.0, Quantity_TOC_RGB)
for e in edges:
ais_edge = AIS_Shape(e)
if(True):
ais_edge.SetColor(color1)
else:
ais_edge.SetColor(color2)
display.Context.Display(ais_edge, True)9、曲率 以及求法
曲线 的一阶导数,是切向量(和数学上的概念类似)
二阶导数反映了凸凹性等,结合一二阶导数可以求曲率
这里不给公式了,直接求
利用曲线适配器的 D1 和 D2 方法可以求曲线的一二阶导数
值得注意的是,曲线是参数方程,也就是 u(或者 t 也行)的函数
curve_adaptor = BRepAdaptor_Curve(edge)
first_vertex = curve_adaptor.FirstParameter()
last_vertex = curve_adaptor.LastParameter()
midpoint_param = (first_vertex + last_vertex) / 2
midpoint = curve_adaptor.Value(midpoint_param)
tangent_vec = gp_Vec()
normal_vec = gp_Vec()
# curve_adaptor.D1(midpoint_param, midpoint, tangent_vec)
curve_adaptor.D2(midpoint_param, midpoint, tangent_vec, normal_vec)
norm_tangent = tangent_vec.Magnitude()
norm_normal = normal_vec.Magnitude()
curvature = norm_normal / (norm_tangent ** 3)面的这里直接给出 能跑的代码
def get_face_feature(face: TopoDS_Face) -> list:
u_min, u_max, v_min, v_max = breptools_UVBounds(face)
surface_adaptor = BRepAdaptor_Surface(face)
u_mid = (u_min + u_max) / 2
v_mid = (v_min + v_max) / 2
center = surface_adaptor.Value(u_mid, v_mid)
normal_vec = gp_Vec()
tangent_vec = gp_Vec()
surface_adaptor.D1(u_mid, v_mid, center, normal_vec, tangent_vec)
d1u=gp_Vec()
d1v=gp_Vec()
d2u=gp_Vec()
d2v=gp_Vec()
d2uv=gp_Vec()
surface_adaptor.D2(u_mid, v_mid, center, d1u, d1v, d2u, d2v, d2uv)
E =d1u.SquareMagnitude()
F = d1u.Dot(d1v)
G = d1v.SquareMagnitude()
L = d2u.Dot(normal_vec)
M = d2uv.Dot(normal_vec)
N = d2v.Dot(normal_vec)
gaussian_curvature = (L * N - M * M) / (E * G - F * F)
mean_curvature = 0.5 * (L * G - 2 * M * F + N * E) / (E * G - F * F)gp_Trsf 是 OpenCASCADE 几何处理库中的一个类,用于表示三维几何变换(transformation)。它支持多种类型的几何变换,包括平移、旋转、缩放、镜像和复合变换。gp 代表 "Geometric Primitives"(几何基本体),而 Trsf 则是 "Transformation" 的缩写。