OpenCASCADE

How to get length of curve in OpenCASCADE

OCCUtils provides a convenience function to get the length of a curve in OpenCASCADE:

#include <occutils/Curve.hxx>

using namespace OCCUtils;

GeomAdaptor_Curve curve = /* ... */;

double length = Curve::Length(curve);

You can call it with GeomAdaptor_Curve or Geom_TrimmedCurve. While you can also call it with Handle(Geom_Curve), this is usually not what you want to do, since Geom_Curve typically describes an infinite curve (like an infinitely long line) and doesn’t know about which section of the curve is actually use (GeomAdaptor_Curve and Geom_TrimmedCurve both have the required Umin/Umax information stored).

In case you need to do it manually (without OCCUtils), use this snippet:

#include <GCPnts_AbscissaPoint.hxx>

GeomAdaptor_Curve curve = /* ... */;

GCPnts_AbscissaPoint::Length(curve);

For a Geom_TrimmedCurve, you need to convert it to a GeomAdaptor_Curve first. See How to convert Geom_TrimmedCurve to GeomAdaptor_Curve in OpenCASCADE for details on how to do that.

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

How to convert Geom_TrimmedCurve to GeomAdaptor_Curve in OpenCASCADE

OCCUtils provides a conveniece function to convert a Geom_TrimmedCurve to a GeomAdaptor_Curve:

#include <occutils/Curve.hxx>

using namespace OCCUtils;

Geom_TrimmedCurve input = /* ... */;

GeomAdaptor_Curve result = Curve::FromTrimmedCurve(input);

If you want to do it manually, use this snippet:

Geom_TrimmedCurve input = /* ... */;

GeomAdaptor_Curve result(
    input.BasisCurve(), input.FirstParameter(), input.LastParameter()
);
Posted by Uli Köhler in C/C++, OpenCASCADE

How to check if two gp_Pnt coincide in OpenCASCADE

If you have two points in OpenCASCADE:

gp_Pnt p1 = /* ... */;
gp_Pnt p2 = /* ... */;

you can’t just use == to check if they coincide in stock OpenCASCADE. However, OCCUtils provides operator== and operator!= for gp_Pnt among other types:

#include <occutils/Equality.hxx>

bool areTheyCoincident = p1 == p2;

If you can’t use OCCUtils, this is the way to check if they are coincident:

if (p1.Distance(p2) <= Precision::Confusion()) {
    // p1 coincides with p2
}

or alternatively:

if (p1.IsEqual(p2, Precision::Confusion())) {
    // p1 coincides with p2
}

The reason for this is that even though two points may represent the same point in space, depending on how you compute their coordinates exactly, their cartesian X/Y/Z coordinates might not be exactly identical. This is always the case with floating-point arithmetic, that’s the type of math computers use. Don’t confuse floating-point arithmetic with exact arithmetic which you have learnt in school or university.

The solution for this issue is to define a very small tolerance – any objects that have a distance less than that tolerance are considered equal.

In OpenCASCADE, Precision::Confusion() in the standard tolerance value for this.

My OCCUtils library provides

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

How to create TopoDS_Edge from to gp_Pnt in OpenCASCADE

OCCUtils provides a conveniece function for creating a TopoDS_Edge between two points (i.e. a straight line):

#include <occutils/Edge.hxx>

using namespace OCCUtils;

gp_Pnt p1 = /* ... */;
gp_Pnt p2 = /* ... */;

TopoDS_Edge edge = Edge::FromPoints(p1, p2);

In case you want to do it manually (i.e. without OCCUtils), use this snippet

TopoDS_Edge edge = BRepBuilderAPI_MakeEdge(p1, p2).Edge();

but be aware that you also have to handle the case of p1 == p2 i.e.

if (p1.Distance(p2) <= Precision::Confusion()) {
    // Don't try to create an Edge in this case
}

OCCUtil’s Edge::FromPoints handles this case by returning TopoDS_Edge() i.e. a TopoDS_Edge where edge.IsNull() == true.

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

How to create TopoDS_Wire from TopoDS_Edge(s) in OpenCASCADE

OCCUtils provides easy-to-use conveniece functions to convert multiple TopoDS_Edges to a TopoDS_Wire:

#include <occutils/Wire.hxx>

using namespace OCCUtils;

TopoDS_Wire& wire = Wire::FromEdges({edge1, edge2})

You can also pass a std::vector<TopoDS_Edge> to Wire::FromEdges().

If you want to do it manually without using OCCUtils, you can use BRepLib_MakeWire like this:

BRepLib_MakeWire wireMaker;
wireMaker.Add(edge1);
wireMaker.Add(edge2);
TopoDS_Wire wire = wireMaker.Wire();
Posted by Uli Köhler in C/C++, OpenCASCADE

How to export colored STEP files in OpenCASCADE

OCCUtils provides an easy way of exporting a STEP with colored elements:

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

TopoDS_Shape cube = Primitive::MakeCube(5 /* mm */);

STEP::ExtendedSTEPExporter stepExporter;
stepExporter.AddShapeWithColor(cube, Quantity_NOC_RED);
stepExporter.Write("ColoredCube.step");

The resulting STEP file looks like this:

You can also add non-colored shapes:

stepExporter.AddShape(myShape);

Doing it without OCCUtils is possible but might make your life miserable since it’s rather hard to get to work correctly.

Here’s a minimal example:

// Get global application
Handle(TDocStd_Application) application = XCAFApp_Application::GetApplication();
// Create document
Handle(TDocStd_Document) document;
application->NewDocument("MDTV-XCAF", document);
// Get shape & color tools
Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(document->Main());
Handle(XCAFDoc_ColorTool) colorTool = XCAFDoc_DocumentTool::ColorTool(document->Main());
// Create shape
// IMPORTANT: DO NOT use shapeTool->AddShape(TopoDS_Shape)!
// This WILL NOT PRESERVE the color!
TDF_Label partLabel = shapeTool->NewShape();
shapeTool->SetShape(partLabel, filletedBody);

//TDF_Label redColor = colorTool->AddColor();
colorTool->SetColor(partLabel, Quantity_NOC_RED, XCAFDoc_ColorGen);

STEPCAFControl_Writer writer;
writer.SetColorMode(true);
writer.Perform(document, "ColoredShape.step");

What colors can you use?

Either define the Quantity_Color yourself using RGB values (from 0.0 to 1.0) like this:

Quantity_Color red(1.0 /* R */, 0.0 /* G */, 0.0 /* B */, Quantity_TypeOfColor::Quantity_TOC_RGB);

alternatively you can do the same in the HSL colorspace:

Quantity_Color brownishOrange(1.0 /* H */, 0.5 /* L */, 0.5 /* S */, Quantity_TypeOfColor::Quantity_TOC_HLS);

or you can use the predefined colors in the Quantity_NameOfColor enum – see Overview of all standard colors available in OpenCASCADE for a table of all available colors.

Posted by Uli Köhler in C/C++, 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 AND)

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