Exchange Toolkit
Examples

Table of Contents

All of the example code snippets found below are extracted from the examples folder.

Attention
The sample code provided here and in the examples folder is provided as-is. You are free to use it and adapt it as needed.

Attaching attributes to B-Rep faces and edges

This snippet was extracted from examples/attrib/main.cpp.

// Get all unique parts from the model file
auto const parts = ts3d::getUniqueLeafEntities( loader.m_psModelFile, kA3DTypeAsmPartDefinition );
for( auto const part : parts ) {
// Attach an arbitrary attribute to the part
A3DRootBaseAttributeAdd( part, const_cast<char*>("part"), const_cast<char*>("foobar") );
// Get the B-Rep model(s) from the part definition
auto const brep_models = ts3d::getLeafInstances( part, kA3DTypeRiBrepModel );
for( auto const brep_model : brep_models ) {
// Obtain the scale associated with this model
ts3d::A3DRiBrepModelWrapper d( brep_model.back() );
ts3d::A3DTopoBodyWrapper topo_body( d->m_pBrepData );
ts3d::A3DTopoContextWrapper topo_context( topo_body->m_pContext );
A3DVector3dData scale;
A3D_INITIALIZE_DATA( A3DVector3dData, scale );
scale.m_dX = scale.m_dY = scale.m_dZ = ( topo_context->m_bHaveScale ? topo_context->m_dScale : 1. );
auto const faces = ts3d::getUniqueLeafEntities( brep_model.back(), kA3DTypeTopoFace );
for( auto const face : faces ) {
// add face attributes
ts3d::attachFaceAttributes( face, scale );
}
ts3d::InstancePathMap edge_instance_path_map;
auto const edges = ts3d::getUniqueLeafEntities( brep_model.back(), kA3DTypeTopoEdge, edge_instance_path_map );
for( auto const edge : edges ) {
auto const &edge_instances = edge_instance_path_map[edge];
ts3d::EntitySet owning_faces;
for( auto const &edge_instance : edge_instances ) {
// get the face referencing this edge instance
auto const face_it = std::find_if( std::begin( edge_instance ), std::end( edge_instance ), [](A3DEntity *ntt ) {
return ts3d::getEntityType( ntt ) == kA3DTypeTopoFace;
});
if( std::end( edge_instance ) != face_it ) {
owning_faces.insert( *face_it );
}
}
ts3d::attachEdgeAttributes( edge, owning_faces, scale );
}
}
}

Constructing a Bill of Materials

This snippet was extracted from examples/bom/main.cpp.

// An instance path map will be used to count the number of part occurrences
ts3d::InstancePathMap instance_paths;
// Obtain a set of unique part definition children
auto const part_definitions = ts3d::getUniqueLeafEntities( loader.m_psModelFile, kA3DTypeAsmPartDefinition, instance_paths );
// Iterator over each unique part
for( auto part_definition : part_definitions ) {
// Get the part's name or owner's name if empty
ts3d::Instance const part_instance( { part_definition } );
auto const part_name = part_instance.getName().empty() ? "<unknown>" : part_instance.getName();
// Print the part name and number of occurrences
std::cout << "\"" << part_name << "\": "
<< "(" << instance_paths[part_definition].size()
<< " instance"
<< (1 != instance_paths[part_definition].size() ? "s)" : ")")
<< std::endl;
}

Comparing tessellation to B-Rep

This snippet was extracted from examples/compare_brep_tess/main.cpp.

auto const ri_instances = ts3d::getLeafInstances( loader.m_psModelFile, kA3DTypeRiBrepModel );
for( auto ri_instance : ri_instances ) {
if( SHOULD_FORCE_RETESSELLATION ) {
A3DRWParamsTessellationData tess_params;
A3D_INITIALIZE_DATA( A3DRWParamsTessellationData, tess_params );
tess_params.m_eTessellationLevelOfDetail = kA3DTessLODMedium;
tess_params.m_bAccurateTessellation = false;
CheckResult( A3DRiRepresentationItemComputeTessellation( ri_instance.back(), &tess_params ) );
}
ts3d::A3DRiBrepModelWrapper brep_model_d( ri_instance.back() );
ts3d::A3DTopoBodyWrapper topo_body_d( brep_model_d->m_pBrepData );
ts3d::A3DTopoContextWrapper context_d( topo_body_d->m_pContext );
auto const brep_scale = context_d->m_bHaveScale ? context_d->m_dScale : 1.;
ts3d::RepresentationItemInstance const ri( ri_instance );
auto const tess3d = std::dynamic_pointer_cast<ts3d::Tess3DInstance>( ri.getTessellation() );
if( nullptr == tess3d ) {
continue;
}
// ensure the number of tess faces is equal to the number of b-rep faces
auto const n_tess_faces = tess3d->faceSize();
auto const brep_faces = ts3d::getLeafInstances( ri_instance.back(), kA3DTypeTopoFace );
if( n_tess_faces != brep_faces.size() ) {
std::cerr << "FAILURE: n_tess_faces (" << n_tess_faces << ") != brep_faces.size() (" << brep_faces.size() << ")" << std::endl;
failure_encountered = true;
}
// for each face, examine the loops
auto const max_face_idx = std::min( static_cast<std::size_t>( n_tess_faces ), brep_faces.size() );
for( auto face_idx = 0u; face_idx < max_face_idx; ++face_idx ) {
auto const tess_face = tess3d->getIndexMeshForFace( face_idx );
auto const brep_face = brep_faces[face_idx].back();
ts3d::A3DTopoFaceWrapper face_d( brep_face );
auto const surface = face_d->m_pSurface;
// ensure the number of tess loops is equal to the number of b-rep loops
auto const tess_loops = tess_face.loops();
auto const n_tess_loops = tess_loops.size();
auto const brep_loops = ts3d::getLeafInstances( brep_faces[face_idx].back(), kA3DTypeTopoLoop );
if( n_tess_loops != brep_loops.size() ) {
std::cerr << "FAILURE: n_tess_loops (" << n_tess_loops << ") != brep_loops.size() (" << brep_loops.size() << ")" << std::endl;
failure_encountered = true;
}
// for each loop, examine the edges
auto const max_loop_idx = std::min( n_tess_loops, brep_loops.size() );
for( auto loop_idx = 0u; loop_idx < max_loop_idx; ++loop_idx ) {
auto const &tess_loop = tess_loops[loop_idx];
// ensure the number of tess edges is equal to the number of b-rep coedges
auto const n_tess_edges = tess_loop._edges.size();
auto const brep_coedges = ts3d::getLeafInstances( brep_loops[loop_idx].back(), kA3DTypeTopoCoEdge );
if( n_tess_edges != brep_coedges.size() ) {
std::cerr << "FAILURE: n_tess_edges ( " << n_tess_edges << ") != brep_coedges.size() (" << brep_coedges.size() << ")" << std::endl;
failure_encountered = true;
}
auto const max_edge_idx = std::min(n_tess_edges, brep_coedges.size());
for( auto edge_idx = 0u; edge_idx < max_edge_idx; ++edge_idx ) {
auto const &tess_edge = tess_loop._edges[edge_idx];
auto const brep_coedge = brep_coedges[edge_idx];
ts3d::A3DTopoCoEdgeWrapper coedge_d( brep_coedge.back() );
ts3d::A3DTopoEdgeWrapper edge_d( coedge_d->m_pEdge );
A3DCrvBase const *curve = nullptr;
CheckResult( A3DTopoEdgeGetOrCompute3DCurve( brep_model_d->m_pBrepData, coedge_d->m_pEdge, &curve ) );
A3DIntervalData interval;
A3D_INITIALIZE_DATA( A3DIntervalData, interval );
if( edge_d->m_bHasTrimDomain ) {
interval = edge_d->m_sInterval;
} else {
CheckResult( A3DCrvGetInterval( curve, &interval ) );
}
for( auto const vertex_index : tess_edge._vertices ) {
A3DVector3dData pt;
A3D_INITIALIZE_DATA( A3DVector3dData, pt );
pt.m_dX = tess3d->coords()[vertex_index] / brep_scale;
pt.m_dY = tess3d->coords()[vertex_index + 1] / brep_scale;
pt.m_dZ = tess3d->coords()[vertex_index + 2] / brep_scale;
auto min_curve_distance = std::numeric_limits<double>::max();
// project the point to the curve
A3DUns32 n_solutions = 0u;
A3DDouble *parameters = nullptr, *distances = nullptr;
if( CheckResult( A3DCrvProjectPoint( curve, &pt, &n_solutions, &parameters, &distances ) ) ) {
for( auto n_solution = 0u; n_solution < n_solutions; ++n_solution ) {
if( distances[n_solution] < min_curve_distance ) {
min_curve_distance = distances[n_solution];
}
}
CheckResult( A3DCrvProjectPoint( nullptr, &pt, &n_solutions, &parameters, &distances ) );
}
// check resulting error against tolerance
auto const curve_error = min_curve_distance;// * brep_scale;
if( curve_error > TOLERANCE ) {
std::cerr << "FAILURE: [Distance to curve] " << curve_error << " > " << TOLERANCE << std::endl;
failure_encountered = true;
}
auto min_surf_distance = std::numeric_limits<double>::max();
// Project the point to the surface
A3DVector2dData *face_solutions = nullptr;
if( CheckResult( A3DSurfProjectPoint( surface, &pt, &n_solutions, &face_solutions, &distances ) ) ) {
for( auto n_solution = 0u; n_solution < n_solutions; ++n_solution ) {
if( distances[n_solution] < min_surf_distance ) {
min_surf_distance = distances[n_solution];
}
}
CheckResult( A3DSurfProjectPoint( nullptr, &pt, &n_solutions, &face_solutions, &distances ) );
}
// check resulting error against tolerance
auto const surface_error = min_surf_distance;// * brep_scale;
if( surface_error > TOLERANCE ) {
std::cerr << "FAILURE: [Distance to surface] " << surface_error << " > " << TOLERANCE << std::endl;
failure_encountered = true;
}
}
}
}
}
}

How to access Markup/PMI Linked Items

This snippet was extracted from examples/pmi_linked_items/main.cpp.

// Use the function to get all markup objects
auto const all_markups = ts3d::getLeafInstances( loader.m_psModelFile, kA3DTypeMkpMarkup );
std::cout << "This file contains " << all_markups.size() << " markups." << std::endl;
// Loop over each markup object and examine if each has any linked items
for( auto const this_markup : all_markups ) {
ts3d::Instance markup_instance( this_markup );
auto const linked_items = ts3d::getLeafInstances( this_markup.back(), kA3DTypeMiscMarkupLinkedItem );
if( ! linked_items.empty() ) {
std::cout << "Mark-up: \"" << markup_instance.getName() << "\" [" << markup_instance.getType() << "]" << std::endl;
}
for( auto const linked_item : linked_items ) {
ts3d::A3DMiscMarkupLinkedItemWrapper d( linked_item.back() );
if( kA3DTypeMiscReferenceOnTopology != ts3d::getEntityType( d->m_pReference ) ) {
// This code is only handling linked items that reference topology
std::cout << "Unhandled reference type: " << ts3d::Instance( { d->m_pReference } ).getType() << std::endl;
continue;
}
// we'll use this data wrapper to obtain info about the topology reference
auto const topo_brep_data_ptr = t->m_pBrepData;
if( nullptr == topo_brep_data_ptr ) {
continue;
}
// Do something unique for each possible type of referenced topology
switch( t->m_eTopoItemType ) {
case kA3DTypeTopoConnex:
{
auto const connex = ts3d::getLeafInstances( topo_brep_data_ptr, t->m_eTopoItemType )[t->m_puiAdditionalIndexes[0]];
ts3d::A3DTopoConnexWrapper d( connex.back() );
std::cout << "The linked connex contains " << d->m_uiShellSize << " shell(s)." << std::endl;
}
break;
case kA3DTypeTopoShell:
{
auto const shell = ts3d::getLeafInstances( topo_brep_data_ptr, t->m_eTopoItemType )[t->m_puiAdditionalIndexes[0]];
ts3d::A3DTopoShellWrapper d( shell.back() );
std::cout << "The linked shell contains " << d->m_uiFaceSize << " face(s)." << std::endl;
}
break;
case kA3DTypeTopoFace:
{
// Print some info about the B-Rep
auto const face = ts3d::getLeafInstances( topo_brep_data_ptr, t->m_eTopoItemType )[t->m_puiAdditionalIndexes[0]];
ts3d::A3DTopoFaceWrapper d( face.back() );
std::cout << "The linked face has a surface type: " << ts3d::Instance( { d->m_pSurface } ).getType() << " and contains " << d->m_uiLoopSize << " loop(s)." << std::endl;
// Print some info about the associated tessellation
auto const part_def = getPartDefinition( this_markup );
if( nullptr != part_def ) {
auto const ri_brep_models = ts3d::getLeafInstances( part_def, kA3DTypeRiBrepModel );
if( ! ri_brep_models.empty() ) {
auto const ri_brep_model = ri_brep_models.back();
ts3d::RepresentationItemInstance ri_instance( { ri_brep_model } );
if( auto const tess = std::dynamic_pointer_cast<ts3d::Tess3DInstance>( ri_instance.getTessellation() ) ) {
auto const index_mesh = tess->getIndexMeshForFace( t->m_puiAdditionalIndexes[0] );
std::cout << "The face's tessellation contains " << index_mesh.vertices().size()/3u << " triangles." << std::endl;
}
}
}
}
break;
case kA3DTypeTopoEdge:
case kA3DTypeTopoCoEdge:
{
// Print some info about the B-Rep
auto const faces = ts3d::getLeafInstances( topo_brep_data_ptr, kA3DTypeTopoFace );
auto const face = faces[t->m_puiAdditionalIndexes[0]].back();
auto const loops = ts3d::getLeafInstances( face, kA3DTypeTopoLoop );
auto const loop = loops[t->m_puiAdditionalIndexes[1]].back();
auto const coedges = ts3d::getLeafInstances( loop, kA3DTypeTopoCoEdge );
auto const coedge = coedges[t->m_puiAdditionalIndexes[2]].back();
auto const curve = d->m_pUVCurve ? d->m_pUVCurve : ts3d::A3DTopoEdgeWrapper( d->m_pEdge )->m_p3dCurve;
std::cout << "The linked coedge has a curve type: " << ts3d::Instance( { curve } ).getType() << std::endl;
// Print some info about the associated tessellation
auto const part_def = getPartDefinition( this_markup );
if( nullptr != part_def ) {
auto const ri_brep_models = ts3d::getLeafInstances( part_def, kA3DTypeRiBrepModel );
if( ! ri_brep_models.empty() ) {
auto const ri_brep_model = ri_brep_models.back();
ts3d::RepresentationItemInstance ri_instance( { ri_brep_model } );
if( auto const tess = std::dynamic_pointer_cast<ts3d::Tess3DInstance>( ri_instance.getTessellation() ) ) {
auto const index_mesh = tess->getIndexMeshForFace( t->m_puiAdditionalIndexes[0] );
auto const tess_loop = index_mesh.loops()[t->m_puiAdditionalIndexes[1]];
auto const tess_edge = tess_loop._edges[t->m_puiAdditionalIndexes[2]];
std::cout << "The edge's tessellation contains " << tess_edge._vertices.size() << " points." << std::endl;
}
}
}
}
break;
case kA3DTypeTopoUniqueVertex:
case kA3DTypeTopoMultipleVertex:
{
auto const faces = ts3d::getLeafInstances( topo_brep_data_ptr, kA3DTypeTopoFace );
auto const face = faces[t->m_puiAdditionalIndexes[0]].back();
auto const loops = ts3d::getLeafInstances( face, kA3DTypeTopoLoop );
auto const loop = loops[t->m_puiAdditionalIndexes[1]].back();
auto const edges = ts3d::getLeafInstances( loop, kA3DTypeTopoEdge );
auto const edge = edges[t->m_puiAdditionalIndexes[2]].back();
auto const vertices = ts3d::getLeafInstances( edge, kA3DTypeTopoVertex );
auto const vertex = vertices[t->m_puiAdditionalIndexes[3]].back();
auto const vertex_type = ts3d::getEntityType( vertex );
if( kA3DTypeTopoUniqueVertex == vertex_type ) {
std::cout << "The linked vertex is at (" << d->m_sPoint.m_dX << ", " << d->m_sPoint.m_dY << ", " << d->m_sPoint.m_dZ << ")" << std::endl;
} else {
// kA3DTypeTopoMultipleVertex
std::cout << "The linked vertex has multiple positions at: " << std::endl;
for( auto idx = 0u; idx < d->m_uiSize; ++idx ) {
std::cout << "(" << d->m_pPts[idx].m_dX << ", " << d->m_pPts[idx].m_dY << ", " << d->m_pPts[idx].m_dZ << ")" << std::endl;
}
}
}
break;
default:
break;
}
}
std::cout << std::endl;
}

Print curve types that make up a wire body

This snippet was extract from examples/wire_body_curve_types/main.cpp.

auto const wire_edges = ts3d::getLeafInstances( loader.m_psModelFile, kA3DTypeTopoWireEdge);
std::cout << "There are " << wire_edges.size() << " single wire edges." << std::endl;
for( auto const we : wire_edges ) {
ts3d::Instance i({ d->m_p3dCurve });
std::cout << "Curve: " << i.getType();
if (d->m_bHasTrimDomain) {
std::cout << " [" << d->m_sInterval.m_dMin << ", " << d->m_sInterval.m_dMax << "]";
}
std::cout << std::endl;
switch (i.leafType()) {
case kA3DTypeCrvPolyLine:
{
ts3d::A3DCrvPolyLineWrapper curve_data(i.leaf());
std::cout << curve_data->m_uiSize << " pts" << std::endl;
}
break;
case kA3DTypeCrvCircle:
{
ts3d::A3DCrvCircleWrapper curve_data(i.leaf());
auto const &o = curve_data->m_sTrsf.m_sOrigin;
std::cout << "Center (" << o.m_dX << ", " << o.m_dY << ", " << o.m_dZ << ")" << std::endl;
std::cout << "Radius: " << curve_data->m_dRadius << std::endl;
}
break;
case kA3DTypeCrvEllipse:
{
ts3d::A3DCrvEllipseWrapper curve_data(i.leaf());
auto const &o = curve_data->m_sTrsf.m_sOrigin;
std::cout << "Center (" << o.m_dX << ", " << o.m_dY << ", " << o.m_dZ << ")" << std::endl;
std::cout << "X Radius: " << curve_data->m_dXRadius << std::endl;
std::cout << "Y Radius: " << curve_data->m_dYRadius << std::endl;
}
break;
case kA3DTypeCrvLine:
{
ts3d::A3DCrvLineWrapper curve_data(i.leaf());
auto const &o = curve_data->m_sTrsf.m_sOrigin;
auto const &x = curve_data->m_sTrsf.m_sXVector;
auto const &y = curve_data->m_sTrsf.m_sYVector;
std::cout << "Origin (" << o.m_dX << ", " << o.m_dY << ", " << o.m_dZ << ")" << std::endl;
std::cout << "X (" << x.m_dX << ", " << x.m_dY << ", " << x.m_dZ << ")" << std::endl;
std::cout << "Y (" << y.m_dX << ", " << y.m_dY << ", " << y.m_dZ << ")" << std::endl;
}
break;
}
}

Write an OBJ file

This snippet was extract from examples/obj/main.cpp.

auto const ri_instances = ts3d::getLeafInstances( loader.m_psModelFile, kA3DTypeRiRepresentationItem );
for( auto ri_instance : ri_instances ) {
ts3d::RepresentationItemInstance const ri( ri_instance );
if( !ri.Instance::getNetShow() ) {
continue;
}
auto const tess3d = std::dynamic_pointer_cast<ts3d::Tess3DInstance>( ri.getTessellation() );
if( nullptr == tess3d ) {
continue;
}
auto const name = ri.getName();
obj_file << "o " << name << std::endl;
auto const net_style = ri.Instance::getNetStyle();
auto const mtl = getMaterial( net_style );
if( !mtl.empty() ) {
obj_file << "usemtl " << mtl << std::endl;
}
auto const net_matrix = ts3d::getNetMatrix( ri );
auto const exchange_coords = tess3d->coords();
auto const n_coords = tess3d->coordsSize();
for( auto idx = 0u; idx < n_coords; idx += 3 ) {
auto v = net_matrix * ts3d::VectorType( exchange_coords[idx], exchange_coords[idx+1], exchange_coords[idx+2], 1. );
obj_file << "v " << v(0) << " " << v(1) << " " << v(2) << std::endl;
}
auto const exchange_normals = tess3d->normals();
auto const n_normals = tess3d->normalsSize();
for( auto idx = 0u; idx < n_normals; idx += 3 ) {
auto n = net_matrix * ts3d::VectorType( exchange_normals[idx], exchange_normals[idx+1], exchange_normals[idx+2], 0. );
obj_file << "vn " << n(0) << " " << n(1) << " " << n(2) << std::endl;
}
for( auto idx = 0u; idx < tess3d->faceSize(); ++idx ) {
auto const face_mesh = tess3d->getIndexMeshForFace( idx );
auto const n_vertices = face_mesh.vertices().size();
for(auto idx = 0u; idx < n_vertices; ++idx ) {
if( 0 == idx % 3 ) {
obj_file << std::endl << "f";
}
obj_file << " -" << (n_coords - face_mesh.vertices()[idx])/3 << "//-" << (n_normals - face_mesh.normals()[idx])/3;
}
}
obj_file << std::endl;
}

Print the product structure

These snippers were extracted from examples/print_structure/main.cpp

void printNameAndRecurse( A3DEntity *owner, unsigned int level ) {
for( auto idx = 0u; idx < level; ++idx ) {
std::cout << '\t';
}
std::cout << ts3d::Instance( {owner} ).getName() << std::endl;
level++;
auto const children = ts3d::getChildren( owner, kA3DTypeAsmProductOccurrence );
for( auto const child : children ) {
printNameAndRecurse( child, level );
}
}

This function is used in the body of main as follows.

loader.Import( i );
if( nullptr == loader.m_psModelFile ) {
std::cerr << "The specified file could not be loaded." << std::endl;
return -1;
}
auto level = 0u;
printNameAndRecurse( loader.m_psModelFile, level );