概述
本教程提供了使用 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
或者
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 或更多节点的树状结构,描述零件的装配。 其中主要包含:
- 当前 Product 的名称、描述信息,通过 ProductName() 与 Description() 获取;
- 当前 Product 的子 Product,即当前 StepData 节点的子树,数量为 0 或更多,通过 Children() 获取;
- 当前 Product 的 TopoShape,数量为 0 或更多,通过 Shapes() 获取;
- 当前 Product 下针对每个 TopoShape 的属性(如名称、颜色)和缩放信息,通过 PropertyChains() 与 Factors() 获取;
- 当前 Product 装配至上级 Product 时使用的位移、旋转信息,通过 Transformation() 获取;
Mesh Reader
结构、成员与 StepData 基本一致,但是增加了如下变量:
- 当前 Product 的 Mesh 数据,通过 Meshes() 获取。
(可选,仅 TopoShape)将树状 StepData 转换为一维数组
如果不需要保留 Product 的 Product 组装结构信息,可以将树状 Product 树状结构转换为一维数组,每个节点中有且只有 1 个 TopoShape,且没有子节点。
获取 sub-shape 的属性信息
获取到具体的 TopoShape 之后,可以通过 TopoIterator、TopoExplorer、TopoExplorerTool 等由 AMCAX 内核提供的接口获取其 sub-shape。 可以通过以下代码获取其名称等信息:
std::shared_ptr<AMCAX::STEP::StepData> treeroot = shapes[0];
const AMCAX::STEP::PropertyChain& props = treeroot->PropertyChains()[0];
exp.Next())
{
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())
{ }
if (property.ColorHasValue())
{ }
}
}
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,名称,颜色等信息:
std::shared_ptr<AMCAX::STEP::StepMeshData<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>> root = shapes[0];
std::vector<std::vector<AMCAX::StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>> meshes = root->Meshes();
std::cout << meshes.size() << '\n';
std::vector<StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>& meshes0 = meshes[0];
std::cout << meshes0.size() << '\n';
std::vector<StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>& meshes1 = meshes[1];
std::cout << meshes1.size() << '\n';
for (auto& pr : solidSet0)
{
int solidIndex = pr->second;
}
用于读取 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 实例:
用于将 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 文件头部添加必要的信息,用户需要首先调用:
这一步将在文件中输出 ISO 标准编号,应用协议版本,以及其它一些必要的实体。 为了保证文件格式,需要在 Init() 执行完毕后再将 TopoShape 写入 Writer。
写入 TopoShape
无论是一次性将包含所有 TopoShape 的 AMCAX::STEP::StepDataList 传入 Writer,还是多次调用函数传入,都有一样的效果。 也就是说,除去出于展示需要而特别指定颜色的部分,以下两段代码的效果是一致的:
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];
writer.WriteShapes(shape_part_0);
writer.WriteShape(shape_part_1);
writer.WriteShape(shape_last);
或
writer.WriteShapes(shapes);
另外也可以通过这种方式将多个来源的图形数据输出至同一个 STEP 文件。
完成输出并保存文件
在添加所有 TopoShape 到 Writer 之后,为了将文件收尾,并保存至硬盘,需要执行以下代码:
这段代码将添加符合 STEP 格式规范文件结尾,并关闭文件输出流(当且仅当该 Writer 实例是使用文件(路径)创建的)。 注意:如果再次对该 Writer 实例调用 Init() 函数,已输出的文件将会被覆盖。
简化接口
在 AMCAX::STEP::StepDataTool 类中,亦提供了简化的接口提供导入、导出 TopoShape 的功能。注意其中零件名称、装配、颜色、渲染等信息可能不会被保留。 其使用方式如下:
点击这里example01可获得 Reader 的完整源码,大家根据学习需要自行下载。 点击这里example02可获得 Mesh Reader 的完整源码,大家根据学习需要自行下载。
附录
#include <iostream>
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);
}
}
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::STEP::PropertyChain& props = node->PropertyChains()[i];
SolidExp.Next())
{
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;
reader.SetUnit(AMCAX::STEP::StepLengthUnit::PresetLengthUnit::METRE);
bool topo_success = reader.Read();
if (!topo_success)
{
return -1;
}
shapes = reader.GetShapes();
for (std::shared_ptr<AMCAX::STEP::StepData> root : shapes)
{
printProductName(root);
printSolidName(root);
}
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