AMCAX Kernel
Geometry kernel for CAD/CAE/CAM
九韶内核 1.0.0.0
载入中...
搜索中...
未找到
STEP 文件读写示例

概述

本教程提供了使用 AMCAX STEP Reader(下称 Reader 或 TopoShape Reader) 与 AMCAX STEP Writer (下称 Writer) 的基本用法。 此外,本模块也提供将读取数据转换为 Mesh 格式的 AMCAX STEP Mesh Reader(下称 Mesh Reader)。 然而,目前尚未提供以 STEP 协议导出 Mesh 数据的接口。

所需知识

开发人员需要具备一些基础知识,包括现代 C++ 编程语言、3D 几何建模概念和 B-Rep 拓扑结构。具体来说,AMCAX 内核与 Reader / Writer 是使用 C++ 17 标准设计的,且使用 STL 容器和算法作为基本数据结构。

导入部分

引入头文件

TopoShape Reader

本示例程序中 Reader 部分只需要引入一个头文件,通过以下代码将该头文件加入程序:

用于将 STEP 文件转换为 TopoShape 对象的类

Mesh Reader

若需要使用 Mesh 相关接口,则需要使用 StepMeshReader。Mesh Reader 在以下头文件中定义。Mesh Reader 是 Reader 的子类,在其基础上追加了 Mesh 相关功能。

用于读取 STEP 文件并将实体/shells 转换为网格的类

另外,Mesh Reader作为可以接受多种 Traits 的模板类型,如果编译参数中缺少相关宏定义,也可以通过以下代码启用对应 Traits 类型的代码:

// 可根据具体情况替换为其它宏
#define AMCAXMeshing_Enable_CommonTraits

创建 Reader 实例

TopoShape Reader

STEP Reader 并不提供静态函数,所有调用都需要对实例使用。 构造时需要传入一个文件名 (std::string) 或输入流 (std::istream) 作为参数:

std::ifstream ifs("/path/to/input.step");
if (!ifs.is_open())
{
return 1;
}
用于将 STEP 文件转换为 TopoShape 对象的类
定义 StepReader.hpp:26

或者

AMCAX::STEP::StepReader reader("/path/to/input.step");

Mesh Reader

类似的,Mesh Reader 也提供了相同的构造器:

MeshReader reader(ifs);
用于读取 STEP 文件并将实体/shells 转换为网格的类
定义 StepMeshReader.hpp:26

或者

MeshReader reader("/path/to/input.step");

(可选)设置缩放

默认情况下,Reader 会以毫米为单位长度,对读取到的模型进行缩放操作。 可以通过以下代码进行调整:

reader.SetUnit(AMCAX::STEP::StepLengthUnit::PresetLengthUnit::NOSCALE);

在转换完成后,亦可以通过 StepReader::GetUnit() / StepMeshReader::GetUnit() 或 StepData::Factors() / StepMeshData::Factors() 获取 STEP 文件中提供的单位。

(可选)设置进度汇报

无论是对 Reader 或是对 Mesh Reader,加载进度都是通过回调函数汇报的。然而,其使用的参数稍有不同:

TopoShape Reader

一般来说,c1的值(如有)表示当前已完成处理的图形数量,c2的值(如有)则表示图形总数。

reader.SetProgressCallback(
{
std::cout << " State: " << int(state) << std::endl;
if (c2.type == AMCAX::STEP::StepProgressCarrier::U64_TYPE && c2.payload.u64)
{
std::cout << " Carrier1: " << c1.payload.u64 << std::endl;
std::cout << " Carrier2: " << c2.payload.u64 << std::endl;
}
});
A class used for representing the states in the progress of the STEP process.
定义 StepProgress.hpp:53
A carrier for carrying data the progress of the STEP process.
定义 StepProgress.hpp:17

对于每个状态及其参数的含义,可以参考 <step/StepProgress.hpp>

Mesh Reader

Mesh Reader 的回调函数与 Reader 基本一致,仅 State 的类型有一点区别:

reader.SetProgressCallback(
{
std::cout << " State: " << int(state) << std::endl;
if (c2.type == AMCAX::STEP::StepProgressCarrier::U64_TYPE && c2.payload.u64)
{
std::cout << " Carrier1: " << c1.payload.u64 << std::endl;
std::cout << " Carrier2: " << c2.payload.u64 << std::endl;
}
});
Class used for reporting the state of the STEP process.
定义 StepMeshProgress.hpp:16

对于每个状态及其参数的含义,可以参考 <step/mesh/StepMeshProgress.hpp>

读取文件

在构建 Reader 或 Mesh Reader 实例后,便可以读取文件:

bool topo_success = reader.Read();

不管是对 Reader 还是对 Mesh Reader,这一步都将完成 STEP 至 TopoShape 的翻译工作。 一般来说,受到软硬件以及输入文件的复杂度差异影响,运行时间会持续数毫秒到数十分钟不等。 若在前一步骤中使用 std::istream 作为参数,用户需要确保在这一函数执行完毕前该输入流的生命周期不会失效。 如果读取失败,Read() 会返回 false 。

(仅 Mesh)将 TopoShape 转换为 Mesh 格式

完成 STEP 的翻译工作之后,便可以进行 TopoShape 至 Mesh 的转换工作。

bool mesh_success = reader.ToMesh();

其中,ToMesh() 接受一个可选的 double 类型参数作为网格化的参数。

创建用于储存数据的容器

TopoShape Reader

本模块的读、写子模块均使用 AMCAX::STEP::StepDataList 传输数据:

AMCAX::STEP::StepDataList shapes;

AMCAX::STEP::StepData 类型由 <step/StepData.hpp> 头文件定义,但一般情况下无需显式引入该头文件。

Mesh Reader

类似的,Mesh Reader 作为 Reader 的扩展,使用 AMCAX::STEP::StepMeshDataList 类型传输数据:

AMCAX::STEP::StepMeshDataList<AMCAX::Meshing::Mesh::TriSoupTraits_Coord> shapes;

其定义可以在 <step/mesh/StepMeshData.hpp> 中找到。

获取结果

不管是对 Reader 还是对 Mesh Reader,获取翻译结果的代码都是相同的:

shapes = reader.GetShapes();

TopoShape Reader

根据文件的内容和转换的情况,shapes 中会包含 0 或更多 StepData 对象。 StepData 根据 STEP 文件中的 Product_Definition,以一个包含 1 或更多节点的树状结构,描述零件的装配。 其中主要包含:

  1. 当前 Product 的名称、描述信息,通过 ProductName() 与 Description() 获取;
  2. 当前 Product 的子 Product,即当前 StepData 节点的子树,数量为 0 或更多,通过 Children() 获取;
  3. 当前 Product 的 TopoShape,数量为 0 或更多,通过 Shapes() 获取;
  4. 当前 Product 下针对每个 TopoShape 的属性(如名称、颜色)和缩放信息,通过 PropertyChains() 与 Factors() 获取;
  5. 当前 Product 装配至上级 Product 时使用的位移、旋转信息,通过 Transformation() 获取;

Mesh Reader

结构、成员与 StepData 基本一致,但是增加了如下变量:

  1. 当前 Product 的 Mesh 数据,通过 Meshes() 获取。

(可选,仅 TopoShape)将树状 StepData 转换为一维数组

如果不需要保留 Product 的 Product 组装结构信息,可以将树状 Product 树状结构转换为一维数组,每个节点中有且只有 1 个 TopoShape,且没有子节点。

// header file
// ...
// or
AMCAX::STEP::StepDataList shapesN2 = AMCAX::STEP::StepDataTool::Flatten(shapes);
Class of tools for StepData.
static AMCAX_API AMCAX::STEP::StepDataList Flatten(const AMCAX::STEP::StepDataList &shapes, bool unrolling=true)
Create a copy of the StepData tree, and flatten it into an array.
static AMCAX_API AMCAX::STEP::StepDataList & FlattenInplace(AMCAX::STEP::StepDataList &shapes, bool unrolling=true)
Flatten a StepData tree into an one-dimensional array, inplace.

获取 sub-shape 的属性信息

获取到具体的 TopoShape 之后,可以通过 TopoIterator、TopoExplorer、TopoExplorerTool 等由 AMCAX 内核提供的接口获取其 sub-shape。 可以通过以下代码获取其名称等信息:

// 假设 shapes 中包含至少一个 Product 树
std::shared_ptr<AMCAX::STEP::StepData> treeroot = shapes[0];
// 假设当前 StepData 节点中包含至少一个 TopoShape,且其中类型为 Compound
AMCAX::TopoShape compound = treeroot->Shapes()[0];
const AMCAX::STEP::PropertyChain& props = treeroot->PropertyChains()[0];
// PropertyChain 作为一个 std::vector 的别名,其元素类型为 std::pair<TopoShape, StepPropertyUnit>;
// 由 StepReader 输出的 PropertyChain 中,每个 sub-shape 均只会出现一次;
// StepPropertyUnit 重载了 operator+=(),允许不同的对象之间进行合并、覆盖操作。
// 因此,如果用户预期对属性会有比较频繁的查找和更新操作,
// 也可以尝试构建 std::unordered_map<TopoShape, StepPropertyUnit>
for (AMCAX::TopoExplorer exp(compound, AMCAX::ShapeType::Solid);
exp.More();
exp.Next())
{
const AMCAX::TopoShape solid = exp.Current();
auto it = std::find_if(props.begin(), props.end(),
[&solid](const AMCAX::STEP::PropertyPair& pair)
{ return solid.IsSame(pair.first); });
if (it != props.end())
{
const StepPropertyUnit& property = it->second;
if (property.NameHasValue())
{ /* do something with name */ }
if (property.ColorHasValue())
{ /* do something with color */ }
}
}
Class of a tool for exploring the B-Rep structure
定义 TopoExplorer.hpp:14
AMCAX_API bool More() const
Does the explorer have more shapes
Base class of shape, containing an underlying shape with a location and an orientation
定义 TopoShape.hpp:15

(仅 Mesh)获取 Mesh 信息

由于 Mesh 数据是由 TopoShape 转换而来的,因此当且仅当一个 StepMeshData 节点中包含 TopoShape 数据时,才可能出现对应的 Mesh 数据。 Mesh 的转换工作由 StepMesh 类完成,可以参考 <step/mesh/StepMesh.hpp> 获取更多信息。现阶段 StepMesh 只接受类型为 Solid 的 TopoShape。 参考以下代码,获取 Mesh 数据以及对应的 TopoShape,名称,颜色等信息:

// 此处假设 shapes 中包含至少一个 StepMeshData 树
std::shared_ptr<AMCAX::STEP::StepMeshData<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>> root = shapes[0];
// 获取当前节点的所有 Mesh 数据
std::vector<std::vector<AMCAX::StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>> meshes = root->Meshes();
// 此处假设该 StepMeshData 树的 root 节点中包含 2 个 TopoShape,即 Shapes().size() 为 2
// 此处假设第一个 Shape 的类型为 Compound,其中包含 3 个 solid,一个 face
// 此处假设第二个 Shape 的类型为 Solid
std::cout << meshes.size() << '\n'; // "2",每个 TopoShape,无论其中包含几个 Solid,
// 都与一个 std::vector<AMCAX::StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>> 对应
AMCAX::TopoShape& shape0 = root->Shapes()[0];
std::vector<StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>& meshes0 = meshes[0];
std::cout << meshes0.size() << '\n'; // "3",因为该 Compound 拥有 3 个 Solid sub-shape,且 StepMesh 只处理 Solid 类型
AMCAX::TopoShape& shape1 = root->Shapes()[1];
std::vector<StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>& meshes1 = meshes[1];
std::cout << meshes1.size() << '\n'; // "1",因为第二个 shape 本身类型为 Solid,因此只包含一个 solid
// 获取第一个 shape 对应的 Mesh 数据,name、color 信息
for (auto& pr : solidSet0)
{
const AMCAX::TopoShape& solid = pr->first;
int solidIndex = pr->second;
// 通过 index 访问对应的 Mesh
// 关于获取 solid name 和 solid color 的部分,请参考前一小节中的内容
// doSomethingWith(curMesh, name, color);
}
// 获取第一个 shape 对应的 Mesh 数据,name、color 信息
// 与第一个 shape 的代码基本一致,此处不再重复
用于读取 STEP 文件并将实体转换为网格的类
定义 StepMeshData.hpp:21

(仅 Mesh)将 StepMeshData 转换为 StepData

在部分情况下,也会出现需要将 StepMeshData 转换为基类 StepData、或是将 StepMeshDataList 转换为 StepDataList 的场景。 可以参考以下代码:

using MeshDataList = AMCAX::STEP::StepMeshDataList<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>;
MeshDataList smdl = reader.GetShapes();
AMCAX::STEP::StepDataList sdl;
sdl.reserve(smdl.size());
for (auto& smdptr : smdl)
{
sdl.push_back(smdptr->ToStepData());
}

(仅 TopoShape) 导出部分

导入头文件

本示例程序中 Writer 部分只需要引入一个头文件,通过以下代码将该头文件加入程序:

用于将 TopoShape 对象转换为 STEP 文件的类

创建写入器实例

以下代码通过指定输出文件之文件名(路径)的方式创建并初始化一个 Writer 实例:

AMCAX::STEP::StepWriter writer("/path/to/output.step");
用于将 TopoShape 对象转换为 STEP 文件的类
定义 StepWriter.hpp:25

同 Reader 一样, Writer 也可以接受输入流作为构造参数:

std::ofstream ofs("/path/to/output.step");
if (!ofs.is_open())
{
return 1;
}

注意:若构造器中指定的文件已经存在,操作会导致其被覆盖。

(可选)设置缩放

只要输入的 TopoShape 符合输出条件,Writer 就会假设其单位长度为 1 毫米,并直接将其数值以不做缩放的方式输出至文件。 然而,假使该 TopoShape 创建时使用了其他单位,这会导致导出的图形尺寸不符。

虽然不会对输出的数值进行缩放,但可以通过以下代码调整 STEP 文件中定义的单位长度:

writer.SetUnit(AMCAX::STEP::StepLengthUnit::PresetLengthUnit::CENTIMETRE);

原则上来说,这一步需要在进行写入之前完成;

(可选)设置进度汇报

和 Reader 一样,Writer 也使用回调函数进行进度汇报,参考以下代码:

writer.SetProgressCallback(
{
std::cout << " State: " << int(state) << std::endl;
if (c2.type == AMCAX::STEP::StepProgressCarrier::U64_TYPE && c2.payload.u64)
{
std::cout << " Carrier1: " << c1.payload.u64 << std::endl;
std::cout << " Carrier2: " << c2.payload.u64 << std::endl;
}
});

进行写入

写入文件头

为了在 STEP 文件头部添加必要的信息,用户需要首先调用:

writer.Init();

这一步将在文件中输出 ISO 标准编号,应用协议版本,以及其它一些必要的实体。 为了保证文件格式,需要在 Init() 执行完毕后再将 TopoShape 写入 Writer。

写入 TopoShape

无论是一次性将包含所有 TopoShape 的 AMCAX::STEP::StepDataList 传入 Writer,还是多次调用函数传入,都有一样的效果。 也就是说,除去出于展示需要而特别指定颜色的部分,以下两段代码的效果是一致的:

// 假设 StepDataList shapes 包含至少3个元素;
AMCAX::STEP::StepDataList shape_part_0 = {shapes.begin(), shapes.begin() + shapes.size() - 2};
std::shared_ptr<AMCAX::STEP::StepData> shape_part_1 = shapes[shapes.size() - 2];
AMCAX::TopoShape shape_last = shapes.back()->Shapes().front();
writer.WriteShapes(shape_part_0);
writer.WriteShape(shape_part_1);
writer.WriteShape(shape_last);

writer.WriteShapes(shapes);

另外也可以通过这种方式将多个来源的图形数据输出至同一个 STEP 文件。

完成输出并保存文件

在添加所有 TopoShape 到 Writer 之后,为了将文件收尾,并保存至硬盘,需要执行以下代码:

writer.Done();

这段代码将添加符合 STEP 格式规范文件结尾,并关闭文件输出流(当且仅当该 Writer 实例是使用文件(路径)创建的)。 注意:如果再次对该 Writer 实例调用 Init() 函数,已输出的文件将会被覆盖。

简化接口

AMCAX::STEP::StepDataTool 类中,亦提供了简化的接口提供导入、导出 TopoShape 的功能。注意其中零件名称、装配、颜色、渲染等信息可能不会被保留。 其使用方式如下:

bool success = AMCAX::STEP::StepDataTool::Read(shape, "/path/to/input.step");
if (success)
{
AMCAX::STEP::StepDataTool::Write(shape, "/path/to/output.step");
}
static AMCAX_API bool Write(const AMCAX::TopoShape &s, std::ostream &os)
Write a TopoShape to a stream, in STEP format.
static AMCAX_API bool Read(AMCAX::TopoShape &s, std::istream &is)
Read a TopoShape from a stream, in STEP format.

点击这里example01可获得 Reader 的完整源码,大家根据学习需要自行下载。 点击这里example02可获得 Mesh Reader 的完整源码,大家根据学习需要自行下载。

附录

#include <iostream>
// 输出 Product 树状结构
void printProductName(const std::shared_ptr<AMCAX::STEP::StepData> node, int indent = 0)
{
for (int i = 0; i < indent; ++i)
{
std::cout << "| ";
}
std::cout << node->ProductName() << std::endl;
for (const std::shared_ptr<AMCAX::STEP::StepData> child : node->Children())
{
printProductName(child, indent + 1);
}
}
// 输出 Product 树中所有 Solid 的命名
void printSolidName(const std::shared_ptr<AMCAX::STEP::StepData> node, int indent = 0)
{
for (int i = 0; i < indent; ++i)
{
std::cout << "| ";
}
for (size_t i = 0; i < node->Shapes().size(); ++i)
{
const AMCAX::TopoShape& origShape = node->Shapes()[i];
const AMCAX::STEP::PropertyChain& props = node->PropertyChains()[i];
for (AMCAX::TopoExplorer SolidExp(origShape, AMCAX::ShapeType::Solid);
SolidExp.More();
SolidExp.Next())
{
const AMCAX::TopoShape& solid = SolidExp.Current();
auto it = std::find_if(props.begin(), props.end(),
[&solid](const AMCAX::STEP::PropertyPair& pair)
{ return solid.IsSame(pair.first); });
if (it != props.end() && (*it).second.NameHasValue())
{
std::string name = (*it).second.Name();
std::cout << '"' << name << '"' << ',' << ' ';
}
std::cout << std::endl;
}
}
for (const std::shared_ptr<AMCAX::STEP::StepData> child : node->Children())
{
printSolidName(child, indent + 1);
}
}
int main()
{
AMCAX::STEP::StepDataList shapes;
/**** Reading Part ****/
AMCAX::STEP::StepReader reader("./data/bed214T.step");
// 设置导入的图形在内核中使用的单位。此操作会造成图形的缩放。
reader.SetUnit(AMCAX::STEP::StepLengthUnit::PresetLengthUnit::METRE);
bool topo_success = reader.Read();
if (!topo_success)
{
return -1;
}
shapes = reader.GetShapes();
// 输出 Product 树的名称以树中所有 Solid 的名称
for (std::shared_ptr<AMCAX::STEP::StepData> root : shapes)
{
printProductName(root);
printSolidName(root);
}
// 将 StepDataList 的树状结构转换为一维数组
AMCAX::STEP::StepDataList flatten = AMCAX::STEP::StepDataTool::Flatten(shapes);
/**** Writing Part ****/
AMCAX::STEP::StepWriter writer("./output.step");
// 设置导出的图形在 STEP 文件中的格式。此操作只会影响输出的单位信息。
writer.SetUnit(AMCAX::STEP::StepLengthUnit::PresetLengthUnit::METRE);
writer.Init();
writer.WriteShapes(shapes);
writer.Done();
}
Class of a tool for exploring the B-Rep structure
Class of iterator for B-Rep structure