I know that most geometry manipulation tasks are by necessity single-threaded, but I regularly have a need to perform boolean operations on many hundreds of objects simultaneously. This often fails if all objects are picked at once, but can succeed if you pick smaller subsets of the same group and boolean them first, then combine the results afterwards. This begs the question: Can these subset tasks be ascribed to separate threads and processed in parallel?
Any ideas?
Boolean operations on large geometry sets are usually single-threaded, but breaking them into smaller subsets can enable parallel processing. If your geometry engine supports multi-threading, you can assign subset tasks to separate threads,
The problem is it relies on you visually picking subsets of objects that actually intersect so the operation can actually do something. If the algorithm picks smaller groups at random, that may not be the case.
I made a component that takes a list of Breps (R=20 mm Spheres) and performs BooleanUnion on all of them, in Parallel / Multi-threaded.
This is a first version and needs some further testing.
When the Breps are ordered so the Breps are near each other in the index order of the list, it takes between 2.8 and 3 seconds to BooleanUnion some 2000 Breps into one single Brep.
Doing the BooleanUnion manually all at once, takes about 2 min 21 seconds on my computer (an AMD 5950X, 16 core).
When the list of Breps are in randomized order, the BooleanUnion takes about 22 seconds, but in this case not all items are Booleaned together into one, instead it ends up being 5 separate Breps (I manually Booleaned together these unfinished segments in micro seconds).
I have not yet debugged deep enough to figure out why the randomized list of Breps didn’t Boolean into one single Brep (but it may be useful as it is anyway?).
I did some minor changes to the component, corrected some spelling and added a description describing overall concept of the algorithm. I’ll add the same description text here:
// HISTORY ---------------------------------------------------------
// ----------
// 2025-05-05
// ----------
// 0.9.1 * Corrected comments.
// * Merged the method counting the Breps in list into
// the "Compact" method.
// * Fixed more consistent renaming of variables.
// * Added a conceptual description of the main algorithm.
//
// 0.9.0 * First version
// ------------------------------------------------------------------
// ------------------------------------------------------------------
// DESCRIPTIOMN
// ------------------------------------------------------------------
// * This main method evaluates each Brep in the input list to identify
// Brep pairs that may intersect, by checking intersections of their
// BoundingBoxes.
//
// * Identified pairs are stored as a list of index pairs, referencing
// the Brep pairs, and sent for final BooleanUnion processing by the
// "ParallelUnionBreps(pairs)" method.
//
// * The Parallel BooleanUnion method selects Breps from the brep
// lists using index pairs, then performs a BooleanUnion operation on
// them and stores the result in the original list, replacing the first Brep.
// The second Brep of a pair is set to null in the original list, as it
// has been merged into the first Brep. (This is why the lists are
// "Compacted" after each round in the while loop, which only means that
// any null items are removed, and so the lists/arrays gets shorter.)
//
// * A while-loop reiterates the algorithm if it fails to identify all
// pairs on the initial attempt, which is more probable if the input
// list items are not pre-ordered or positioned in close proximity
// within the input list.
//
// * HINT: If the component fails to produce a single Brep, consider
// adding a second component and operating them in series (connect the
// output of the first component to the input of the second component
// instance).
// ------------------------------------------------------------------
I think the component works “good enough” for now, since any failure to BooleanUnion all the Brep items the first time can be fixed by adding a second component to catch the remaining stumps.
Just something which is often forgotten about boolean operation. It is a short-cut and a general purpose command. The optimisation is not necessary about applying parallelism, but in improving the computation and their conditions.
As a software engineer, I rarely use multi-threading to optimise performance, because the bottleneck is almost always the algorithm and the way data is provided. McNeel might create variations of this algorithm, if you give it more information (so that redundant checks and computations can be by-passed)
Surface complexity (Degree, Spans etc) has a big impact on the computation of intersections. If you create better geometrical conditions, you can increase performance significantly and reduce the occurrence of error. Also note, if you operate on a larger single and complex surface with multiple objects, you basically increase the complexity of the Brep boundary on each iteration. Therefore dividing the surface-to-operate-on into multiple pieces can speed up computation greatly.
Also prevent condition which are causing tolerance related issues. Singularities and operation on already cut shapes. Always choose a geometry which overlaps clearly and is clean. Sometimes its just better to bypass Boolean operation and use a couple of trimming operations instead.
I hardly had situations were Boolean operation on hundreds of objects were necessary. If you create a pattern, then use Grasshopper or a script to build it logical. Also consider that you might not need certain complexities to visualise or produce something. E.g. a perforation can also be rendered by applying a texture…