Boolean Operation Fail

Boolean Operation Fail

marcelo_quevedo
Contributor Contributor
24,246 Views
62 Replies
Message 1 of 63

Boolean Operation Fail

marcelo_quevedo
Contributor
Contributor

Hello,

The core of our application works with solid operations between solids, and we constantly faced Boolean operation issues by using the ExecuteBooleanOperation() API method. It throws the next exception that doesn’t allow to do the Boolean operation.

 

“Failed to perform the Boolean operation for the two solids. This may be due to geometric inaccuracies in the solids, such as slightly misaligned faces or edges. If so, eliminating the inaccuracies by making sure the solids are accurately aligned may solve the problem. This also may be due to one or both solids having complexities such as more than two faces geometrically meeting along a single edge, or two coincident edges, etc. Eliminating such conditions, or performing a sequence of Boolean operations in an order that avoids such conditions, may solve the problem.”

 

I created a simple C# sample with a command that allows to do a Boolean operation between two solids. Also, I have attached two Revit files. Each file contains two intersected directShape objects. Using the C# sample and those files you could reproduce the issue by trying to do a Boolean operation between the two objects.

 

We will be very grateful if you could help us with some directions to solve the issue, and it would be great if you take into consideration this API issue for the future Revit APIs.

 

Thanks a lot, in advance!

Marcelo

 

24,247 Views
62 Replies
Replies (62)
Message 41 of 63

tommy.stromhaug
Contributor
Contributor

Yes, this is a neverending tail. As I see it you have two low hanging options. 

In 2025 revit can export step files. If this can be done with the api it would be great.Have not checked it yet.

But I am planning of making a simple small application(.exe) with OpenCascade(https://dev.opencascade.org/) and automatically running the boolean operations there and then reopen the resulting Step into Revit.

If not using 2025 of Revit you have to make your own export and then converting the geometry to Open Cascade geometry entites. For most of the geometry in Revit this is quite simple. Revits Hermite splines have to be approximated to Open Cascades B-splines. Open Cascade has functionality for this. 

Happy coding!

Message 42 of 63

ankofl
Advocate
Advocate

Do you have good experience in C++ and experience working with OpenCascade? Once I tried to turn something like this around - it turned into a living hell 

As a result, for my current project, we managed to achieve the necessary results using a Boolean operation not on Solid, but on Face, although for this we also have to upload geometry to json, upload it to Unity, and use two third-party libraries and 3D transformations to perform Boolean operations in 2D, and then return the result back to 3D.

0 Likes
Message 43 of 63

tommy.stromhaug
Contributor
Contributor

I agree. C++ can be a living hell 🙂 I have another option using OpenCascade with c#, but if I remeber correctly I had to build OpenCascade myself. Check this git repo : https://github.com/Macad3D/Macad3D.

He has made a c# wrapper for most of the OpenCascade. His main project is a cad application but he has made a c

# wrapper himself. It is possible to build the whole project and only using the wrapper. I did that a few years ago.

Maybe the OpenCascade dll files are included in the project.

 

 

Message 44 of 63

jeremy_tammik
Alumni
Alumni

The development team analysed the issues reported in REVIT-122714 [Boolean Operation Fail - case 13578517] and say:

  

This item is really a group of issues.

  

The Subtraction_error is solved by the user more carefully aligning the elements, and that is what they did. The request is that Revit do this alignment for them.

  

The items DoABooleanOperation 1 and 2 have some solids that can't be selected for cut or join. They are structural framing members with no type properties - I guess they were made through API. At any rate they are not very useful for debugging. Clearly this is another case of booleans needing to be "better" but we have many of those.

  

Is there any meat in this burger?

  

This requires more analysis...

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 45 of 63

tommy.stromhaug
Contributor
Contributor

Hi. As follow up i want to report that i made a small test library in Revit 2025 and a c++/cli project in c++ using Open Cascade. Exporting elements as step require that you either isolate(hide all other) the element in the current view or make a temporary view with the element in question. Then you call the code in c++ from the c++/cli code to do the actual boolean operation. After that the result is written to a .step file for the Revit addin to import an place in the project. At this point i have not tested various algorithms in Open Cascade like using the fuzzy logic. I have also not been doing any "hard" problems testing in Revit(Problems files I know fails in Revit) yet. The Booleans in Open Cascade is said to be quite robust, but remember that the problem of boolean operations is difficult as a whole and it is an active research field. Currently we have customers that require some boolean operations on ship hulls and that forced me to rethink the whole work-flow. Anyway, the export of step files in Revit 2025 also made it possible to use any functions of the cad-kernel(Open Cascade) with some simple c++ functions. Communication between Open Cascade and Revit addin is step files and that could make a problem if there are hundreds of operations. Hope this can help others that is having problems with boolean operations. When I get time to test this properly i will be happy to share some code snippets.

Message 46 of 63

jeremy_tammik
Alumni
Alumni

Thank you for the very interesting report. Good luck moving forward with this, and looking forward to your future progress!

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 47 of 63

ghxc
Community Visitor
Community Visitor

I agree with you.I suggest Autodesk to tell us which part is "geometric inaccuracies" at least,then I can avoid this problem.

Message 48 of 63

tommy.stromhaug
Contributor
Contributor

I do not think it is that simple. Even the most robust cad kernels do have problems in some situations. Revit have problems in many situations and can not be considered robust. Here are a list of possible problems situations :

 

Short edges and sliver faces

Huge tolerant entities and collapsed features

Improper intersections

Near-coincident entities

Near-tangent interactions

Complicated intersections

 

It is almost impossible to avoid all the elements in the list.

0 Likes
Message 49 of 63

ido_b
Explorer
Explorer

Its 2025 and still no solution?

0 Likes
Message 50 of 63

ankofl
Advocate
Advocate

Hi @ido_b 

I
think you can use CGAL for this. However, although you can use it to perform a Boolean operation on two Solid objects, this will be done in a separate tongue-tied application based on triangular grid data, which you will save as files. But in the context of Revit - you probably won't be able to create a Solid Geometry based on the result obtained from CGAL, for the same reason that you couldn't perform a Boolean operation in Revit itself- the accuracy of the data you get from CGAL is much better than Revit is able to recreate. Therefore, at best you will have an open grid rather than a Solid one.

0 Likes
Message 51 of 63

tommy.stromhaug
Contributor
Contributor

Hi, I think it would be impossible to recreate Breps from the results of CGAL. On the other hand you could use OpenCascade. It operates on breps. 
Getting it back as breps is as easy as importing the results from OpenCascade as a step file into a spesific view in Revit. 

0 Likes
Message 52 of 63

fujiwara-revit-1
Explorer
Explorer

次のようにやってみました。いかかでしょうか?   

 List<Solid> BooleanOplation_Common_Cascade(Solid solid1,Solid solid2,Transaction t)
 {
    string tempPath = Path.GetTempPath();
    string savepath_revitOut = Path.Combine(tempPath, "temp_export.step");
    
    DirectShape elem1 =doc.GetDirectShape(solid1);
    DirectShape elem2 = doc.GetDirectShape(solid2);
    //一時ビュー作成
    View3D TEMPview = CreateTempo3DViewIfNotExists();
    List<ElementId> tempElems = new List<ElementId> { elem1.Id, elem2.Id };
    IsolateOnlyElementsInView(TEMPview, tempElems);
    t.Commit();
    t.Start();

    STEPExportOptions options = new STEPExportOptions
    {
       ViewId= TEMPview.Id,
       TargetUnit=ExportUnit.Millimeter,
       
    };

    doc.Export(tempPath, "temp_export.step", options);
    doc.Delete(tempElems);
    //OCCTで ファイルを読み込む
    // STEPリーダーを初期化
    STEPControl_Reader reader = new STEPControl_Reader();

    
    IFSelect_ReturnStatus status = reader.ReadFile(savepath_revitOut);
    if (status != IFSelect_ReturnStatus.IFSelect_RetDone)
        throw new Exception("STEPファイルの読み込みに失敗しました");

    // これを追加!
    reader.TransferRoots();
    // シェイプを取得
    TopoDS_Shape shape = reader.OneShape();
    // Solid を探す
    TopExp_Explorer explorer = new TopExp_Explorer(shape, TopAbs_ShapeEnum.TopAbs_SOLID);

    List<TopoDS_Solid> topoDS_Solids = new List<TopoDS_Solid>(); 

    while (explorer.More)
    {
        TopoDS_Solid solid = explorer.Current as TopoDS_Solid;
        topoDS_Solids.Add(solid);
        explorer.Next();
    }

    if (topoDS_Solids.Count != 2) 
        throw new Exception("ソリッドは2個である必要があります");
    // Union(Fuse)演算
    TopoDS_Shape intersection = new BRepAlgoAPI_Common(topoDS_Solids[0], topoDS_Solids[1]).Shape;

    // 結果をSTEPとして出力
    STEPControl_Writer writer = new STEPControl_Writer();
    writer.Transfer(intersection, STEPControl_StepModelType.STEPControl_AsIs);

    string saveOCCTpath = Path.Combine(tempPath, "temp_OCCToutput.step");
    IFSelect_ReturnStatus writeStatus = writer.Write(saveOCCTpath);

    //レビットで再読み込み
    STEPImportOptions options2 = new STEPImportOptions() 
    { 
        Placement= ImportPlacement.Origin,
        Unit= ImportUnit.Millimeter
    };
   
    ElementId elemID3 =doc.Import(saveOCCTpath, options2, doc.ActiveView);
    Element elem3 = doc.GetElement(elemID3);
    elem3.Pinned = false;
    ImportInstance? importInstance = elem3 as ImportInstance;
    ElementId id = importInstance.LevelId;
    Level level = doc.GetElement(id) as Level;
    double move = -level.Elevation;
    ElementTransformUtils.MoveElement(doc, elemID3, new XYZ(0, 0, move));
    var result= RevitUtil.GetElementSolids(elem3);

    doc.Delete(elemID3);
    return result;

    View3D CreateTempo3DViewIfNotExists()
    {
        // 既に「TEMPO」という名前の3Dビューが存在するか確認
        View3D existingView = new FilteredElementCollector(doc)
            .OfClass(typeof(View3D))
            .Cast<View3D>()
            .FirstOrDefault(v => v.Name == "TEMPO");

        if (existingView != null)
            return existingView; // すでに存在しているのでそれを返す

        // 3Dビュー用の ViewFamilyType を取得
        ViewFamilyType viewFamilyType = new FilteredElementCollector(doc)
            .OfClass(typeof(ViewFamilyType))
            .Cast<ViewFamilyType>()
            .FirstOrDefault(vft => vft.ViewFamily == ViewFamily.ThreeDimensional);

        if (viewFamilyType == null)
            throw new InvalidOperationException("3Dビュー用の ViewFamilyType が見つかりません。");

        // 3Dビューを作成
        View3D newView = View3D.CreateIsometric(doc, viewFamilyType.Id);
        newView.Name = "TEMPO";
        return newView;
    }
}

                             

0 Likes
Message 53 of 63

fujiwara-revit-1
Explorer
Explorer

Hi everyone,

I often run into issues when using the BooleanOperationsUtils.ExecuteBooleanOperation() method in Revit API. It frequently fails due to geometric inaccuracies between solids. So I created a workaround using OpenCascade and STEP file exchange.

Here’s a simplified version of my method. It performs Boolean Common operation between two Revit solids via STEP export/import using OpenCascade in the background:                   

List<Solid> BooleanOperation_Common_Cascade(Solid solid1, Solid solid2, Transaction t)
{
    string tempPath = Path.GetTempPath();
    string exportPath = Path.Combine(tempPath, "temp_export.step");
    
    DirectShape elem1 = doc.GetDirectShape(solid1);
    DirectShape elem2 = doc.GetDirectShape(solid2);

    View3D tempView = CreateTemp3DViewIfNotExists();
    IsolateOnlyElementsInView(tempView, new[] { elem1.Id, elem2.Id });

    t.Commit();
    t.Start();

    // Export solids to STEP
    STEPExportOptions options = new STEPExportOptions
    {
        ViewId = tempView.Id,
        TargetUnit = ExportUnit.Millimeter
    };
    doc.Export(tempPath, "temp_export.step", options);

    doc.Delete(new[] { elem1.Id, elem2.Id });

    // Load STEP into OpenCascade
    STEPControl_Reader reader = new STEPControl_Reader();
    if (reader.ReadFile(exportPath) != IFSelect_ReturnStatus.IFSelect_RetDone)
        throw new Exception("Failed to read STEP file");

    reader.TransferRoots();
    TopoDS_Shape shape = reader.OneShape();

    List<TopoDS_Solid> solids = new List<TopoDS_Solid>();
    TopExp_Explorer explorer = new TopExp_Explorer(shape, TopAbs_ShapeEnum.TopAbs_SOLID);
    while (explorer.More)
    {
        solids.Add((TopoDS_Solid)explorer.Current);
        explorer.Next();
    }

    if (solids.Count != 2)
        throw new Exception("Expected 2 solids");

    // Perform Boolean Common (Intersection)
    TopoDS_Shape result = new BRepAlgoAPI_Common(solids[0], solids[1]).Shape;

    // Export result as STEP again
    string outPath = Path.Combine(tempPath, "temp_result.step");
    STEPControl_Writer writer = new STEPControl_Writer();
    writer.Transfer(result, STEPControl_StepModelType.STEPControl_AsIs);
    writer.Write(outPath);

    // Re-import into Revit
    STEPImportOptions importOptions = new STEPImportOptions
    {
        Placement = ImportPlacement.Origin,
        Unit = ImportUnit.Millimeter
    };
    ElementId imported = doc.Import(outPath, importOptions, doc.ActiveView);
    Element importElem = doc.GetElement(imported);

    // Adjust vertical position if needed
    ImportInstance inst = importElem as ImportInstance;
    Level level = doc.GetElement(inst.LevelId) as Level;
    double adjustZ = -level.Elevation;
    ElementTransformUtils.MoveElement(doc, imported, new XYZ(0, 0, adjustZ));

    var resultSolids = RevitUtil.GetElementSolids(importElem);
    doc.Delete(imported);
    return resultSolids;
}

 

Message 54 of 63

ankofl
Advocate
Advocate

Hi @fujiwara-revit-1 

How many Solid objects did you manage to combine in this way?

Thanks!

0 Likes
Message 55 of 63

tommy.stromhaug
Contributor
Contributor

Cool solution. My method was almost just like yours 🙂 This solution solved all my problems with "simple" boolean solutions that should not fail in Revit. I did not shar my solution because OpenCascade can be a **** to setup properly. Once done things changes to the better. Also remember that OpenCascade can be used  in almost everything in Revit! 

0 Likes
Message 56 of 63

essam-salah
Collaborator
Collaborator

@ankofl wrote:

Hi @ido_b 
I
think you can use CGAL for this. However,..


i've been using Autocad .Net Api for 3D Solids boolean operations since Autocad 2020 and it works very well.

0 Likes
Message 57 of 63

tommy.stromhaug
Contributor
Contributor

CGAL is good for meshes. Reverse a mesh into a brep however is an even bigger problem.
The Autocad kernel is robust, but then you need two licences to solve this problem.

0 Likes
Message 58 of 63

ankofl
Advocate
Advocate

Hi @tommy.stromhaug 

What is
the problem of converting BREP to Mesh and back?

CGAL has built-in, standard methods for this.

0 Likes
Message 59 of 63

tommy.stromhaug
Contributor
Contributor

Hi,

 

The process of converting a mesh back to a BREP is a classic example of reverse engineering. CGAL has no functions for this. A B-spline converted to mesh is very hard to convert back to B-spline. Think of an object of a lot of splines and other surfaces converted to mesh, then try to get the original BREP back 🙂 You are stuck with the mesh after using CGAL.

0 Likes
Message 60 of 63

ankofl
Advocate
Advocate
typedef CGAL::Exact_predicates_exact_constructions_kernel K;
typedef CGAL::Polyhedron_3<K> Mesh;
typedef CGAL::Nef_polyhedron_3<K, CGAL::SNC_indexed_items> Nef;
Nef N(mesh);

I'm sorry, it's my mistake, I confused BREP and Nef. After reading a little bit about BREP itself, I found out that it is better suited for CAD, because it is more reliable and productive. In this case, I don't understand why it works so poorly in Revit, if BREP objects translated into grids work better in third-party software?

0 Likes