OpenCASCADE

How to install OpenCascade on Ubuntu 22.04

The following command installs a recommended subset of the OpenCascade packages (and development headers) using apt. Since the packages are now available from the official repositories, you don’t need to add extra repositories.

sudo apt install -y xfonts-scalable libocct-data-exchange-dev libocct-draw-dev libocct-foundation-dev libocct-modeling-algorithms-dev libocct-modeling-data-dev libocct-ocaf-dev libocct-visualization-dev

 

Posted by Uli Köhler in Linux, OpenCASCADE

How to cut shapes using OCCUtils for OpenCascade (boolean difference)

The OCCUtils library provides an easy way to compute the difference, or cut one shapefrom the other, in OpenCASCADE. Also see How to create a Box TopoDS_Solid in OpenCASCADE and How to create a Cylinder TopoDS_Solid in OpenCASCADE for more details on how we can generate those shapes using OCCUtils.

If we have two shapes:

#include <occutils/Primitive.hxx>

// ...
TopoDS_Solid box = Primitive::MakeBox(10, 10, 10 /* mm */);

TopoDS_Solid cylinder = Primitive::MakeCylinder(3 /* mm diameter */,
        100 /* mm length */, Primitive::Orientation::Y);

we can use Boolean::Cut from OCCUtils to compute the boolean difference:

TopoDS_Shape result = Boolean::Cut(box, cylinder);

Complete main.cpp example with STEP export:

#include <occutils/Primitive.hxx>
#include <occutils/STEPExport.hxx>
#include <occutils/Boolean.hxx>

using namespace OCCUtils;

int main() {
    // Make basic box
    TopoDS_Solid box = Primitive::MakeBox(10, 10, 10 /* mm */);

    TopoDS_Solid cylinder = Primitive::MakeCylinder(3 /* mm diameter */,
        100 /* mm length */, Primitive::Orientation::Y);

    TopoDS_Shape result = Boolean::Cut(box, cylinder);

    STEP::ExportSTEP(result, "out.step");
}

The result (out.step) will look like this when viewed in FreeCAD

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

OCCUtils full example: Make box and export it to STEP

This example creates a box using OCCUtils’s Primitive::MakeBox (or see How to create a Box TopoDS_Solid in OpenCASCADE ) and exports it to a STEP file.

First, initialize the git repository:

git init

Now we can add OCCUtilsusing the submodule method:

git submodule init
git submodule add https://github.com/ulikoehler/OCCUtils.git OCCUtils

Now we can add the CMake config:

project(mkbox)
cmake_minimum_required(VERSION 3.3)

add_subdirectory(OCCUtils)

add_executable( mkbox main.cpp )
add_dependencies( mkbox occutils )
target_include_directories( mkbox PUBLIC /usr/include/opencascade/ )
target_link_libraries( mkbox
    occutils
    TKernel
    TKMath
    TKFillet
    TKBinXCAF 
    TKBRep
    TKBO
    TKFeat
    TKG2d
    TKXDESTEP
    TKG3d
    TKGeomAlgo
    TKGeomBase
    TKHLR
    TKIGES
    TKPrim
    TKShHealing
    TKSTEP
    TKSTEP209
    TKSTEPAttr
    TKSTEPBase
    TKXSBase
    TKSTL
    TKTopAlgo 
    TKV3d
    TKOffset
    TKService
)

And the main source code:

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

using namespace OCCUtils;

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

    STEP::ExportSTEP(myCube, "out.step");
}

Now we can configure & build using

cmake .
make

When running the program using

./mkbox

we see the following debug output:

*******************************************************************
******        Statistics on Transfer (Write)                 ******

*******************************************************************
******        Transfer Mode = 0  I.E.  As Is       ******
******        Transferring Shape, ShapeType = 2                      ******
** WorkSession : Sending all data
 Step File Name : out.step(350 ents)  Write  Done

and mkbox will generate out.step which you can open, for example, in FreeCAD to view the generated STEP file:

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

What does “BRep” mean in OpenCASCADE?

BRep in class and function names like BRepGProp means Boundary Representation. That means representing a geometrical object by its boundary, i.e. its outer shell. For example, a cylinder in boundary representation would be represented by two circles (its end faces) and a cylindrical shell.

BRep ist the way STEP files represent their geometry data – in contrast to, for example, STL files, representing the geometry by a buch of triangles connected together. Boundary reprensentation allows exact representation of geometries whereas representing objects with triangles or other methods often involves a loss of precision, i.e. the original geometry will be slightly different to the geometry as stored in a STL file.

Posted by Uli Köhler in OpenCASCADE

How to make TopoDS_Face from gp_Pnts

OCCUtils provides Face::FromPoints() to linearly connect a set of gp_Pnt points and make a face from the resulting edges:

#include <occutils/Face.hxx>

using namespace OCCUtils;

gp_Pnt p1, p2, p3; // Your points!

TopoDS_Face face = Face::FromPoints({p1, p2, p3});

Face::FromPoints() will automatically remove duplicate consecutive points and connect the last point to the first point.

Note that if there are not enough unique points (you need at least 3 unique points to make a valid face!), Face::FromPoints() will return a TopoDS_Face where .IsNull() is true.

Posted by Uli Köhler in OpenCASCADE

Converting vector of TopoDS_Face to vector of TopoDS_Shape in OpenCASCADE

OCCUtils provides Shapes::FromFaces  to convert a std::vector<TopoDS_Face> to a std::vector<TopoDS_Shape> in OpenCASCADE:

#include <occutils/Shape.hxx>

using namespace OCCUtils;

std::vector<TopoDS_Face> faces = /* ... */;
std::vector<TopoDS_Shape> shapes = Shapes::FromFaces(faces);

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

#include <algorithm>

// Create return vector
std::vector<TopoDS_Shape> shapes;
shapes.reserve(faces.size());
// Do the copying
std::copy(faces.begin(), faces.end(), std::back_inserter(shapes));

 

Posted by Uli Köhler in OpenCASCADE

Converting vector of TopoDS_Solid to vector of TopoDS_Shape in OpenCASCADE

OCCUtils provides Shapes::FromSolids  to convert a std::vector<TopoDS_Solid> to a std::vector<TopoDS_Shape> in OpenCASCADE:

#include <occutils/Shape.hxx>

using namespace OCCUtils;

std::vector<TopoDS_Solid> solids = /* ... */;
std::vector<TopoDS_Shape> shapes = Shapes::FromSolids(solids);

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

#include <algorithm>

// Create return vector
std::vector<TopoDS_Shape> shapes;
shapes.reserve(solids.size());
// Do the copying
std::copy(solids.begin(), solids.end(), std::back_inserter(shapes));

 

Posted by Uli Köhler in OpenCASCADE

Computing distance between gp_Pnt and gp_Ax1 in OpenCASCADE

OCCUtils provides convenience functions for computing the orthogonal direction to two directions:

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

gp_Ax1 axis = /* ... */;
gp_Pnt pnt = /* ... */;
double distance = Axis::Distance(axis, pnt);

Alternatively, you can also use Point::Distance() which internally just calls Axis::Distance() but might make your code more readable under some circumstances. Note that the argument order is inverted!

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

gp_Ax1 axis = /* ... */;
gp_Pnt pnt = /* ... */;
double distance = Point::Distance(pnt, axis);

In case you can’t use OCCUtils, here’s the code to do it manually:

double distance = gp_Lin(axis).Distance(pnt);
Posted by Uli Köhler in C/C++, OpenCASCADE

How to check if gp_Ax1 contains gp_Pnt in OpenCASCADE

OCCUtils provides convenience functions for computing the orthogonal direction to two directions:

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

gp_Ax1 axis = /* ... */;
gp_Png point = /* ... */;
bool pointIsOnAxis = Axis::Contains(axis, point);

In case you can’t use OCCUtils, here’s the code to do it manually:

gp_Lin(axis).Contains(pnt, Precision::Confusion());
Posted by Uli Köhler in C/C++, OpenCASCADE

How to get gp_Dir orthogonal to two gp_Dirs in OpenCASCADE

OCCUtils provides convenience functions for computing the orthogonal direction to two directions:

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

gp_Dir dir1 = /* ... */;
gp_Dir dir2 = /* ... */;
gp_Dir orthogonalDirection = Direction::Orthogonal(dir1, dir2);

In case you can’t use OCCUtils, here’s the code to do it manually:

gp_Dir orthogonalDirection = dir1.Crossed(dir2);

The function used is called Crossed() since the mathematical operation being used is the cross product between the two direction vectors.

 

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

How to get midpoint/center between points in OpenCASCADE

OCCUtils provides convenience functions for getting the midpoint (also called the center) between 2 or more points:

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

gp_Pnt p1 = /* ... */;
gp_Pnt p2 = /* ... */;
gp_Pnt midpointOfP1AndP2 = Point::Midpoint({p1, p2});

You can also call Point::Midpoint() with a std::vector<gp_Pnt>.

In case you can’t use OCCUtils, here’s the code to do it manually:

double x = 0.0, y = 0.0, z = 0.0;
for (const gp_Pnt &pnt : points) {
    x += pnt.X();
    y += pnt.Y();
    z += pnt.Z();
}
size_t size = points.size();
gp_Pnt midpoint(x / size, y / size, z / size);

 

 

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

How to compute volume of TopoDS_Shape / TopoDS_Solid in OpenCASCADE

OCCUtils provides a convenience function to do this:

#include <occutils/Shape.hxx>

using namespace OCCUtils;

TopoDS_Shape myShape = /* ... */;

double volume = Shape::Volume(myShape);

In case you need to do this without OCCUtils, use

GProp_GProps gprops;
BRepGProp::VolumeProperties(shape, gprops);
double volume = gprops.Mass();

 

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

How to compute surface normal in OpenCASCADE

OCCUtils provides a convenient utility to compute the Normal of a surface (represented by a GeomAdaptor_Surface) in OpenCASCADE:

#include <occutils/Surface.hxx>

using namespace OCCUtils;

GeomAdaptor_Surface surf = /* ... */;

gp_Ax1 surfaceNormal = Surface::Normal(surf);

// ...or just get the direction
gp_Dir surfaceDirection = Surface::NormalDirection(surf);

This function computes the normal vector at specific U/V coordinates which default to (0,0). You can also give custom U/V coordinates:

gp_Ax1 normal = Surface::Normal(surf, 1.0 /* u */, -4.5 /* v */);

In case you can’t use OCCUtils and you need to do it manually, here’s how you can do it:

#include <GeomLProp_SLProps.hxx>

GeomLProp_SLProps props(surf.Surface(), u, v, 1 /* max 1 derivation */, precision);
gp_Ax1 axis(props.Value(), props.Normal());

props.Value() returns the point on the surface at the given U/V coordinates.

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

Overview of all standard colors available in OpenCASCADE

This table shows you all the available standard colors in OpenCASCADE.

Use like this:

Quantity_Color myColor(Quantity_NOC_ALICEBLUE);

See below for the source code used to generate this table!

Continue reading →

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