I was just going through the code, thank you very much for the answers, I really appreciate it.
I’m pretty new with c#, I guess I’ll go slowly implementing all the logic.
i don’t know what happens if i rotate them randomly
, maybe you could evaluate the distance between the rotated vectors and the angle.
Elementary my dear Watson: then the Intervals check is not an assured condition for the existence of ccx Events meaning that we need Plan B: a classic ccx check. That said I do hope that you know the famous Separation Axis Theorem (for convex objects). Read and enjoy:
MethodOfSeparatingAxes.pdf (334.2 KB)
BTW: how the Box Intervals are defined:
So … this is your target (for the moment):
Depending on the Topology of Boxes this approach may (or may not) have a big impact on Elapsed times most notably for the next phase. See for instance that instead of circa 2400000 (using 1600 Boxes => ~ 1600*1600 ) comparisons we have “only” used about half of them (hope dies last).
And this is the general case (Any orientation, Breps or Meshes + 6 Clustering options including K-Means etc etc).
joder!, I’m overwhelmed, it’s a brilliant process and the elapsed computation time is insane. I’m definitely going to read up on the split axes theorem and get better at C#. Your workflow is very inspiring.
I am blown away by the way it organizes everything from geometry, topology extraction, classification types… all in one/two C# files.
Really cool process, thanks so much for sharing.
Take a big break (+ espresso + cigars) and read - many times - what the Master has to say:
https://www.albahari.com/threading/
Other than that if you hit the wall with the Clustering algo … well … drop a word (but is a good “basic” challenge - after all Engineering is not about Geometry: it’s about managing Geometry [not to mention data/meta data mining]).
All in all: if you do something and that something is slow … blame first yourself (guilty at 99.9999% of cases) and then - maybe - blame the stupid computer.
For instance let’s talk about single level (i.e. not nested) Containment Clustering (multi leveled is called HAC Clustering). Compare Elapsed using the Intervals “pre-check” VS not using it.
Perfect! reading the resource right now. Yes, the pre-check steps make a difference. Extremely interesting, where can I find documentation on managing geometry data with C#? sounds essential
Below is an attempt to multithread SplitBrepMultiple using node-in-code. My initial thought was just to get the output in the same format (trimmed srfs and open breps, not just trimmed srfs), but I’m finding there must be some other things the OOTB component does which make it faster than just Brep.Split. I’m also using Clash to cluster the splitters.
try_multi_BW.gh (16.3 KB)
Node-in-code
private void RunScript(DataTree<object> B, DataTree<object> 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) => {
///Node in code to get consistent results from OOTB Split Brep Multiple
///https://discourse.mcneel.com/t/calling-grasshopper-components-in-c-for-grasshopper/66610/4
var func_info = Rhino.NodeInCode.Components.FindComponent("SplitBrepMultiple");
var func = func_info.Delegate as dynamic;
var result = func(B.Branches[i][0], C.Branches[i]);
Brep[] b = new Brep[result.Count];
for (int j = 0; j < result.Count; j++)
{
b[j] = (Brep) result[j];
}
fragments[i] = b;
});
for(int i = 0;i < n;i++){
results.AddRange(fragments[i], new GH_Path(i));
}
R = results;
}
-Brian
Well … I should rather stay on the state of things as are exposed insofar … but … well … things are not that simple.
-
Most people believe that the Holly Grail of speed is // computing (+ a Thread safe policy). So they buy Cores and then they press the red button. In most of cases nothing happens (i.e. no noticable diff in Elapsed times > what to do? > easy > just buy some Cores more > hope dies last).
-
See these 2 C#'s : Using 2000 test rnd Boxes the left does the Ccx Clustering with a “classic” mindset in circa 200ms . The right yields the same result (using various pro tricks) in 8ms (less than 3 using an I9 12th Gen).
None is using // stuff.
Moral of the day: Long is the Path (and hilly).
It works blasting fast! Thank you very much for sharing the code. It is very interesting to see how you solve it.
8ms… It’s amazing, answer that you upload, answer that blows my mind. Long path i see.
This is a similar approach, but using SolidUnion as multithreaded node-in-code. The Disjoint Sets C# component helps to cluster the boxes based on Clash, reducing the load on SolidUnion.
try_multiBooleanUnion_BW.gh (19.0 KB)
Disjoint Sets Code
private void RunScript(DataTree<int> connectionIDs, ref object sets)
{
///
var dus = new DisjointUnionSets(connectionIDs.BranchCount);
for (int i = 0; i < connectionIDs.BranchCount; i++)
{
foreach (int id in connectionIDs.Branches[i])
{
dus.union(connectionIDs.Branches[i][0], id);
}
}
List<int> nodeList = connectionIDs.AllData().Distinct().ToList();
///Create separate lists of indexes grouped by their root node
var groups = nodeList.GroupBy(g => dus.find(g));
Grasshopper.DataTree<int> s = new Grasshopper.DataTree<int>();
int j = 0;
foreach (var group in groups)
{
s.AddRange(group, new GH_Path(j));
j++;
}
sets = s;
}
// <Custom additional code>
///Disjoint Union Set from
///https://www.geeksforgeeks.org/disjoint-set-data-structures/
class DisjointUnionSets
{
int[] rank, parent;
int n;
// Constructor
public DisjointUnionSets(int n)
{
rank = new int[n];
parent = new int[n];
this.n = n;
makeSet();
}
// Creates n sets with single item in each
public void makeSet()
{
for (int i = 0; i < n; i++)
{
// Initially, all elements are in
// their own set.
parent[i] = i;
}
}
// Returns representative of x's set
public int find(int x)
{
// Finds the representative of the set
// that x is an element of
if (parent[x] != x)
{
// if x is not the parent of itself
// Then x is not the representative of
// his set,
parent[x] = find(parent[x]);
// so we recursively call Find on its parent
// and move i's node directly under the
// representative of this set
}
return parent[x];
}
// Unites the set that includes x and
// the set that includes x
public void union(int x, int y)
{
// Find representatives of two sets
int xRoot = find(x), yRoot = find(y);
// Elements are in the same set,
// no need to unite anything.
if (xRoot == yRoot)
return;
// If x's rank is less than y's rank
if (rank[xRoot] < rank[yRoot])
// Then move x under y so that depth
// of tree remains less
parent[xRoot] = yRoot;
// Else if y's rank is less than x's rank
else if (rank[yRoot] < rank[xRoot])
// Then move y under x so that depth of
// tree remains less
parent[yRoot] = xRoot;
else // if ranks are the same
{
// Then move y under x (doesn't matter
// which one goes where)
parent[yRoot] = xRoot;
// And increment the result tree's
// rank by 1
rank[xRoot] = rank[xRoot] + 1;
}
}
}
Solid Union Node-in-code
private void RunScript(DataTree<object> B, ref object R)
{
Grasshopper.DataTree<Brep> results = new Grasshopper.DataTree<Brep>();
int n = B.Paths.Count;
Brep[][] fragments = new Brep[n][];
Parallel.For(0, n, (i) => {
///Node in code to get consistent results from OOTB Split Brep Multiple
///https://discourse.mcneel.com/t/calling-grasshopper-components-in-c-for-grasshopper/66610/4
var func_info = Rhino.NodeInCode.Components.FindComponent("SolidUnion");
var func = func_info.Delegate as dynamic;
var result = func(B.Branches[i]);
Brep[] b = new Brep[result.Count];
for (int j = 0; j < result.Count; j++)
{
b[j] = (Brep) result[j];
}
fragments[i] = b;
});
for(int i = 0;i < n;i++){
results.AddRange(fragments[i], new GH_Path(i));
}
R = results;
}
Really useful and necessary components. Thank you!









