I’ve been stuck on a problem of combining functions in different order in an elegant and working way. I could not to find anything on this forum resembling what I want to achieve, sorry in advande if I missed it. I should mention that I’m working with Rhino 7/Grasshopper 1.0 and that I cannot afford to use any third party plugin like anemone or hoopsnake (I stumbled upon these references while looking for solution), and I’m not fluent in C#.
The problem:
Let’s say we have a brep onto which we wish to perform several transforms. These transforms are not commutative so order of execution matters. We want to choose which transform are to be performed and in which order.
In my case, I have 5 transforms (for now, could be more in the future) that amount to 325 permutations so copying functions and manually link them all is out of question.
My first thought was to create a DataTree with the same data in paths that represent the transforms and the order of transforms. And then use streams gates/filter based on these indices to route the data. But working like this gets you a “Recursive data stream” error.
Another approach was feeding brep parameters via a python script to avoid a “Recursive data stream” while routing the data. But the code breaks the solution even using the GH_Document.ScheduleSolution method. The code is a fork adapted to brep from this thread: The only safe way to Update Sliders in GhPython - #26 by piac. The trigger (next_dispatch) is a boolean stating if there’s another function after the current trasnform function.
import Grasshopper
import System
import Rhino
class UpdateBrep():
def __init__(self, Brep_comp, Brep_data):
if not Brep_comp or not Brep_data:
Brep_comp = None
Brep_data = None
return
self.Brep_comp = Brep_comp
self.Brep_data = Grasshopper.Kernel.Types.GH_Brep(Brep_data)
self.Component = ghenv.Component
self.GhDef = None
if self.Component: self.GhDef = self.Component.OnPingDocument()
def Update(self):
if not self.GhDef: return
if not self.Brep_comp or not self.Brep_data: return
ghObjects = self.GhDef.Objects # Get to the GH objects
# Iterate the GH objects
for obj in ghObjects:
if type(obj) is Grasshopper.Kernel.Parameters.Param_Brep and \
obj.NickName == self.Brep_comp:
# Set value
self.GhDef.ScheduleSolution(500, obj.SetPersistentData(self.Brep_data))
obj.ExpireSolution(True)
if __name__ == "__main__":
update_brep = UpdateBrep(Brep_comp, Brep_data)
if next_dispatch is not None:
try:
update_brep.Update()
except Exception, ex:
System.Windows.Forms.MessageBox.Show(str(ex))
A working but fragile approach is to bake the result of functions in a specific layer in the Rhino.Doc.ActiveDoc via a python script and then retrieve it via another python script, but the baking/retrieving part bothers me a bit regarding how the solution is computed in GH (will it take the right data at the right time?).
I’ve tried other variants of the above to no avail. I’m open to any suggestion.
Avoid recursion.
And if you know what you are doing, avoid iterative method.
Don’t move around your breps, instead select and sort your transformations and combine them into a single transformation, lastly apply that transformation to your brep.
Please , further explain this ^
I think you can solve this with vanilla GH and no c#/python at all.
Please provide a working minimal example of a brep being transformed as you need.
325 permutations? Please show , say, 5 permutations applied to the same brep, so we can properly understand what are you saying.
I’m not understanding your problem, at the moment.
With “Cross Reference” you might set up all possible “permutations” in seconds, create the compound transformation and apply it to your brep and easily “explore” them one by one…
But 5! is not equal to 325, so I have no idea where that comes from…
A permutation is like a combination of elements of a set, but where order matters (Permutation - Wikipedia).
For k-length permutations of n, the formula is \frac{n!}{(n-k)!}, so if you account for every length up to n, you have \displaystyle\sum_{k=1}^{n}\frac{n!}{(n-k)!}.
For n=3,4,5, you get 15, 64, 325 for instance.
I understand your point, compounding the transforms and then applying it to the brep, but the transforms here are not linear transforms in the mathematical sense that can be expressed as matrices but rather clusters of components.
I wouldn’t know how to combine them before applying to the data, and this is why I chose to route the data rather than the transforms.
I’ve provided a stripped example of what I want to do in the first post to show the main idea but I can’t provide a full example.
Just to be clear: are we talking of actual transformations? (that at least could be defined by the 4x4 matrix) Or you are doing also stuff like solid booleans?
In your “stripped example” you used simple transformations.
Are your trasformations being calculated from other geometries, so the current position of your brep would generate a new different, unique, transformation?
In that case you could inverse-transform those geometries to calculate the transformation in the starting position of your brep…
Sorry if I’ve been misleading by malaproprism, here the transforms are clusters of components that can’t be expressed as actual 4x4 transformations. For instance, one such transformation cuts a brep in the middle, insert a new piece to make the transformed brep.
I used actual transforms in my example for simplicity’s sake, it is not meant to be taken as is. In fact, the cluster encapsulates complex transformations.
I’m not sure what you meant, but no other geometries are involved in the transformations.
But as I’ve said, these transformations are not linear so I wouldn’t know how to calculate them as 4x4 matrices.
This script will chain-connect some clusters in the order you give them in the input “sequence”.
Note that clusters have one extra useless input for the c# to connect to.
“sequence” should end with last element being always the same, the last cluster, so your data can properly flow in your definition.
If you apply the formula \displaystyle\sum_{k=1}^{n}\frac{n!}{(n-k)!} with n = 5, you get: \frac{5!}{4!} + \frac{5!}{3!} + \frac{5!}{2!} + \frac{5!}{1!} + \frac{5!}{0!} \\ = 5 + 20 + 60 + 120 + 120 = 325
@maje90, I have to tweak your code and I’m running into what seems to be a trivial problem, but since I’m not familiar with C#, maybe you can help me.
I need to segregate inputs and outputs (or dispatchs here), i.e. I’m not connecting clusters directly but rather Param_Brep happening before and after the clusters. I want to identify them by their unique NickName which I set beforehand (the string lists at the start of the code).
The following code says you can’t cast from ‘Grasshopper.Kernel.IGH_DocumentObject’ in ‘Grasshopper.Kernel.Parameters.Param_Brep’. How can I be solve this?
Grasshopper.Kernel.Parameters.Param_Brep start = (Grasshopper.Kernel.Parameters.Param_Brep) this.Component.Params.Input[0].Sources[0].Attributes.GetTopLevel.DocObject;
// Initialisation des listes de params
List<string> ParamsInput = new List<string>();
List<string> ParamsDispatch = new List<string>();
string InputSuffix = "_input";
string DispatchSuffix = "_disptach";
foreach(string s in Params) {
ParamsInput.Add(string.Format("{0}{1}", s, InputSuffix));
ParamsDispatch.Add(string.Format("{0}{1}", s, DispatchSuffix));
}
// Recherche des params dans le document
List<Grasshopper.Kernel.Parameters.Param_Brep> ParamsInputObjects = new List<Grasshopper.Kernel.Parameters.Param_Brep>();
List<Grasshopper.Kernel.Parameters.Param_Brep> ParamsDispatchObjects = new List<Grasshopper.Kernel.Parameters.Param_Brep>();
IList<Grasshopper.Kernel.IGH_DocumentObject> DocObjects = doc.Objects;
List<string> DocObjectsNicknames = new List<string>();
foreach(Grasshopper.Kernel.IGH_DocumentObject obj in DocObjects) {
DocObjectsNicknames.Add(obj.NickName);
}
foreach(string s in ParamsInput) {
for(int i; i < DocObjectsNicknames.Count; i++) {
if (s == DocObjectsNicknames[i]) {
ParamsInputObjects.Add(DocObjects[i]);
}
}
}
foreach(string s in ParamsDispatch) {
for(int i; i < DocObjectsNicknames.Count; i++) {
if (s == DocObjectsNicknames[i]) {
ParamsDispatchObjects.Add(DocObjects[i]);
}
}
}
Is the error from the first row of the code you posted?
If I do this:
private void RunScript(object x, object y, ref object A)
{
Grasshopper.Kernel.Parameters.Param_Brep start = (Grasshopper.Kernel.Parameters.Param_Brep) this.Component.Params.Input[0].Sources[0].Attributes.GetTopLevel.DocObject;
}
It doesn’t give me any error.
Please attach your .gh file (or via PM), or at least a screenshot of the error… very hard to guess where is the problem by looking only at a piece of code…
I cannot attach the .gh file but I can elaborate on the problem.
This is more a GH data type kind of problem.
I want to populate a list of Param_Brep based on their nickname.
// Param_Brep list instantiation
List<Grasshopper.Kernel.Parameters.Param_Brep> ParamsInputObjects = new List<Grasshopper.Kernel.Parameters.Param_Brep>();
// Collecting all objects from the doc
IList<Grasshopper.Kernel.IGH_DocumentObject> DocObjects = doc.Objects;
// Creating the list of nicknames
List<string> DocObjectsNicknames = new List<string>();
foreach(Grasshopper.Kernel.IGH_DocumentObject obj in DocObjects) {
DocObjectsNicknames.Add(obj.NickName);
}
// Populating the Param_Brep list based on their nickname
foreach(string s in ParamsInput) {
for(int i; i < DocObjectsNicknames.Count; i++) {
if (s == DocObjectsNicknames[i]) {
ParamsInputObjects.Add(DocObjects[i]);
}
}
}
This is on this last bit that I get an “impossible cast” error. I thought that there would be an implicit cast from Grasshopper.Kernel.IGH_DocumentObject to Grasshopper.Kernel.Parameters.Param_Brep. I don’t see how to do the type conversion.
I wasn’t able to make the C# script work so I did it in python. I eventually understood how to use the callback in the ScheduleSolution method.
Here’s the code and GH file.
import Grasshopper
def SolutionCallback(doc):
n = len(sequence)
# Identification du brep de référence
start = ghenv.Component.Params.Input[0].Sources[0].Attributes.GetTopLevel.DocObject
# Initialisation des listes de params
InputSuffix = "_input"
DispatchSuffix = "_dispatch"
ParamsInput = [s + InputSuffix for s in params_list]
ParamsDispatch = [s + DispatchSuffix for s in params_list]
# Recherche des params dans le document
ParamsInputObjects, ParamsDispatchObjects = [], []
DocObjects = doc.Objects
DocParamBreps = [obj for obj in DocObjects if type(obj) == Grasshopper.Kernel.Parameters.Param_Brep]
DocParamBrepsNicknames = [obj.NickName for obj in DocParamBreps]
obj_count = len(DocParamBrepsNicknames)
for s in ParamsInput:
for i in range(obj_count):
if s == DocParamBrepsNicknames[i]:
ParamsInputObjects.append(DocParamBreps[i])
for s in ParamsDispatch:
for i in range(obj_count):
if s == DocParamBrepsNicknames[i]:
ParamsDispatchObjects.append(DocParamBreps[i])
for comp in ParamsInputObjects:
comp.RemoveAllSources()
# Routage des données
for i in range(n):
if i == 0:
ParamsInputObjects[sequence[i]].AddSource(start)
else:
ParamsInputObjects[sequence[i]].AddSource(ParamsDispatchObjects[sequence[i-1]])
if __name__ == "__main__":
doc = ghenv.Component.OnPingDocument()
try:
doc.ScheduleSolution(10, SolutionCallback)
except Exception, ex:
System.Windows.Forms.MessageBox.Show(str(ex))