OpenCASCADE

How to iterate TopTools_IndexedMapOfShape

In OpenCASCADE, you can get e.g. a list of edges for a TopoDS_Shape using

TopTools_IndexedMapOfShape edges;
TopExp::MapShapes (shape, TopAbs_EDGE, edges);

How can you iterate edges?

The easiest way is to use indexing like this:

for (size_t i = 1; i <= edges.Extent(); i++) {
    TopoDS_Shape& edge = edges(i);
    /* ... */
}

If you want to get the TopoDS_Edge from the TopoDS_Shape in the above example, use TopoDS::Edge(edge) inside the loop.

Posted by Uli Köhler in C/C++, OpenCASCADE

How to fuse TopoDS_Shapes in OpenCASCADE (boolean OR)

The OCCUtils library provides an easy way to fuse two shapes in OpenCASCADE.

First, let’s generate a box and a cylinder using OCCUtil’s primitive generation functions:

#include <occutils/Primitive.hxx>

using namespace OCCUtils;
  
TopoDS_Shape solid1 = Primitive::MakeBox(18 /* X */, 8 /* Y */, 14 /* Z */,
    Primitive::CenterX | Primitive::CenterY);

TopoDS_Shape solid2 = Primitive::MakeCylinder(5 /* d */, 35 /* L */,
    Primitivs::Orientation::Z,
    Primitive::CenterD);

Now we can fuse the primitives using this snippet:

#include <occutils/Boolean.hxx>

using namespace OCCUtils;

TopoDS_Shape fused = Boolean::Fuse({solid1, solid2});

Boolean::Fuse() can take a number of different argument types. In this example, we’are using a std::initializer_list (this is the curly-bracket syntax) – but it can also take a TopTools_ListOfShape or a std::vector<TopoDS_Shape> (and also most other STL- or non-STL containers).

If you want to perform a fuse without using OCCUtils, you might be tempted to use this syntax:

BRepAlgoAPI_Fuse fuse(solid1, solid2);
fuse.Build();
TopoDS_Shape result = fuse.Shape();

However, the BRepAlgoAPI_Fuse(TopoDS, TopoDS) constructor is deprecated and should therefore not be used. Instead, the following syntax should be used:

// Configure fuse
BRepAlgoAPI_Fuse fuse;
fuse.SetArguments(arguments);
fuse.SetTools(tools);
// Run fuse
fuse.Build();
TopoDS_Shape shape = fuse.Shape(); // Raises NotDone if not done.

where arguments and tools are two TopTools_ListOfShape instances. For other boolean algorithms like BRepAlgoAPI_Cut (boolean difference) the two have distinct meanings, but for BRepAlgoAPI_Fuse it’s just important that both arguments and tools have at least one element each!

You can use ListUtils::SplitIntoHeadAndTail from OCCUtils to generate a std::pair of lists (arguments and tools), with arguments having one element and tools having the rest like this:

auto toolsAndArgs = ListUtils::SplitIntoHeadAndTail(shapes, 1);
auto tools = toolsAndArgs.first;
auto arguments = toolsAndArgs.second;

However, if you use ListUtils::SplitIntoHeadAndTail(), you can just use Boolean::Fuse() right away.

Posted by Uli Köhler in C/C++, OpenCASCADE

How to export STEP file in OpenCASCADE

My OCCUtils library provides a super-easy way of exporting your TopoDS_Shape to a STEP AP203 file:

#include <occutils/STEPExport.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;
STEP::ExportSTEP(myShape, "myShape.step");

In case you want to do it manually, it’s much more complicated to do right, but here are the basic steps to do it:

STEPControl_Writer writer;
writer.Transfer(shape, STEPControl_AsIs);
writer.Write(filename.c_str());

The full STEP::ExportSTEP code from OCCUtils is:

if (shape.IsNull () == true) {
    throw new invalid_argument("Can't export null shape to STEP");
}

STEPControl_Writer writer;
Interface_Static::SetCVal ("xstep.cascade.unit", unit.c_str());
Interface_Static::SetCVal ("write.step.unit", unit.c_str ());
Interface_Static::SetIVal ("write.step.nonmanifold", 1);
// "Transfer" = convert
IFSelect_ReturnStatus transferStatus = writer.Transfer(shape, STEPControl_AsIs);

if (transferStatus != IFSelect_RetDone) {
    throw std::logic_error ("Error while transferring shape to STEP");
}
// Write transferred structure to STEP file
IFSelect_ReturnStatus writeStatus = writer.Write(filename.c_str());

// Return previous locale
if (writeStatus != IFSelect_RetDone)
{
    throw std::logic_error ("Error while writing transferred shape to STEP file");
}

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to create a Cylinder TopoDS_Solid in OpenCASCADE

Also see How to create a Box TopoDS_Solid in OpenCASCADE and How to create a Cube TopoDS_Solid in OpenCASCADE

Using my OCCUtils library, you can easily create a cylinder of user-defined diameter and length:

#include <occutils/Primitive.hxx>

using namespace OCCUtils;

TopoDS_Solid myCube = Primitive::MakeCylinder(5.0 /* mm diameter */, 25.0 /* mm length */);

You can also define the orientation of the cylinder using Primites::Orientation::X, Primites::Orientation::Y or Primites::Orientation::Z:

#include <occutils/Primitive.hxx>
#include <vector>

using namespace OCCUtils;

TopoDS_Solid myCube = Primitive::MakeCylinder(5.0 /* mm diameter */,
    25.0 /* mm length */, Primitive::Orientation::X);

You can also center this cylinder on the length axis. The origin point (default: (0,0,0)) always lies on the cylinder’s main axis (i.e. along its length.

#include <occutils/Primitive.hxx>
#include <vector>

using namespace OCCUtils;

/*
 * Make a cube that is centered on the X- and Y axes
 */
TopoDS_Solid myCube = Primitive::MakeCylinder(5.0 /* mm diameter */, 25.0 /* mm length */,
    Primitive::Orientation::X,
    Primitive::CenterL);

Also you can use a specific point of origin as the third argument (gp_Pnt).

If you want to do it manually without OCCUtils, have a look at the OCC class BRepPrimAPI_MakeBox which you can use like this:

gp_Ax2 ax = /* Define origin point and direction of the cylinder here */;
BRepPrimAPI_MakeCylinder cyl(ax, diameter / 2.0, length);
cyl.Build();
TopoDS_Solid mySolid = cyl.Solid();

In this case, you have to center the object manually by computing the correct point of origin. In OCCUtils this is done using

// Compute offsets based on centering
if(center & CenterX) {
    origin.SetX(origin.X() - xSize / 2.0);
}
if(center & CenterY) {
    origin.SetY(origin.Y() - ySize / 2.0);
}
if(center & CenterZ) {
    origin.SetZ(origin.Z() - zSize / 2.0);
}

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to create a Box TopoDS_Solid in OpenCASCADE

Also see How to create a Cube TopoDS_Solid in OpenCASCADE and How to create a Cylinder TopoDS_Solid in OpenCASCADE

Using my OCCUtils library, you can easily create a box of user-defined dimensions:

#include <occutils/Primitive.hxx>
#include <vector>

using namespace OCCUtils;

TopoDS_Solid myCube = Primitive::MakeBox(5.0 /* X size */, 7.0 /* Y size */, 9.0 /* Z size */);

You can also center this box on one or more axes (if not centered in any axis, one of the corners is going to be on thre origin point):

#include <occutils/Primitive.hxx>
#include <vector>

using namespace OCCUtils;

/*
 * Make a box that is centered on the X- and Y axes
 */
TopoDS_Solid myCube = Primitive::MakeBox(5.0, 7.0, 9.0
    Primitive::CenterX | Primitive::CenterY);

Also you can use a specific point of origin as the third argument (gp_Pnt).

If you want to do it manually without OCCUtils, have a look at the OCC class BRepPrimAPI_MakeBox which you can use like this:

BRepPrimAPI_MakeBox box(origin, xSize, ySize, zSize);
box.Build();
TopoDS_Solid mySolid = box.Solid();

In this case, you have to center the object manually by computing the correct point of origin. In OCCUtils this is done using

// Compute offsets based on centering
if(center & CenterX) {
    origin.SetX(origin.X() - xSize / 2.0);
}
if(center & CenterY) {
    origin.SetY(origin.Y() - ySize / 2.0);
}
if(center & CenterZ) {
    origin.SetZ(origin.Z() - zSize / 2.0);
}

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to create a Cube TopoDS_Solid in OpenCASCADE

Also see How to create a Box TopoDS_Solid in OpenCASCADE and How to create a Cylinder TopoDS_Solid in OpenCASCADE

Using my OCCUtils library, you can easily create a cube of user-defined dimensions:

#include <occutils/Primitive.hxx>

using namespace OCCUtils;

TopoDS_Solid myCube = Primitive::MakeCube(5.0 /* mm side length */);

You can also center this cube on one or more axes (if not centered in any axis, one of the corners is going to be on thre origin point):

#include <occutils/Primitive.hxx>

using namespace OCCUtils;

/*
 * Make a cube that is centered on the X- and Y axes
 */
TopoDS_Solid myCube = Primitive::MakeCube(5.0 /* mm side length */,
    Primitive::CenterX | Primitive::CenterY);

Also you can use a specific point of origin as the third argument (gp_Pnt).

If you want to do it manually without OCCUtils, have a look at the OCC class BRepPrimAPI_MakeBox which you can use like this:

BRepPrimAPI_MakeBox box(origin, size, size, size);
box.Build();
TopoDS_Solid mySolid = box.Solid();

In this case, you have to center the object manually by computing the correct point of origin. In OCCUtils this is done using

// Compute offsets based on centering
if(center & CenterX) {
    origin.SetX(origin.X() - xSize / 2.0);
}
if(center & CenterY) {
    origin.SetY(origin.Y() - ySize / 2.0);
}
if(center & CenterZ) {
    origin.SetZ(origin.Z() - zSize / 2.0);
}

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to create TopTools_ListOfShape of two or more shapes in OpenCASCADE

TopTools_ListOfShape is just another name for NCollection_List<TopoDS_Shape>.

Using my OCCUtils library this is really easy:

#include <TopTools_ListOfShape.hxx>
#include <occutils/ListUtils.hxx>

using namespace OCCUtils;

/* ... */

TopoDS_Solid solid1 = /* ... */;
TopoDS_Solid solid2 = /* ... */;

TopTools_ListOfShape shapes = ListUtils::ToOCCList({solid1, solid2});

ListUtils::ToOCCList() takes almost anything as an argument, a std::initializer_list (this is the bracket syntax from the example above) but also any type of STL container like a std::vector or a std::list, and converts it into a NCollection_List<T>:

#include <TopTools_ListOfShape.hxx>
#include <occutils/ListUtils.hxx>
#include <vector>

using namespace OCCUtils;

/* ... */

TopoDS_Solid solid1 = /* ... */;
TopoDS_Solid solid2 = /* ... */;

std::vector<TopoDS_Shape> shapeVector({solid1, solid2});

TopTools_ListOfShape shapes = ListUtils::ToOCCList(shapeVector);

In case you don’t want to use OCCUtils, this is how you can create a TopTools_ListOfShape manually:

TopTools_ListOfShape list;
list.Append(shape1);
list.Append(shape2);

Since NCollection_List and therefore also TopTools_ListOfShape is a linked list internally, you can also use list.Prepend(shape); to add a shape to the front of the list without any performance penalty.

In case you want to create the list from a std::vector or any similar STL container, use this snippet:

std::vector<TopoDS_Shape>& shapeVector = /* ... */;

TopTools_ListOfShape list;
for(const TopoDS_Shape& shape : shapeVector) {
    list.Append(arg);
}

After creating the container, you can iterate it using the C++11 range-based for-loop:

TopTools_ListOfShape myShapes = /* ... */;

// Iterate myShapes
for(const TopoDS_Shape& shape : myShapes) {
    /* ... */
}

See How to iterate TopTools_ListOfShape in OpenCASCADE and How to iterate NCollection_List in OpenCASCADE for more details on that.

Posted by Uli Köhler in C/C++, OpenCASCADE

How to iterate TopTools_ListOfShape in OpenCASCADE

TopTools_ListOfShape is a NCollection_List<T> and hence supports iteration using the C++11 foreach loop (also called range-based for loop):
TopTools_ListOfShape myShapes = /* ... */;

// Iterate myShapes
for(const TopoDS_Shape& shape : myShapes) {
    /* ... */
}

Remember to compile with at least --std=c++11 or equivalent for your compiler to allow using the range-based for loop.

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to iterate NCollection_List in OpenCASCADE

NCollection_List<T> supports iteration using the C++11 foreach loop (also called range-based for loop):
NCollection_List<T> myList = /* ... */;

// Iterate myList
for(const T& value : myList) {
    /* ... */
}

Remember to compile with at least --std=c++11 or equivalent for your compiler to allow using the range-based for loop.

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to iterate all edges in TopoDS_Face using OpenCASCADE

You can use the OCCUtils library to iterate all TopoDS_Face instances in a TopoDS_Solid

#include <occutils/ShapeComponents.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

auto edges = ShapeComponents::AllEdgesWithin(myShape);

// Iterate all solids
for(const TopoDS_Edge& edge : edges) {
    /* ... */
}

Alternatively, you can use this raw OpenCASCADE source code without OCCUtils:

#include <TopTools_IndexedMapOfShape.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopExp.hxx>

TopoDS_Shape myShape = /* ... */;

TopTools_IndexedMapOfShape faces;
TopExp::MapShapes (myShape, TopAbs_EDGE, faces);

for (int i = 1; i <= faces.Extent (); i++) {
    TopoDS_Edge face = TopoDS::Edge(edges(i));
    /* ... */
}

If you have the choice, I recommend using OCCUtils since it makes your code much more readable than using the raw OpenCASCADE API.

Posted by Uli Köhler in C/C++, OpenCASCADE

How to iterate all faces in TopoDS_Solid using OpenCASCADE

You can use the OCCUtils library to iterate all TopoDS_Face instances in a TopoDS_Solid

#include <occutils/ShapeComponents.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

auto faces = ShapeComponents::AllFacesWithin(myShape);

// Iterate all solids
for(const TopoDS_Face& face : faces) {
    /* ... */
}

Alternatively, you can use this source code without OCCUtils:

#include <TopTools_IndexedMapOfShape.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopExp.hxx>

TopoDS_Shape myShape = /* ... */;

TopTools_IndexedMapOfShape faces;
TopExp::MapShapes (myShape, TopAbs_FACE, faces);

for (int i = 1; i <= faces.Extent (); i++) {
    TopoDS_Face face = TopoDS::Face(faces(i));
    /* ... */
}

If you have the choice, I recommend using OCCUtils since it makes your code much more readable than using the raw OpenCASCADE API.

Posted by Uli Köhler in C/C++, OpenCASCADE

How to get TopoDS_Solid(s) from TopoDS_Shape in OpenCASCADE

There are two important cases if you want to get a TopoDS_Solid object from a TopoDS_Shape:

A: The TopoDS_Shape is a solid

You can check if that is the case using

#include <TopoDS_Shape.hxx>

TopoDS_Shape myShape = /* ... */

bool isSolid = myShape.ShapeType() == TopAbs_SOLID;

Alternatively, you can use the Shape::IsSolid() function from OCCUtils:

#include <occutils/Shape.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

if(Shape::IsSolid(myShape)) {
    /* ... */
}

If it’s a solid, you can convert it to a TopoDS_Solid directly using:

TopoDS::Solid(myShape);

Full example:

#include <TopoDS_Shape.hxx>
#include <TopoDS.hxx>
#include <occutils/Shape.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

if (Shape::IsSolid(myShape)) {
    TopoDS_Solid solid = TopoDS::Solid(myShape);
    /* ... */
}

B: The shape is not itself a TopoDS_Solid but contains one or more TopoDS_Solids

This is often the case for TopoDS_Compounds. Use Shape::IsCompound() from OCCUtils to check if your TopoDS_Shape is actually a TopoDS_Compound.

#include <occutils/Shape.hxx>
using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;
if(Shape::IsCompound(myShape)) { /* ... */ }

Using OCCUtils, you have now several options to get the TopoDS_Solids from the Compound:

If there is a chance that there are zero or more than one TopoDS_Solids in your compound:

Use ShapeComponents::AllSolidsWithin() from OCCUtils to get a std::vector<TopoDS_Solid> of all solids within the TopoDS_Compound:

#include <occutils/Shape.hxx>
#include <occutils/ShapeComponents.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

auto solids = ShapeComponents::AllSolidsWithin(myShape);

// Iterate all solids
for(const TopoDS_Solid& solid : solids) {
    /* ... */
}

If you are sure the compound only contains one solid (also works if it’s a solid itself):

Use ShapeComponents::TryGetSingleSolid(shape) which returns a std::optional<TopoDS_Solid> (no value if there is no solid or there are multiple solids in shape) or ShapeComponents::GetSingleSolid(shape) which throws an exception if there is no solid or there are multiple solids in the shape.

Example:

#include <occutils/ShapeComponents.hxx>
#include <iostream> // cerr, endl

using namespace std;
using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

auto solidOpt = ShapeComponents::TryGetSingleSolid(myShape);

if(solidOpt.has_value()) {
    TopoDS_Solid solid = solidOpt.value();
} else {
    cerr << "No solid or multiple solids in shape!" << endl;
}

If you want to use pure OpenCASCADE without OCCUtils:

Use this snippet to directly iterate the TopoDS_Solid instances in the shape

TopTools_IndexedMapOfShape solidShapes;
TopExp::MapShapes (myShape, TopAbs_SOLID, solidShapes);

for (int i = 1; i <= solidShapes.Extent (); i++) {
    TopoDS_Solid solid = TopoDS::Solid(solidShapes(i));
    /* Do what you need with the solid here ! */
}

 

Posted by Uli Köhler in C/C++, OpenCASCADE

How to compute surface area of TopoDS_Face in OpenCASCADE

In order to compute the surface area of a TopoDS_Face, in OpenCASCADE, simply use BRepGProp::SurfaceProperties and call .Mass() on the resulting GProp_GProps object:

GProp_GProps gprops;
BRepGProp::SurfaceProperties(face, gprops); // Stores results in gprops
double area = gprops.Mass();

Alternatively, you can use my OCCUtils library which also also providers a lot of other utility functions:

#include <occutils/Surface.hxx>
using namespace OCCUtils;

double Surface::Area(const TopoDS_Shape& face);

Example:

#include <occutils/Surface.hxx>

using namespace OCCUtils;

// ...
double area = Surface::Area(myFace);
// ...

 

Posted by Uli Köhler in C/C++, OpenCASCADE