1624/2078

DirectShape from BrepBuilder and Boolean

Originally Published in

Here is an interesting code snippet illustrating the use of BRepBuilder and Boolean operations to generate a DirectShape.

It might come in useful somewhere, even though this approach is non-optimal to address the task at hand, as explained below.

Question: I want to create a DirectShape object which is cut by void geometry.

I found that the BRepBuilder constructor accepts both BRepType.Void and BRepType.Solid, so I tested if I could use that.

However, my attempts so far lead to an internal error.

Is there any mistake in my test code?

Does the DirectShape.AppendShape method not accept a void shape?

DirectShape from BrepBuilder Boolean

Answer: It seems to me that you actually want to do is create two solid geometries, and then use a Boolean operation to subtract one from the other.

This will give the desired shape.

Taking a look at the code, I notice that the function CreateBrepVoid is incorrect – it tells the BRepBuilder that it wants to create a void, but the geometry defines a solid, not a void. This is determined by the face and edge loop orientations. I assume that BRepBuilder complains about invalid input at some point in the process.

The orientation conventions are discussed in the descriptions of BRepBuilder AddFace and AddCoEdge methods.

Please note that BRepBuilder wasn’t really meant for ‘manually’ constructing geometry. Its interface is very cumbersome for that purpose. It was meant for translating existing geometry into Revit, with rather thorough validation of the input geometry.

Here is the code that constructs two solid geometries and then applies the Boolean operation to find the difference.

The code constructs the desired DirectShape shown in the image above:

[Transaction( TransactionMode.Manual )]
class CmdBrepBuilder : IExternalCommand
{
  /// <summary>
  /// Create a cube 100 x 100 x 100, from 
  /// (0,0,0) to (100, 100, 100).
  /// </summary>
  public BRepBuilder CreateBrepSolid()
  {
    BRepBuilder b = new BRepBuilder( BRepType.Solid );

    // 1. Planes.
    // naming convention for faces and planes:
    // We are looking at this cube in an isometric view. 
    // X is down and to the left of us, Y is horizontal 
    // and points to the right, Z is up.
    // front and back faces are along the X axis, left 
    // and right are along the Y axis, top and bottom 
    // are along the Z axis.
    Plane bottom = Plane.CreateByOriginAndBasisnew XYZ50500 ), new XYZ100 ), new XYZ010 ) ); // bottom. XY plane, Z = 0, normal pointing inside the cube.
    Plane top = Plane.CreateByOriginAndBasisnew XYZ5050100 ), new XYZ100 ), new XYZ010 ) ); // top. XY plane, Z = 100, normal pointing outside the cube.
    Plane front = Plane.CreateByOriginAndBasisnew XYZ1005050 ), new XYZ001 ), new XYZ010 ) ); // front side. ZY plane, X = 0, normal pointing inside the cube.
    Plane back = Plane.CreateByOriginAndBasisnew XYZ05050 ), new XYZ001 ), new XYZ010 ) ); // back side. ZY plane, X = 0, normal pointing outside the cube.
    Plane left = Plane.CreateByOriginAndBasisnew XYZ50050 ), new XYZ001 ), new XYZ100 ) ); // left side. ZX plane, Y = 0, normal pointing inside the cube
    Plane right = Plane.CreateByOriginAndBasisnew XYZ5010050 ), new XYZ001 ), new XYZ100 ) ); // right side. ZX plane, Y = 100, normal pointing outside the cube

    // 2. Faces.
    BRepBuilderGeometryId faceId_Bottom = b.AddFace( BRepBuilderSurfaceGeometry.Create( bottom, null ), true );
    BRepBuilderGeometryId faceId_Top = b.AddFace( BRepBuilderSurfaceGeometry.Create( top, null ), false );
    BRepBuilderGeometryId faceId_Front = b.AddFace( BRepBuilderSurfaceGeometry.Create( front, null ), true );
    BRepBuilderGeometryId faceId_Back = b.AddFace( BRepBuilderSurfaceGeometry.Create( back, null ), false );
    BRepBuilderGeometryId faceId_Left = b.AddFace( BRepBuilderSurfaceGeometry.Create( left, null ), true );
    BRepBuilderGeometryId faceId_Right = b.AddFace( BRepBuilderSurfaceGeometry.Create( right, null ), false );

    // 3. Edges.

    // 3.a (define edge geometry)
    // walk around bottom face
    BRepBuilderEdgeGeometry edgeBottomFront = BRepBuilderEdgeGeometry.Createnew XYZ10000 ), new XYZ1001000 ) );
    BRepBuilderEdgeGeometry edgeBottomRight = BRepBuilderEdgeGeometry.Createnew XYZ1001000 ), new XYZ01000 ) );
    BRepBuilderEdgeGeometry edgeBottomBack = BRepBuilderEdgeGeometry.Createnew XYZ01000 ), new XYZ000 ) );
    BRepBuilderEdgeGeometry edgeBottomLeft = BRepBuilderEdgeGeometry.Createnew XYZ000 ), new XYZ10000 ) );

    // now walk around top face
    BRepBuilderEdgeGeometry edgeTopFront = BRepBuilderEdgeGeometry.Createnew XYZ1000100 ), new XYZ100100100 ) );
    BRepBuilderEdgeGeometry edgeTopRight = BRepBuilderEdgeGeometry.Createnew XYZ100100100 ), new XYZ0100100 ) );
    BRepBuilderEdgeGeometry edgeTopBack = BRepBuilderEdgeGeometry.Createnew XYZ0100100 ), new XYZ00100 ) );
    BRepBuilderEdgeGeometry edgeTopLeft = BRepBuilderEdgeGeometry.Createnew XYZ00100 ), new XYZ1000100 ) );

    // sides
    BRepBuilderEdgeGeometry edgeFrontRight = BRepBuilderEdgeGeometry.Createnew XYZ1001000 ), new XYZ100100100 ) );
    BRepBuilderEdgeGeometry edgeRightBack = BRepBuilderEdgeGeometry.Createnew XYZ01000 ), new XYZ0100100 ) );
    BRepBuilderEdgeGeometry edgeBackLeft = BRepBuilderEdgeGeometry.Createnew XYZ000 ), new XYZ00100 ) );
    BRepBuilderEdgeGeometry edgeLeftFront = BRepBuilderEdgeGeometry.Createnew XYZ10000 ), new XYZ1000100 ) );

    // 3.b (define the edges themselves)
    BRepBuilderGeometryId edgeId_BottomFront = b.AddEdge( edgeBottomFront );
    BRepBuilderGeometryId edgeId_BottomRight = b.AddEdge( edgeBottomRight );
    BRepBuilderGeometryId edgeId_BottomBack = b.AddEdge( edgeBottomBack );
    BRepBuilderGeometryId edgeId_BottomLeft = b.AddEdge( edgeBottomLeft );
    BRepBuilderGeometryId edgeId_TopFront = b.AddEdge( edgeTopFront );
    BRepBuilderGeometryId edgeId_TopRight = b.AddEdge( edgeTopRight );
    BRepBuilderGeometryId edgeId_TopBack = b.AddEdge( edgeTopBack );
    BRepBuilderGeometryId edgeId_TopLeft = b.AddEdge( edgeTopLeft );
    BRepBuilderGeometryId edgeId_FrontRight = b.AddEdge( edgeFrontRight );
    BRepBuilderGeometryId edgeId_RightBack = b.AddEdge( edgeRightBack );
    BRepBuilderGeometryId edgeId_BackLeft = b.AddEdge( edgeBackLeft );
    BRepBuilderGeometryId edgeId_LeftFront = b.AddEdge( edgeLeftFront );

    // 4. Loops.
    BRepBuilderGeometryId loopId_Bottom = b.AddLoop( faceId_Bottom );
    BRepBuilderGeometryId loopId_Top = b.AddLoop( faceId_Top );
    BRepBuilderGeometryId loopId_Front = b.AddLoop( faceId_Front );
    BRepBuilderGeometryId loopId_Back = b.AddLoop( faceId_Back );
    BRepBuilderGeometryId loopId_Right = b.AddLoop( faceId_Right );
    BRepBuilderGeometryId loopId_Left = b.AddLoop( faceId_Left );

    // 5. Co-edges. 
    // Bottom face. All edges reversed
    b.AddCoEdge( loopId_Bottom, edgeId_BottomFront, true ); // other direction in front loop
    b.AddCoEdge( loopId_Bottom, edgeId_BottomLeft, true );  // other direction in left loop
    b.AddCoEdge( loopId_Bottom, edgeId_BottomBack, true );  // other direction in back loop
    b.AddCoEdge( loopId_Bottom, edgeId_BottomRight, true ); // other direction in right loop
    b.FinishLoop( loopId_Bottom );
    b.FinishFace( faceId_Bottom );

    // Top face. All edges NOT reversed.
    b.AddCoEdge( loopId_Top, edgeId_TopFront, false );  // other direction in front loop.
    b.AddCoEdge( loopId_Top, edgeId_TopRight, false );  // other direction in right loop
    b.AddCoEdge( loopId_Top, edgeId_TopBack, false );   // other direction in back loop
    b.AddCoEdge( loopId_Top, edgeId_TopLeft, false );   // other direction in left loop
    b.FinishLoop( loopId_Top );
    b.FinishFace( faceId_Top );

    // Front face.
    b.AddCoEdge( loopId_Front, edgeId_BottomFront, false ); // other direction in bottom loop
    b.AddCoEdge( loopId_Front, edgeId_FrontRight, false );  // other direction in right loop
    b.AddCoEdge( loopId_Front, edgeId_TopFront, true ); // other direction in top loop.
    b.AddCoEdge( loopId_Front, edgeId_LeftFront, true ); // other direction in left loop.
    b.FinishLoop( loopId_Front );
    b.FinishFace( faceId_Front );

    // Back face
    b.AddCoEdge( loopId_Back, edgeId_BottomBack, false ); // other direction in bottom loop
    b.AddCoEdge( loopId_Back, edgeId_BackLeft, false );   // other direction in left loop.
    b.AddCoEdge( loopId_Back, edgeId_TopBack, true ); // other direction in top loop
    b.AddCoEdge( loopId_Back, edgeId_RightBack, true ); // other direction in right loop.
    b.FinishLoop( loopId_Back );
    b.FinishFace( faceId_Back );

    // Right face
    b.AddCoEdge( loopId_Right, edgeId_BottomRight, false ); // other direction in bottom loop
    b.AddCoEdge( loopId_Right, edgeId_RightBack, false );  // other direction in back loop
    b.AddCoEdge( loopId_Right, edgeId_TopRight, true );   // other direction in top loop
    b.AddCoEdge( loopId_Right, edgeId_FrontRight, true ); // other direction in front loop
    b.FinishLoop( loopId_Right );
    b.FinishFace( faceId_Right );

    // Left face
    b.AddCoEdge( loopId_Left, edgeId_BottomLeft, false ); // other direction in bottom loop
    b.AddCoEdge( loopId_Left, edgeId_LeftFront, false ); // other direction in front loop
    b.AddCoEdge( loopId_Left, edgeId_TopLeft, true );   // other direction in top loop
    b.AddCoEdge( loopId_Left, edgeId_BackLeft, true );  // other direction in back loop
    b.FinishLoop( loopId_Left );
    b.FinishFace( faceId_Left );

    b.Finish();

    return b;
  }

  /// <summary>
  /// Create a cylinder to subtract from the cube.
  /// </summary>
  public BRepBuilder CreateBrepVoid()
  {
    // Naming convention for faces and edges: we 
    // assume that x is to the left and pointing down, 
    // y is horizontal and pointing to the right, 
    // z is up.

    BRepBuilder b = new BRepBuilder( BRepType.Solid );

    // The surfaces of the four faces.
    Frame basis = new Framenew XYZ5000 ), new XYZ010 ), new XYZ-100 ), new XYZ001 ) );
    CylindricalSurface cylSurf = CylindricalSurface.Create( basis, 40 );
    Plane top1 = Plane.CreateByNormalAndOriginnew XYZ001 ), new XYZ00100 ) );  // normal points outside the cylinder
    Plane bottom1 = Plane.CreateByNormalAndOriginnew XYZ001 ), new XYZ000 ) ); // normal points inside the cylinder

    // Add the four faces
    BRepBuilderGeometryId frontCylFaceId = b.AddFace( BRepBuilderSurfaceGeometry.Create( cylSurf, null ), false );
    BRepBuilderGeometryId backCylFaceId = b.AddFace( BRepBuilderSurfaceGeometry.Create( cylSurf, null ), false );
    BRepBuilderGeometryId topFaceId = b.AddFace( BRepBuilderSurfaceGeometry.Create( top1, null ), false );
    BRepBuilderGeometryId bottomFaceId = b.AddFace( BRepBuilderSurfaceGeometry.Create( bottom1, null ), true );

    // Geometry for the four semi-circular edges and two vertical linear edges
    BRepBuilderEdgeGeometry frontEdgeBottom = BRepBuilderEdgeGeometry.Create( Arc.Createnew XYZ1000 ), new XYZ9000 ), new XYZ50400 ) ) );
    BRepBuilderEdgeGeometry backEdgeBottom = BRepBuilderEdgeGeometry.Create( Arc.Createnew XYZ9000 ), new XYZ1000 ), new XYZ50-400 ) ) );

    BRepBuilderEdgeGeometry frontEdgeTop = BRepBuilderEdgeGeometry.Create( Arc.Createnew XYZ100100 ), new XYZ900100 ), new XYZ5040100 ) ) );
    BRepBuilderEdgeGeometry backEdgeTop = BRepBuilderEdgeGeometry.Create( Arc.Createnew XYZ100100 ), new XYZ900100 ), new XYZ50-40100 ) ) );

    BRepBuilderEdgeGeometry linearEdgeFront = BRepBuilderEdgeGeometry.Createnew XYZ9000 ), new XYZ900100 ) );
    BRepBuilderEdgeGeometry linearEdgeBack = BRepBuilderEdgeGeometry.Createnew XYZ1000 ), new XYZ100100 ) );

    // Add the six edges
    BRepBuilderGeometryId frontEdgeBottomId = b.AddEdge( frontEdgeBottom );
    BRepBuilderGeometryId frontEdgeTopId = b.AddEdge( frontEdgeTop );
    BRepBuilderGeometryId linearEdgeFrontId = b.AddEdge( linearEdgeFront );
    BRepBuilderGeometryId linearEdgeBackId = b.AddEdge( linearEdgeBack );
    BRepBuilderGeometryId backEdgeBottomId = b.AddEdge( backEdgeBottom );
    BRepBuilderGeometryId backEdgeTopId = b.AddEdge( backEdgeTop );

    // Loops of the four faces
    BRepBuilderGeometryId loopId_Top = b.AddLoop( topFaceId );
    BRepBuilderGeometryId loopId_Bottom = b.AddLoop( bottomFaceId );
    BRepBuilderGeometryId loopId_Front = b.AddLoop( frontCylFaceId );
    BRepBuilderGeometryId loopId_Back = b.AddLoop( backCylFaceId );

    // Add coedges for the loop of the front face
    b.AddCoEdge( loopId_Front, linearEdgeBackId, false );
    b.AddCoEdge( loopId_Front, frontEdgeTopId, false );
    b.AddCoEdge( loopId_Front, linearEdgeFrontId, true );
    b.AddCoEdge( loopId_Front, frontEdgeBottomId, true );
    b.FinishLoop( loopId_Front );
    b.FinishFace( frontCylFaceId );

    // Add coedges for the loop of the back face
    b.AddCoEdge( loopId_Back, linearEdgeBackId, true );
    b.AddCoEdge( loopId_Back, backEdgeBottomId, true );
    b.AddCoEdge( loopId_Back, linearEdgeFrontId, false );
    b.AddCoEdge( loopId_Back, backEdgeTopId, true );
    b.FinishLoop( loopId_Back );
    b.FinishFace( backCylFaceId );

    // Add coedges for the loop of the top face
    b.AddCoEdge( loopId_Top, backEdgeTopId, false );
    b.AddCoEdge( loopId_Top, frontEdgeTopId, true );
    b.FinishLoop( loopId_Top );
    b.FinishFace( topFaceId );

    // Add coedges for the loop of the bottom face
    b.AddCoEdge( loopId_Bottom, frontEdgeBottomId, false );
    b.AddCoEdge( loopId_Bottom, backEdgeBottomId, false );
    b.FinishLoop( loopId_Bottom );
    b.FinishFace( bottomFaceId );

    b.Finish();

    return b;
  }

  public Result Execute
    ExternalCommandData commandData,
    ref string message
    ElementSet elements )
  {
    UIApplication app = commandData.Application;
    UIDocument uidoc = app.ActiveUIDocument;
    Document doc = uidoc.Document;

    // Execute the BrepBuilder methods.

    BRepBuilder brepBuilderSolid = CreateBrepSolid();
    BRepBuilder brepBuilderVoid = CreateBrepVoid();

    Solid cube = brepBuilderSolid.GetResult();
    Solid cylinder = brepBuilderVoid.GetResult();

    // Determine their Boolean difference.

    Solid difference 
      = BooleanOperationsUtils.ExecuteBooleanOperation
        cube, cylinder, BooleanOperationsType.Difference );

    IList<GeometryObjectlist = new List<GeometryObject>();
    list.Add( difference );

    usingTransaction tr = new Transaction( doc ) )
    {
      tr.Start"Create a DirectShape" );

      // Create a direct shape.

      DirectShape ds = DirectShape.CreateElement( doc, 
        new ElementId( BuiltInCategory.OST_GenericModel ) );

      ds.SetShape( list );

      tr.Commit();
    }
    return Result.Succeeded;
  }
}

Note that it’s much easier to use GeometryCreationUtilities functions to create extrusions than it is to use BRepBuilder, unless the whole point of this exercise is to use the latter.

As said, using BRepBuilder to ‘manually’ construct geometry is very inconvenient and error-prone, and that isn’t its purpose.

Many thanks to my colleagues Ryuji Ogasawara, Angel Velez, Boris Shafiro and John Mitchell for sharing, correcting, and commenting on this nice example!

I added it to The Building Coder Samples release 2018.0.136.0 in the new module CmdBrepBuilder.cs.