How to execute BooleanOperations on Revit-Solid by AutoCAD?

How to execute BooleanOperations on Revit-Solid by AutoCAD?

ankofl
Advocate Advocate
29,434 Views
20 Replies
Message 1 of 21

How to execute BooleanOperations on Revit-Solid by AutoCAD?

ankofl
Advocate
Advocate

We are all familiar with the problems with Boolean operations on solid objects. A lot of exceptions occur during the operations of union, intersection and subtraction. In one of the branches, it was proposed to export Revit-Solid to Cascad-Colid, perform painful and other operations in it, and upload back only the result of such calculations, or transfer Cascad-Solid back to Revit-Solid. 
SIX years after the beginning of this branch, the developers from Autodesk have not provided us with a working solution to this problem.
In my opinion, this is too complicated and time-consuming task to translate from Revit to Cascada, and it may be better to export Solid not to Cascad, but to AutoCAD, or rather use the loaded libraries to work with AutoCAD, but perform all operations in the Revit process. The already established export from DWG to RWT speaks in favor of this decision. Maybe it is possible to export Revit-Solid to AutoCAD-Solid and perform Boolean operations already there?

0 Likes
Accepted solutions (2)
29,435 Views
20 Replies
Replies (20)
Message 21 of 21

ankofl
Advocate
Advocate

UPD: 16.10.2024

At the moment, I have managed to union 100% of 1717 solids (screenshot below)

ankofl_1-1729088387265.png
by uploading to off-files and union them using CGAL. The result is an output off-file (it is in the screenshot below)

ankofl_0-1729088225131.png
P.S: When trying to combine the Solids data through Revit, several dozens exceptions occurred about the inability to perform a Boolean operation on solid.

I checked in CGAL that the resulting mesh is correct, i.e. it does not have self-intersections, limits the positive volume, is correctly oriented, and is also a triangular, not a polygonal mesh.

Then I decided to create a Solid based on the off file using the following methods:

 

public static bool ReadOFF(string offFilePath, out List<XYZ> vertices, out List<int[]> triangles)
{
    // 1. Чтение файла OFF
    vertices = [];
    triangles = [];

    if (!File.Exists(offFilePath))
    {
        return false;
    }

    double k = UnitUtils.ConvertToInternalUnits(1, UnitTypeId.Meters);

    var strs = File.ReadAllLines(offFilePath);

    // Чтение количества вершин и треугольников
    string[] counts = strs[1].Split();
    int vertexCount = int.Parse(counts[0]);
    int faceCount = int.Parse(counts[1]);

    // Чтение координат вершин
    for (int i = 3; i < 3 + vertexCount; i++)
    {
        string[] vertexData = strs[i].Split(' ');
        double x = double.Parse(vertexData[0]) * k;
        double y = double.Parse(vertexData[1]) * k;
        double z = double.Parse(vertexData[2]) * k;
        vertices.Add(new XYZ(x, y, z));
    }

    // Чтение треугольников (граней)
    for (int i = 3 + vertexCount; i < 3 + vertexCount + faceCount; i++)
    {
        string[] faceData = strs[i].Split("  ")[1].Split(' ');
        if (faceData.Length == 3)  // Треугольная грань
        {
            int v1 = int.Parse(faceData[0]);
            int v2 = int.Parse(faceData[1]);
            int v3 = int.Parse(faceData[2]);
            triangles.Add([v1, v3, v2]);
        }
    }

    return vertices.Count >= 3;
}

 

And:

 

public static bool SolidFromMesh(List<XYZ> vertices, List<int[]> triangles, out Solid solid)
{
	solid = null;
	try
	{
		// 2. Инициализация BRepBuilder для создания замкнутого объёма
		BRepBuilder brepBuilder = new(BRepType.OpenShell);

		brepBuilder.SetAllowShortEdges();
		brepBuilder.AllowRemovalOfProblematicFaces();

		// 3. Проход по треугольникам и создание граней
		foreach (int[] triangle in triangles)
		{
			XYZ v1 = vertices[triangle[0]];
			XYZ v2 = vertices[triangle[1]];
			XYZ v3 = vertices[triangle[2]];

			if (v1.DistanceTo(v2) < 0.5 || v2.DistanceTo(v3) < 0.5 || v3.DistanceTo(v1) < 0.5)
			{
				continue; // Пропустить такие треугольники
			}

			// Создание плоскости для треугольной грани
			Plane trianglePlane = Plane.CreateByThreePoints(v1, v2, v3);

			// Добавление грани в BRepBuilder
			BRepBuilderGeometryId faceId = brepBuilder.AddFace(
				BRepBuilderSurfaceGeometry.Create(trianglePlane, null), false);

			// Создание рёбер треугольника
			BRepBuilderEdgeGeometry edge1 = BRepBuilderEdgeGeometry.Create(v1, v2);
			BRepBuilderEdgeGeometry edge2 = BRepBuilderEdgeGeometry.Create(v2, v3);
			BRepBuilderEdgeGeometry edge3 = BRepBuilderEdgeGeometry.Create(v3, v1);

			// Добавление рёбер в BRepBuilder
			BRepBuilderGeometryId edgeId1 = brepBuilder.AddEdge(edge1);
			BRepBuilderGeometryId edgeId2 = brepBuilder.AddEdge(edge2);
			BRepBuilderGeometryId edgeId3 = brepBuilder.AddEdge(edge3);

			if (!brepBuilder.IsValidEdgeId(edgeId1) ||
				!brepBuilder.IsValidEdgeId(edgeId2) ||
				!brepBuilder.IsValidEdgeId(edgeId3))
			{

			}

			// Создание цикла для грани
			BRepBuilderGeometryId loopId = brepBuilder.AddLoop(faceId);

			var coEdgeId1 = brepBuilder.AddCoEdge(loopId, edgeId1, false); 
			var coEdgeId2 = brepBuilder.AddCoEdge(loopId, edgeId2, false);
			var coEdgeId3 = brepBuilder.AddCoEdge(loopId, edgeId3, false);

			if (!brepBuilder.IsValidEdgeId(coEdgeId1) ||
				!brepBuilder.IsValidEdgeId(coEdgeId2) ||
				!brepBuilder.IsValidEdgeId(coEdgeId3))
			{

			}

			if (!brepBuilder.IsValidLoopId(loopId))
			{

			}

			if (!brepBuilder.IsValidFaceId(faceId))
			{

			}

			brepBuilder.FinishLoop(loopId);
			brepBuilder.FinishFace(faceId);
		}

		// 4. Завершение создания BRep
		brepBuilder.Finish();

		if (brepBuilder.IsResultAvailable())
		{
			solid = brepBuilder.GetResult();
			return true;
		}
	}
	catch (Exception e)
	{
		TaskDialog.Show("SolidHelper.SolidFromMesh()", e.Message);
	}
	return false;
}

 

But I came across the fact that with any combinations and variants of the

 

public static bool SolidFromMesh(List<XYZ> vertices, List<int[]> triangles, out Solid solid)

 

method when checking in

 

brepBuilder.IsResultAvailable()

 

at the end will be true only in the case of

 

BRepBuilder brepBuilder = new(BRepType.OpenShell)

 

In the case of

 

BRepBuilder brepBuilder = new(BRepType.Solid)
brepBuilder.IsResultAvailable()

 

 - always returns false
I decided that the data was too complex for Revit, and then I created an off-file containing a regular cube:

 

OFF
8 12 0

-50 -50 -50
50 -50 -50
50 50 -50
-50 50 -50
-50 -50 50
50 -50 50
50 50 50
-50 50 50
3  2 1 0
3  3 2 0
3  4 5 6
3  4 6 7
3  0 4 7
3  0 7 3
3  6 5 1
3  2 6 1
3  0 1 5
3  0 5 4
3  6 2 3
3  7 6 3

 

In this case, I received the correct filled Solid (despite the instruction to create OpenShell).

ankofl_3-1729091017864.png

Based on this, I believe that the main problem lies in the verification:

 

if (v1.DistanceTo(v2) < 0.5 || v2.DistanceTo(v3) < 0.5 || v3.DistanceTo(v1) < 0.5)
{
	continue; // Пропустить такие треугольники
}

 

With such a check, of course, instead of Solid, we will get an Open Shell, as can be seen in the screenshot below:

ankofl_2-1729090969172.png
If you make a check of at least 0.1

 

if (v1.DistanceTo(v2) < 0.1 || v2.DistanceTo(v3) < 0.1 || v3.DistanceTo(v1) < 0.1)
{
	continue; // Пропустить такие треугольники
}

 

 

 

brepBuilder.IsResultAvailable()

 

 

 - always returns false


Dear @jeremy_tammik can I ask you or someone from the development team to clarify in more detail what the introductory conditions are for the grid source data? Minimum edge lengths, minimum face area, etc? Because at the moment it seems to me that such an accurate geometry in Revit simply cannot be built from such constraints.

Thanks!

0 Likes