读取 IGES(Initial Graphics Exchange Specification)文件并从中提取几何信息(如点、曲线、曲面等),然后在**特定剖面(section)下建立局部坐标系**,并获取该坐标系下各点的坐标值,是一个典型的 CAD 数据处理任务。IGES 是一种较老但仍在某些工业领域(如航空、船舶)使用的中性 CAD 交换格式,其结构为纯文本(ASCII),但语法复杂、实体类型繁多(如点 `POINT`(Type 116)、线 `LINE`(110)、样条 `SPLINE_CURVE`(126)、面 `SURFACE_OF_REVOLUTION`(120)等),且依赖于“参数行”和“目录行”的严格解析。
⚠️ 重要前提:
- **Python 标准库不支持 IGES 解析**;
- 没有成熟、稳定、开源且完全支持 IGES 全部实体类型的纯 Python 解析器(如 `python-iges` 已多年未维护,仅支持极少数基础实体);
- 工业级可靠方案需借助专业几何内核或封装库。
---
### ✅ 推荐可行方案(兼顾可用性、精度与工程实践)
#### 方案一:使用 `OCC`(Open CASCADE Technology) + `pythonocc-core`(推荐 ★★★★★)
Open CASCADE 是开源、功能完备的 CAD 几何建模内核,原生支持 IGES 读写(`IGESControl_Reader`),可精确重建拓扑(TopoDS_Shape)、提取几何(Geom_Curve/Geom_Surface)、执行剖面(section)切割、坐标系变换(local coordinate system)等。
> ✅ 支持:IGES v5.3 全实体、B-Rep/NURBS 精确解析、剖面平面交线计算、点坐标在任意坐标系下的投影/变换。
---
### 🔧 完整 Python 示例(基于 `pythonocc-core`)
> ✅ 前提安装(推荐 conda):
> ```bash
> conda install -c conda-forge pythonocc-core=7.7.2
> # 或 pip(可能编译失败,conda 更稳)
> ```
#### 🎯 目标实现:
1. 读取 `.igs` 文件;
2. 提取所有 `Geom_Curve`(如边)或 `Geom_Surface`(如面);
3. 在指定剖面平面(例如:过点 `P(0,0,0)`、法向 `(0,0,1)` 的 XY 平面)上做截面(section),得到交线(通常是 `Geom_Curve` 集合);
4. 对每条交线(如 `Geom_Line`, `Geom_Circle`, `Geom_BSplineCurve`),采样若干点;
5. 将这些点坐标 **变换到用户定义的局部坐标系(Local Coordinate System, LCS)** 下(例如:原点在 `O'=(10,5,2)`,X轴沿 `(1,0,0)`,Z轴沿 `(0,0,1)` → 构成右手正交系);
6. 输出各点在 LCS 下的 `(u,v,w)` 坐标。
---
### ✅ Python 代码(含详细注释)
```python
from OCC.Core.IGESControl import IGESControl_Reader
from OCC.Core.TopoDS import topods_Vertex, topods_Edge, topods_Wire, topods_Face
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopAbs import TopAbs_EDGE, TopAbs_FACE, TopAbs_WIRE, TopAbs_VERTEX
from OCC.Core.BRep import BRep_Tool
from OCC.Core.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_Surface
from OCC.Core.Geom import Geom_Curve, Geom_Surface, Geom_Line, Geom_Circle, Geom_BSplineCurve
from OCC.Core.GeomAPI import GeomAPI_PointsToBSpline
from OCC.Core.gp import gp_Pnt, gp_Vec, gp_Ax3, gp_Dir, gp_Trsf, gp_XYZ
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace, BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeEdge
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Section
from OCC.Core.TColgp import TColgp_Array1OfPnt
from OCC.Core.GeomConvert import GeomConvert_CurveToBSplineCurve
from OCC.Core.ShapeAnalysis import ShapeAnalysis_Curve
import numpy as np
def read_iges_file(filename: str):
"""读取 IGES 文件,返回 TopoDS_Shape"""
reader = IGESControl_Reader()
status = reader.ReadFile(filename)
if status != 1: # IFSelect_RetDone
raise RuntimeError(f"Failed to read IGES file: {filename}")
reader.TransferRoots()
shape = reader.OneShape()
if shape.IsNull():
raise RuntimeError("IGES file loaded but no valid shape found.")
return shape
def get_section_curves(shape, section_plane_ax3: gp_Ax3) -> list[Geom_Curve]:
"""
对 shape 与给定平面(gp_Ax3)做布尔截面,返回所有交线(Geom_Curve 列表)
注意:BRepAlgoAPI_Section 返回的是 TopoDS_Shape(由 Edge 组成),需转为 Geom_Curve
"""
section = BRepAlgoAPI_Section(shape, BRepBuilderAPI_MakeFace(section_plane_ax3).Face())
section.Build()
if not section.IsDone():
raise RuntimeError("Section operation failed.")
curves = []
exp = TopExp_Explorer(section.Shape(), TopAbs_EDGE)
while exp.More():
edge = topods_Edge(exp.Current())
adaptor = BRepAdaptor_Curve(edge)
curve = adaptor.Curve().Curve() # Geom_Curve*
if not curve.IsNull():
curves.append(curve)
exp.Next()
return curves
def sample_curve(curve: Geom_Curve, num_points: int = 20) -> list[gp_Pnt]:
"""对 Geom_Curve 均匀采样 num_points 个点(支持 Line/Circle/BSpline 等)"""
c = curve
if c.DynamicType().Name() == "Geom_Line":
# Line: 取两端 + 中间均匀点
p1 = c.StartPoint()
p2 = c.EndPoint()
pts = []
for i in range(num_points):
t = i / (num_points - 1) if num_points > 1 else 0.0
x = p1.X() + t * (p2.X() - p1.X())
y = p1.Y() + t * (p2.Y() - p1.Y())
z = p1.Z() + t * (p2.Z() - p1.Z())
pts.append(gp_Pnt(x, y, z))
return pts
else:
# 通用:用 ShapeAnalysis_Curve 自适应采样(更鲁棒)
sac = ShapeAnalysis_Curve()
first, last = c.FirstParameter(), c.LastParameter()
pts = []
for i in range(num_points):
u = first + (last - first) * i / (num_points - 1)
pts.append(c.Value(u))
return pts
def create_local_coordinate_system(origin: gp_Pnt, x_dir: gp_Dir, z_dir: gp_Dir) -> gp_Ax3:
"""构建右手局部坐标系:origin + X/Z → Y = Z × X"""
y_dir = gp_Dir(z_dir.Crossed(x_dir)) # right-hand rule
return gp_Ax3(origin, z_dir, x_dir) # OCC 中 Ax3: origin, main dir (Z), X direction
def transform_point_to_lcs(pnt: gp_Pnt, lcs: gp_Ax3) -> gp_XYZ:
"""将世界坐标点 pnt 转换到 lcs 坐标系下的 (u,v,w) —— 即 lcs 的局部坐标"""
trsf = gp_Trsf()
trsf.SetTransformation(lcs, gp_Ax3()) # 从 lcs → world;逆变换即 world → lcs
trsf.Invert()
pnt_lcs = pnt.Transformed(trsf)
return pnt_lcs.XYZ()
def main(iges_path: str,
section_origin: tuple[float,float,float] = (0,0,0),
section_normal: tuple[float,float,float] = (0,0,1),
lcs_origin: tuple[float,float,float] = (10,5,2),
lcs_x: tuple[float,float,float] = (1,0,0),
lcs_z: tuple[float,float,float] = (0,0,1),
num_sample_per_curve: int = 30):
"""
主流程:
- 读 IGES
- 构建剖面平面(section plane)
- 计算截面曲线
- 对每条曲线采样点
- 构建局部坐标系(LCS)
- 将所有点转换至 LCS 下
- 返回 numpy array: shape (N, 3) of [u,v,w]
"""
print("✅ Step 1: Reading IGES...")
shape = read_iges_file(iges_path)
print("✅ Step 2: Building section plane...")
sec_pnt = gp_Pnt(*section_origin)
sec_dir = gp_Dir(*section_normal)
section_ax3 = gp_Ax3(sec_pnt, sec_dir) # 默认 XY 平面(Z-up)
print("✅ Step 3: Computing section curves...")
section_curves = get_section_curves(shape, section_ax3)
print(f" → Found {len(section_curves)} section curves.")
# 构建 LCS
print("✅ Step 4: Building local coordinate system...")
lcs_pnt = gp_Pnt(*lcs_origin)
lcs_xdir = gp_Dir(*lcs_x)
lcs_zdir = gp_Dir(*lcs_z)
lcs_ax3 = create_local_coordinate_system(lcs_pnt, lcs_xdir, lcs_zdir)
# 采样 + 变换
all_points_lcs = []
for i, curve in enumerate(section_curves):
print(f" → Sampling curve #{i+1} ({curve.DynamicType().Name()})...")
pts_world = sample_curve(curve, num_sample_per_curve)
for p in pts_world:
xyz = transform_point_to_lcs(p, lcs_ax3)
all_points_lcs.append([xyz.X(), xyz.Y(), xyz.Z()])
result = np.array(all_points_lcs)
print(f"✅ Done! Total points in LCS: {len(result)}")
return result
# —— 使用示例 ——
if __name__ == "__main__":
# 替换为你自己的 .igs 文件路径
igs_file = "example.igs"
# 参数说明:
# section_* : 截面平面(用于切模型)
# lcs_* : 你关心的局部坐标系(输出点在此系下)
points_in_lcs = main(
iges_path=igs_file,
section_origin=(0, 0, 0),
section_normal=(0, 0, 1), # XY 平面截面
lcs_origin=(10.0, 5.0, 2.0),
lcs_x=(1.0, 0.0, 0.0),
lcs_z=(0.0, 0.0, 1.0),
num_sample_per_curve=50
)
print("\n📋 First 5 points in Local Coordinate System (u, v, w):")
print(points_in_lcs[:5])
```
---
### 🔍 关键解释:
| 模块 | 作用 |
|------|------|
| `IGESControl_Reader` | Open CASCADE 官方 IGES 解析器,能完整加载 B-Rep 实体(非仅线框) |
| `BRepAlgoAPI_Section` | 布尔截面运算器,输入 shape + 平面 → 输出交线(TopoDS_Edge 集合) |
| `BRepAdaptor_Curve` | 将 TopoDS_Edge 映射为 `Geom_Curve*`(几何级对象),便于采样/分析 |
| `gp_Ax3` | OCC 中的 3D 坐标系(原点 + Z轴 + X轴),是定义 LCS 的核心类型 |
| `gp_Trsf::SetTransformation(lcs, gp_Ax3())` | 构造「LCS → World」的变换;`.Invert()` 后即得「World → LCS」 |
| `transform_point_to_lcs()` | 利用齐次变换,将世界点映射到 LCS 的 `(u,v,w)` 坐标(本质是坐标系基底投影) |
> 💡 提示:若需更高精度剖面(如 NURBS 曲面精确交线),可改用 `GeomAPI_IntSS`(曲面-曲面交)或 `IntCurvesFace_ShapeIntersector`,但 `BRepAlgoAPI_Section` 对大多数工程模型已足够。
---
### ⚠️ 注意事项 & 常见问题
- ❌ `python-iges`、`pyiges` 等轻量库**无法处理带拓扑/曲面的 IGES**,仅适用于 Type 116(点)、110(线)等简单实体,不满足“剖面+坐标系变换”需求;
- ✅ 必须用 Open CASCADE(或商业内核如 ACIS、Parasolid)才能保证几何保真度;
- 📁 IGES 文件常含单位歧义(inch/mm),建议预处理统一为 mm(可在 OCC 中用 `IGESControl_Controller.SetUnit` 设置,本例省略);
- 🧩 若模型含大量小边/退化面,`Section` 可能失败 → 可先 `ShapeFix_Shape` 修复;
- 📈 输出 `points_in_lcs` 是 `np.ndarray`,可直接导出 CSV、用于后续拟合/检测/可视化(如 `matplotlib` 或 `plotly`)。
---
### 🧩 进阶扩展方向(供你后续探索)
- ✅ 自动识别 IGES 中的 `SECTION` 实体(Type 406 + Form 12)—— 有些 IGES 直接自带剖面定义;
- ✅ 对采样点拟合 B-Spline 曲线(`GeomAPI_PointsToBSpline`)→ 得到参数化剖面轮廓;
- ✅ 批量处理多个剖面(Z 向等距切片)→ 生成层厚数据(类似 3D 打印切片);
- ✅ 将 LCS 下点云与 CAD 模型标注尺寸比对(GD&T 分析);
- ✅ 导出为 `.csv` 或 `.ply` 供 ParaView/CloudCompare 可视化。
---