Overview
This tutorial introduces the basic usage of AMCAX STEP Reader (hereafter referred to as Reader or StepReader) and AMCAX STEP Writer (hereafter Writer or StepWriter). In addition, this module provides AMCAX STEP Mesh Reader (hereafter StepMeshReader) for converting read data to Mesh format. Currently, there is no interface to export Mesh data in the STEP format.
Prerequisites
Developers should be familiar with modern C++ programming, 3D geometric modeling, and B-Rep topology. The AMCAX kernel and StepReader / StepWriter are developed based on the C++17 standard and make extensive use of STL containers and algorithms.
Import Section
Include Header Files
StepReader
In the Reader part of this example program, you only need to include one header file:
Class for reading and translating STEP files into TopoShape objects.
StepMeshReader
If you need to use Mesh-related interfaces, use StepMeshReader. StepMeshReader is defined in the following header file. It is a subclass of Reader with additional Mesh-related features.
Class for reading STEP file and convert solids / shells to mesh.
In addition, since StepMeshReader is a template class that can accept various Traits, if the relevant macro is not defined in the build options, you can enable the desired Traits type in your code as follows:
#define AMCAXMeshing_Enable_CommonTraits
Creating Reader Objects
StepReader
STEP Reader does not provide static functions; all operations are instance-based. The constructor takes a filename (std::string) or an input stream (std::istream) as a parameter:
std::ifstream ifs("/path/to/input.step");
if (!ifs.is_open())
{
return 1;
}
Class for reading and translating STEP files into TopoShape objects.
Definition StepReader.hpp:29
Or
StepMeshReader
Similarly, StepMeshReader provides the same constructors:
MeshReader reader(ifs);
Class for reading STEP file and convert solids / shells to mesh.
Definition StepMeshReader.hpp:26
Or
MeshReader reader("/path/to/input.step");
(Optional) Set Scaling
By default, Reader treats millimeters as the unit and scales the imported model accordingly. You can adjust the scaling setting as follows:
reader.SetUnit(AMCAX::STEP::StepLengthUnit::PresetLengthUnit::NOSCALE);
After conversion, you can also obtain the unit information specified in the STEP file via StepReader::GetUnit(), StepMeshReader::GetUnit(), StepData::Factors(), or StepMeshData::Factors().
(Optional) Set Progress Callback
Both Reader and StepMeshReader support progress reporting via callback functions. However, their parameter types differ slightly.
StepReader
Usually, c1 represents the number of processed shapes, and c2 represents the total number of shapes.
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.
Definition StepProgress.hpp:54
A carrier for carrying data the progress of the STEP process.
Definition StepProgress.hpp:18
For each state and its parameters, refer to <step/StepProgress.hpp>.
StepMeshReader
The callback function for StepMeshReader is essentially the same, with a slightly different State type:
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.
Definition StepMeshProgress.hpp:16
For state and parameter meanings, refer to <step/mesh/StepMeshProgress.hpp>.
Reading Files
After constructing a Reader or StepMeshReader object, you can read the file:
bool topo_success = reader.Read();
For both Reader and StepMeshReader, this completes the translation from STEP to TopoShape. The Read() method blocks the current thread until parsing completes. Depending on hardware and input file complexity, runtime may range from milliseconds to dozens of minutes. If you use std::istream as a parameter in the previous step, ensure the stream remains valid until this function completes. If reading fails, Read() returns false.
Obtaining Partial Results Early
Although Read() blocks the process, some data may be available early or midway and can be accessed in advance. You can pass AMCAX::STEP::DataCallback as a parameter to Read(); this callback will be invoked when relevant data is ready.
const AMCAX::STEP::ProductPtr p,
size_t rep_idx)
{
switch (e)
{
{
break;
}
{
break;
}
{
break;
}
default:;
}
});
DataEvent
Definition StepProgress.hpp:134
@ ProductReady
This event indicates that the Product tree is fully constructed, all the data, other than Shapes,...
Definition StepProgress.hpp:142
@ LabelReady
This event comes with 2 parameters. he first one is the pointer to the StepData object....
Definition StepProgress.hpp:152
@ ShapeReady
This event comes with 2 parameters. The first one is the pointer to the StepData object....
Definition StepProgress.hpp:147
Note: Since the callback is called directly in the execution thread, if your callback is too complex, it may affect subsequent computations. If you move or modify data (e.g. std::move, StepData::AddShape) within the Read() call, undefined behavior may occur.
(Mesh Only) Convert TopoShape to Mesh
After STEP translation is complete, you can convert TopoShape(s) to Mesh.
bool mesh_success = reader.ToMesh();
ToMesh() accepts an optional double parameter for meshing.
Creating Data Containers
StepReader
Both Reader and Writer modules use AMCAX::STEP::StepDataList for data transfer:
AMCAX::STEP::StepDataList shapes;
The type AMCAX::STEP::StepData is defined in <step/StepData.hpp>, but usually you do not need to include this header explicitly.
StepMeshReader
Similarly, StepMeshReader uses AMCAX::STEP::StepMeshDataList for data transfer:
AMCAX::STEP::StepMeshDataList<AMCAX::Meshing::Mesh::TriSoupTraits_Coord> shapes;
Defined in <step/mesh/StepMeshData.hpp>.
Obtaining Results
For both StepReader and StepMeshReader, the code to get translation results is the same:
shapes = reader.GetShapes();
StepReader
Depending on the file and conversion, shapes will contain 0 or more StepData objects. StepData corresponds to a Product entity in the STEP file and is used to transfer STEP data. StepData.hpp also defines ProductPtr as an alias for std::shared_ptr<StepData>. This class mainly includes the following members:
- Name and description of the current Product, accessed via ProductName() and Description();
- Child Products of the current Product (the node's subtree), via Children() / ChildAt(size_t);
- TopoShape(s) of the current Product, via Shapes() / ShapeAt(size_t);
- Properties (e.g. name, color) and scaling of each TopoShape under the Product, via PropertyChains() and Factors();
- Transformation info (position, rotation) when assembling this Product, via Location().
If multiple Product nodes form a tree via Children(), this represents assembly between Products. You can traverse StepDataList and check whether every StepData's ChildrenSize() is zero to see if assemblies are present. For assemblies, since a Product can be assembled into different parents with different transformations, StepData introduces the concept of a Shadow node to avoid duplicated information.
Shadow Nodes
A Shadow node is defined as a node containing only transformation info (Location). You can check StepData::IsShadow() to determine if a node is a Shadow, and use StepData::Target() to get the actual data node. Although Shadow nodes do not directly store shapes, calls to Shapes()/Children() etc. will automatically return data from the Target node. If you do not need Shadow node features, you can convert the entire Product tree to regular nodes as follows:
void ConvertToNormal(ProductPtr& root)
{
if (root->IsShadow())
{
root->MarkNormal();
}
for(size_t i = 0; i < root->ChildrenSize(); ++i)
{
ConvertToNormal(root->ChildAt(i));
}
}
ProductPtr node = shapes[0];
ConvertToNormal(node);
StepMeshReader
The structure and members are basically the same as StepData, with the following addition:
- Mesh data of the current Product, accessed via Meshes().
(Optional, TopoShape Only) Flatten Product Tree to Array
If you do not need to keep the assembly structure, you can flatten the Product tree into a one-dimensional array. Each node will have exactly one TopoShape and no children.
Getting Sub-shape Property Info
Once you have a TopoShape, use TopoIterator, TopoExplorer, or TopoExplorerTool from the AMCAX kernel to access sub-shapes. The following code retrieves their names and other info:
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.ShapeStyleHasValue())
{
if(property.GetShapeStyle().SurfaceStyleHasValue())
{
auto surfacestyle = property.GetShapeStyle().GetSurfaceStyle();
if(surfacestyle.colorHasValue())
{}
auto side = surfacestyle.getSide();
{}
}
if(property.GetShapeStyle().CurveStyleHasValue())
{
auto curvestyle = property.GetShapeStyle().GetCurveStyle();
if(curvestyle.colorHasValue())
{}
if(curvestyle.fontHasValue())
{}
if(curvestyle.widthHasValue())
{}
}
}
}
}
Class of a tool for exploring the B-Rep structure.
Definition TopoExplorer.hpp:14
AMCAX_API bool More() const noexcept
Does the explorer have more shapes.
Base class of shape, containing an underlying shape with a location and an orientation.
Definition TopoShape.hpp:15
(Mesh Only) Get Mesh Info
Since Mesh data is converted from TopoShape, a StepMeshData node will have Mesh data only if it contains TopoShape data. Mesh conversion is handled by StepMesh. For more info see <step/mesh/StepMesh.hpp>. Currently, StepMesh only accepts TopoShape of type Solid. See the following code for how to get Meshes, corresponding TopoShapes, names, and colors:
std::shared_ptr<AMCAX::STEP::StepMeshData<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>> root = shapes[0];
std::cout << meshes.size() << '\n';
std::vector<StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>& meshes0 = root->MeshAt(0);
std::cout << meshes0.size() << '\n';
std::vector<StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>& meshes1 = root->MeshAt(1);
std::cout << meshes1.size() << '\n';
std::vector<AMCAX::TopoShape> solidSet0;
{
solidSet0.push_back(exp.Current());
}
for (size_t solidIndex = 0; solidIndex < solidSet0.size(); ++solidIndex)
{
}
Class for reading STEP file and convert solid to mesh.
Definition StepMesh.hpp:30
(Mesh Only) Convert StepMeshData to StepData
Sometimes you may need to convert StepMeshData to base StepData, or StepMeshDataList to StepDataList. Refer to the following code:
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 Only) Extract TopoShape from StepData
While StepData::Shape() can be used to get TopoShape, since each StepData may contain multiple shapes and assembly may be complex, the module provides additional methods for complete TopoShape extraction:
Get Only the TopoShape Stored in the Current StepData
for (std::shared_ptr<StepData> node : reader.GetShapes())
{
TopoShape cur_shape = node->OneShape( false);
}
Get TopoShape from Current StepData and All Children
for (std::shared_ptr<StepData> node : reader.GetShapes())
{
TopoShape cur_shape = node->OneShape( true);
}
Get TopoShape Composed from Complete StepDataList
TopoShape result = AMCAX::STEP::StepDataTool::MakeCompound(reader.GetShapes());
(TopoShape Only) Export Section
Include Header File
In Writer part, you only need to include one header file:
Class for exporting TopoShape objects and related data to STEP files.
Create Writer Object
The following code creates and initializes a Writer object by specifying the output filename (path):
AMCAX::STEP::StepWriter writer("/path/to/output.step");
Similarly, Writer can accept an output stream as a constructor parameter:
std::ofstream ofs("/path/to/output.step");
if (!ofs.is_open())
{
return 1;
}
Class for exporting TopoShape objects and related data to STEP files.
Definition StepWriter.hpp:30
Note: If the output file exists, it will be overwritten.
(Optional) Set Scaling
As long as the input TopoShape meets the output condition, Writer assumes its unit is 1 mm and outputs it without scaling. However, if the TopoShape was created in another unit, this may result in incorrect size in the exported model.
While no scaling is performed on values, you can set the unit in the STEP file as follows:
writer.SetUnit(AMCAX::STEP::StepLengthUnit::PresetLengthUnit::CENTIMETRE);
You must set this before exporting shape data.
(Optional) Set Progress Callback
Like Reader, Writer also uses callback functions for progress reporting:
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;
}
});
Exporting
(Optional) Write File Header
To add required info to the STEP file header, call the following first:
This outputs the ISO standard number, application protocol version, and other necessary entities. To ensure file format, pass TopoShape to Writer after Init() completes. By default, Writer auto-calls Init().
Write TopoShape
You can pass all TopoShape in AMCAX::STEP::StepDataList to Writer at once, or call functions multiple times; the effect is the same (except for color specification):
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);
Or
writer.WriteShapes(shapes);
Write Label
AMCAXAf Label is a geometric format supporting properties. You can export shapes with properties by passing Label to Writer:
writer.WriteShape(root);
The class of Label.
Definition Label.hpp:27
However, since StepData stores both TopoShape and Label, you must specify which to use when passing StepData to Writer:
auto node = std::make_shared<AMCAX::STEP::StepData>();
node->AddShape(root);
writer.WriteShape(node);
writer.WriteLabel(root);
Finish Exporting and Save File
By default, StepWriter performs actual exporting and closes output stream at destruction (only when created with a file path). You can also manually finish exporting:
Note: If you call Init() on the Writer object again, the file will be overwritten.
Simplified Interface
AMCAX::STEP::StepDataTool provides utility functions for StepDataList generated by StepReader, and a simplified interface for importing/exporting TopoShape. Note: Part name, assembly, color, and rendering info may not be preserved. Usage:
Click here example01 to download the complete Reader source code as example01. Click here example02 to download the complete StepMeshReader source code as example02.
Appendix
#include <iostream>
void printProductName(const std::shared_ptr<AMCAX::STEP::StepData> node, int indent = 0)
{
for (int i = 0; i < indent; ++i)
{
std::cout << "| ";
}
if (node->IsShadow())
{
std::cout << "SHADOW: " << node->ProductName() << std::endl;
}
else
{
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 (size_t i = 0; i < node->ShapesSize(); ++i)
{
const AMCAX::STEP::PropertyChain& props = node->PropertyChainAt(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::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);
}
std::shared_ptr<AMCAX::STEP::StepData> treeroot = shapes[0];
const AMCAX::STEP::PropertyChain& props = treeroot->PropertyChains()[0];
{
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.ShapeStyleHasValue())
{
if(property.GetShapeStyle().SurfaceStyleHasValue())
{
auto surfacestyle = property.GetShapeStyle().GetSurfaceStyle();
if(surfacestyle.colorHasValue())
{}
auto side = surfacestyle.getSide();
{}
}
if(property.GetShapeStyle().CurveStyleHasValue())
{
auto curvestyle = property.GetShapeStyle().GetCurveStyle();
if(curvestyle.colorHasValue())
{}
if(curvestyle.fontHasValue())
{}
if(curvestyle.widthHasValue())
{}
}
}
}
}
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.