Although there probably is no true parallelism when running multithreaded tasks, my guess is that this is theoretically impossible. If you have a single polysurface and a number of cutters, and they all want to split the former at the same time, you’ll probably end up with a paradox, since the cuts should happen successively and not all at the same time.
Yes, I suspect something like that. I was wondering if the process of slicing breps (with a tree structure, multiple breps, and multiple slice objects), even if it can’t process everything at the same time, could split it by branches and send each one to a different processor.
Note that on the “B” tree input, only the first item of each branch is used, and the script stops iterate until the last branch of either of the 2 trees, “B” and “C”, if one is longer, everything past that is ignored.
Code:
using System.Threading.Tasks;
private void RunScript(DataTree<Brep> B, DataTree<Brep> C, ref object R)
{
Grasshopper.DataTree<Brep> results = new Grasshopper.DataTree<Brep>();
int n = Math.Min(B.Paths.Count, C.Paths.Count);
Brep[][] fragments = new Brep[n][];
Parallel.For(0, n, (i) => {
Brep[] f = B.Branches[i][0].Split(C.Branches[i], this.RhinoDocument.ModelAbsoluteTolerance);
fragments[i] = f;
});
for(int i = 0;i < n;i++){
results.AddRange(fragments[i], new GH_Path(i));
}
R = results;
}
You guessed right.
But you need to manage data correctly.
In your script you are working with as input simple lists (I guess), to work with parallel tasks you need to let your data enter your script as tree.
Then, you also need to manage the results, because you can’t have the different threads access the same final tree and do the .Addrange simultaneously. Better practice is to store each result in a pre-allocated array, like my Brep[][] and only later iterate (single thread aka normally for cycle) to send all data from that array to a datatree.
… generally?
Yes, we are working with rhino libraries, so who knows what’s happening behind the scenes…
It’s strange because your example works very well, but when I try to apply it to another example, it keeps computing much longer. Any suggestions? Am I wrong somewhere?
In general Elapsed time is related with the Topology AND the splitters (i.e. Is Brep a Blob? (bad news), Are splitters Nurbs Surfaces? (bad*bad news). Other than that splitting something against N splitters is done (by the book) via a classic Recursion where a Tree keeps track on results on a per Recursion Loop basis.
For the “fasted” case (A thread safe approach + Brep as Box + splitters: rnd Planes + Loops around 15 or so) you should get Elapsed times between 7-- ms (some latest I9x on duty) and 30++ ms (some old crap I5 [I always use a crap CPU when writing/testing code]).
true, the screenshot shows a wrong connection in the tree stats (working with the list before), so if you change the connection from L to C, you’ll end up with all boxes (cutters) except the one you want to cut. Unfortunately, that only reduces the time to 6 minutes .
If you have random blocks (boxes) and you have to do some random buildings out of them (this means that the random splitting planes have Z.Axis // Plane.World.XY) then for about 500 blocks and divisions from 5 to, say, 10 … you should expect 50-100 milliseconds.
But is way better ro randomly Split Rectangles/Polylines (that’s real-time: no need for any // stuff) and then get “solid” buildings at varying heights (say proportional to the Polyline area).
In this case use
Brep building = Brep.CreateFromOffsetFace Method (using the base Polyline as Curve)
For instance this C# does random buildings in more or less real-time (by dealing solely - at Split phase - with Polylines) :
Wow, the timings sound great, impressive.
We are using the georeferenced curves (geographic institute of Spain) to create blocks by extruding the curve (perimeter and height data).
The problem comes from extracting the exterior facade of the building. We need to get only the breps that intersect with each building and remove the faces that intersect with it (we had previously gone through the solid union process but used to return incomplete results).
The problem is that we have to apply the function to all cities in Spain using a batch process. There is no drama except in Barcelona or Madrid because there is much more density → more intersections → exponential increase in time (days ).
screenshot: the white lines are the original brep, red ones are the splitters and the purple is the cutting result.
I’ll check out the RH method and try the cluster-curve-solid process, it seems by far the most efficient process. The screenshots are very useful.
Thank you very much for the tips!
If you have to Cluster big N of things … we occasionaly use a “pre check” related with Intervals (derived from Boxes in X/Y/Z) Intersections: if true then check further if false abandon ship (for a given pair, that is). This case captured deals with coplanar things thus a Z Interval is not used.
Obviously nothing is free: doing the Intervals from Boxes has a certain cost … but just stick to the broad aspect of things (BTW: avoid killing the mosquito with the bazooka if you have few things on hand).
It’s a very nice way to reduce cluster size, it’s not free but faster than the entire operation, i see, very cool. I’ll put the bazooka aside for a little while .
great help!
Mastermind a way to “separate” the rnd Boxes … so that we’ll get more than one Cluster.
Sample the Box corners X/Y/Z coords in an Intervals Tree.
Write the Interval X/Y/Z Ccx pre-check Method. But wait: for this case this is the ONLY criterion needed (but that’s because we have only “ortho” Boxes on hand - but what happens if you Rotate them randomly? You tell me).
Ccx Cluster the Boxes into a ConnectivityTree (of type int) . No need to sample the Boxes itselfs.
Optional: Using a bias probability value (0 to 1) separate the splitters from the rest. Do that as follows if(rnd.NextDouble() < bias) … that’s a splitter. Or you can opt to Split (recursively) any VS any (within a given Cluster, that is).
Use SOLELY code for that kind of game. In case that you want to try // stuff remember that Thread Safety is everything.