AMCAX Kernel 1.0.0.0
A Step File Reading And Writing Example

Overview

This tutorial provides basic instructions on how to use the AMCAX STEP Reader (hereafter referred to as Reader or TopoShape Reader) and AMCAX STEP Writer (hereafter referred to as Writer). In addition, this module also provides the AMCAX STEP Mesh Reader (hereafter referred to as Mesh Reader) for converting read data into Mesh format. However, an interface for exporting Mesh data using the STEP protocol is currently not available.

Required Knowledge

The developers need to have some basic knowledge including the modern C++ programming language, 3D geometric modeling concepts, and the B-Rep topology structure. In specific, the AMCAX kernel and the AMCAX STEP Reader / Writer are designed and implemented using C++ 17 standard, and STL containers and algorithms are used as basic data structures and utilities. The geometric entities are represented by a Boundary-Representation (B-Rep) structure, including geometry objects and topology structures.

The Reader Section

Import The Header Files

TopoShape Reader

The AMCAXStep module provides a header file for Reader:

Class used for translating STEP file to TopoShape objects.

Mesh Reader

And there is also a header file for Mesh Reader. You need to make sure the macro for traits is defined before importing the header file:

// Can be replaced with other supported traits.
#define AMCAXMeshing_Enable_CommonTraits
Class for reading STEP file and convert solid to mesh.

Or, if the macro is already defined in compiler flags:

You only need to include the corresponding header file to use Mesh Reader.

Declare And Construct A Reader Instance

The STEP Reader does not provide static functions, so all calls must be made using an instance. To read a STEP file, a reader instance is required.

TopoShape Reader

When constructing, you need to pass a filename (std::string) or input stream (std::istream) as a parameter:

std::ifstream ifs("/path/to/input.step");
if (!ifs.is_open())
{
return 1;
}
Class used for translating STEP file to TopoShape objects.
Definition: StepReader.hpp:26

Or

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

Mesh Reader

Similarly, you can construct a Mesh Reader instance with following code:

MeshReader reader(ifs);
Class for reading STEP file and convert solid to mesh.
Definition: StepMeshReader.hpp:26

Or

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

(Optional) Configure The Scaling Behavior

By default, Reader will use millimeter as the unit length and scale all imported graphics according to the input length factor. This behavior can be configured via following code:

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

The input length factor can be accessed via GetUnit() or StepData::Factors()/StepMeshData::Factors() after calling Read().

(Optional) Progress And Callback

For Both Reader and Mesh Reader, an optional user-defined callback function will be called during the process. The interface for them are almost identical:

TopoShape Reader

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:53
A carrier for carrying data the progress of the STEP process.
Definition: StepProgress.hpp:17

Usually, the value of c1 (if provided) represents the amount of shapes that have already been imported. While the value of c2 (if provided) represents the total amount of shapes that will be or have already been processed. You can find more information in the documentation of the header file.

Mesh Reader

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

You can find more information in the documentation for both StepProgress and StepMeshProgress.

Start The Actual Reading Process

After finishing the optional configurings of the reader, it is now ready for translating the STEP input to AMCAX shape format:

bool topo_success = reader.Read();

For both Reader and Mesh Reader, this is the step where the STEP input being translated to AMCAX TopoShape. Usually, it will call a few milliseconds to several minutes to complete the entire process. If a std::istream was passed to the constructor, the user needs to ensure that the life cycle of the istream will not expire before this function is executed. Read() will return false if failed to translate the STEP input.

(Mesh Only) Convert TopoShape to Mesh

After the STEP input being translated to TopoShape, you may now convert the TopoShape objects to Mesh format.

//
bool mesh_success = reader.ToMesh();

Or, you can use ToMesh(const double). The parameter will be used when executing the triangulation operation.

Get Translation Results.

Declaration A Container For Storing The Results

TopoShape Reader

StepDataList is an alias of std::vector<std::shared_ptr<StepData>>. It is defined in <step/StepData.hpp>, which is transitively included from <step/StepReader.hpp>.

AMCAX::STEP::StepDataList shapes;

Mesh Reader

As a derived class of Reader, Mesh Reader uses a derived class of StepDataList, StepMeshDataList, to transfer the results.

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

It was an alias of std::vector<std::shared_ptr<StepMeshData<Traits>>>, while StepMeshData is a template that is defined in <step/mesh/StepMeshData.hpp>.

Get the Result From Reader

shapes = reader.GetShapes();

TopoShape Reader

If the translating process succeed, one or more StepData may be added to the result-containing container variable, shapes.

However, you still cannot obtain the imported TopoShape objects just yet. This is because StepData is, instead of a shape representation, it is a represention of the Product_Definition entity, which is the unit used to represent assembly.

Each StepData object mainly contains:

  1. The name and description of current product node.
  2. The shape representations that are related to current product node. (no less than 0)
  3. The child Product nodes of current product node. (no less than 0)
  4. Properties of shape representations and their sub-shapes.
  5. The transformation used in the process of assembling into parent nodes. Check out <step/StepData.hpp> for more information.

Mesh Reader

In addition to the member variables of StepData, StepMeshData also contains:

  1. Mesh data for each TopoSolid in current product node. Check out <step/mesh/StepMeshData.hpp> for more information.

(Optional, TopoShape Only) Converting The Tree Structured StepData To A One-Dimensional Array

The user may want to convert the tree structured output to a one-dimensional array. This can be done with following code:

// header file
// ...
// or make a copy and flatten it
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.

All node will have and only have exactly one TopoShape object.

(TopoShape Only) Get The Properties Of Sub-Shapes

Once a specific TopoShape is obtained, its sub-shapes can be retrieved using interfaces provided by the AMCAX kernel, such as TopoIterator, TopoExplorer, and TopoExplorerTool. The following code snippet demonstrates how to retrieve the name and other information of a sub-shape:

// Assuming 'shapes' contains at least one Product tree
std::shared_ptr<AMCAX::STEP::StepData> treeroot = shapes[0];
// Assuming the current StepData node contains at least one TopoShape of type Compound
AMCAX::TopoShape compound = treeroot.Shapes()[0];
// Assuming 'compound' contains multiple TopoSolid objects
for (auto& pr : solidSet)
{
const TopoShape& solid = pr->first;
// Assuming the current 'solid' has name and color information
std::string subname = treeroot->PartName()[solid];
// ... do something with name
AMCAX::STEP::ShapeRGBA color = treeroot->PartColor()[solid];
// ... do something with color
}
Class for representation Color RGBA.
Definition: ShapeRGBA.hpp:18
static AMCAX_API void MapShapes(const TopoShape &s, ShapeType t, IndexSet< TopoShape > &shapeSet)
Construct a set of sub-shapes of given type.
Base class of shape, containing an underlying shape with a location and an orientation.
Definition: TopoShape.hpp:15

(Mesh Only) Get The properties And Mesh Data Of Sub-Shapes

Following code is an example of accessing Mesh data and its corresponding Property data.

// Assuming 'shapes' contains at least one StepMeshData tree
std::shared_ptr<AMCAX::STEP::StepMeshData<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>> root = shapes[0];
// Retrieve all Mesh data from the current node
std::vector<std::vector<AMCAX::STEP::StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>> meshes = root->Meshes();
// Assuming the root node of this StepMeshData tree contains 2 TopoShapes (Shapes().size() == 2)
// Assuming the first Shape is of type Compound and contains 3 solids and 1 face
// Assuming the second Shape is of type Solid
std::cout << meshes.size() << '\n'; // Expecting "2" - Each TopoShape, regardless of the number of Solids it contains,
// corresponds to a std::vector<AMCAX::StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>
AMCAX::TopoShape& shape0 = root->Shapes()[0];
std::vector<AMCAX::STEP::StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>& meshes0 = meshes[0];
std::cout << meshes0.size() << '\n'; // Expecting "3" - Since this Compound has 3 Solid sub-shapes,
// and StepMesh only processes Solid types
AMCAX::TopoShape& shape1 = root->Shapes()[1];
std::vector<AMCAX::STEP::StepMesh<AMCAX::Meshing::Mesh::TriSoupTraits_Coord>>& meshes1 = meshes[1];
std::cout << meshes1.size() << '\n'; // Expecting "1" - Since the second shape is itself of type Solid,
// it only contains one solid
// Retrieve the Solid lists and indices for both Shapes
AMCAX::IndexSet<AMCAX::TopoShape> solidSet0, solidSet1;
AMCAX::TopoExplorerTool::MapShapes(shape0, AMCAX::ShapeType::Solid, solidSet0);
AMCAX::TopoExplorerTool::MapShapes(shape1, AMCAX::ShapeType::Solid, solidSet1);
// Retrieve Mesh data, name, and color information for the first shape's corresponding Mesh
for (auto& pr : solidSet0)
{
const AMCAX::TopoShape& solid = pr->first;
int solidIndex = pr->second;
// Access the corresponding Mesh using the index
// Assuming the current solid has name and color information (need to verify in actual application)
std::string name = root->PartName()[solid];
AMCAX::STEP::ShapeRGBA color = root->PartColor()[solid];
// doSomethingWith(curMesh, name, color);
}
// The code for retrieving Mesh data, name, and color information for the second shape
// is identical to the code for the first shape, omitted here.
Class for reading STEP file and convert solid to mesh.
Definition: StepMesh.hpp:32

(Optional, Mesh Only) Converting StepMeshData To StepData

Sometimes the user may want to

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) The Writer Section

Import The Header Files

The AMCAXStep module provides a header file for Writer.

Class used for translating TopoShape objects to STEP file.

Declare And Construct A Writer Instance.

To export a STEP file, a writer instance is required.

AMCAX::STEP::StepWriter writer("/path/to/output.step");
Class used for translating TopoShape objects to STEP file.
Definition: StepWriter.hpp:25

Same to Reader, you can also construct Writer with a std::istream:

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

(Optional) Configure The Scaling Behavior

Under any circumstances Writer will export the shape representation without rescaling it. However, this may cause some problem if the shape was created with a length unit other than millimeter. You can configure the output unit with following code:

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

Even though having different length units for different shapes is possible, it's recommanded to use a consistent length unit.

(Optional) Progress And Callback

For Writer, a optional user-defined callback function will be called during the process. It is configurable via the interface below:

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;
}
});

Start Writing

The Header Section

Each valid STEP file requires a header section.

writer.Init();

This function will also setup the writer and output a few essential entities. Make sure it is called exactly once before writing any shapes.

The Data Section

You can either pass a StepData to the writer or pass a TopoShape to it. Note that

// Assuming StepDataList shapes contains no less than 3 shapes.
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);

The Footer Section

Each valid STEP file requires a footer section.

writer.Done();

Simplified Interface

In AMCAX::STEP::StepDataTool, there are several simplified interface for importing from, and exporting to STEP file. Check out <step/StepDataTool.hpp> for more information.

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.

Click here example01 to get the complete source code of the Reader, and download it according to your learning needs. Click here example02 to get the complete source code of the Mesh Reader, and download it according to your learning needs.

Appendix

#include <iostream>
// Print the name of the Product tree structure, recursively
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);
}
}
// Print all the name of sub-shapes of a Product tree structure, recursively
void printSubName(const std::shared_ptr<AMCAX::STEP::StepData> node, int indent = 0)
{
for (int i = 0; i < indent; ++i)
{
std::cout << "| ";
}
std::unordered_map<AMCAX::TopoShape, std::string> partname = node->PartName();
for (const AMCAX::TopoShape& shape : node->Shapes())
{
for (AMCAX::TopoIterator iter(shape); iter.More(); iter.Next())
{
if (auto it = partname.find(iter.Value()); it != partname.end())
{
std::cout << it->second << std::endl;
}
else
{
std::cout << "No PartName" << std::endl;
}
}
}
for (const std::shared_ptr<AMCAX::STEP::StepData> child : node->Children())
{
printSubName(child, indent + 1);
}
}
int main()
{
AMCAX::STEP::StepDataList shapes;
/**** Reading Part ****/
AMCAX::STEP::StepReader reader("/absolute/or/relative/path/to/input.step");
// Configure the scaling behavior
reader.SetUnit(AMCAX::STEP::StepLengthUnit::PresetLengthUnit::METRE);
bool topo_success = reader.Read();
if (!topo_success)
{
return -1;
}
shapes = reader.GetShapes();
// Print all the Product tree structure
for (std::shared_ptr<AMCAX::STEP::StepData> root : shapes)
{
printProductName(root);
printSubName(root);
}
// Convert a StepDataList to a one-dimensional array
AMCAX::STEP::StepDataList flatten = AMCAX::STEP::StepDataTool::Flatten(shapes);
/**** Writing Part ****/
AMCAX::STEP::StepWriter writer("/absolute/or/relative/path/to/output.step");
// Configure the length unit
writer.SetUnit(AMCAX::STEP::StepLengthUnit::PresetLengthUnit::METRE);
writer.Init();
writer.WriteShapes(shapes);
writer.Done();
}
Class of iterator for B-Rep structure.
Class of iterator for B-Rep structure.
Definition: TopoIterator.hpp:14
AMCAX_API bool More() const
Does the iterator has more shape.