I’m looking for a way to access data on another component. I searched for an answer, but the the most I could find was a suggestion to store the data inside a file and retrieve it back from the file. Looks like the methods in the component class can’t change from protected access, naturally, so what would be my best option? Let’s say the file route isn’t an option, I’m left with creating a derived class or isn’t this feasible?
So, what I understand from your answer is that, technically I could create a derived class and inherit the data, but it’s advised against.
I find useful sometimes to have something similar to a database that different components could access on the background instead of having to pass through data from component to component. It could be a minor nuissance for me, but when I’m creating components for other people to use it requires them to have comprehensive knowledge of the data pipeline. So, perhaps, this possibility could be a valuable development to the Grasshopper ecosystem.
Regarding my previous edited post, I was able to identify the problem upstream during the creation of the baseplane for the mesh sphere. The error message threw me off, because it did not appear for the Brep sphere. Naturally, only after posting the doubt I remembered to double check. Weird that the behaviour is different for mesh and brep though.
Sure, let’s say I want to associate to each Point3d in a point cloud a bunch of attributes that characterize each point; for instance: point coords, force vector, distance to given object… etc.
I’d like to have different components for different purposes, each would access a different subset of data and carry on with calculation, geometry transformation or whatever needed.
There are many ways to create this custom data structure, but how to pass a selection of data to a specific component without having dedicated outputs? Let’s assume that for this component you would have 10 different outputs and to carry on with a specific processing task you’d require 3 of those, and you repeat the possible combinations for connecting with another 10 components. As you see, if you know what you’re doing, it’ll be more or less ok, but to the casual user this will required a somewhat steep learning curve, a greater chance for mistakes, and more code to account for all possible exceptions.
The standard Grasshopper way is to pass each attribute in a parallel connection: for each point its color, for each point its vector, etc. Grasshopper is meant to work that way. Users know.
There is no “point cloud”, btw, in Grasshopper, however the type exists in RhinoCommon.
If you deem it necessary (do NOT do this for simple types or copies of Rhino types: it will just make working with your library complicated), you can also encapsulate data in one or more classes, then read them from the computation component:
Component A, upstream:
def __init__(self, sphere, message):
self.sphere = sphere
self.message = message
a = [CrystalBall(x, "fortune with cards"), CrystalBall(y, "fortune with algorithms")]
Component B, downstream, single input x (item access):
print("The crystal ball says: " + x.message)
As you can see, there is no need to know the type with Python. All you need to know is the interface (".message"). Later, you can also use Ambassadors classes, as long as they have the .message attribute.
Another simple GHPython solution (i.e. sending arbitrary structured data from one GHPython component to another) is to structure the data in a Python dictionary (i.e. key/value), wrap that in a list/tuple and output that, it’ll pass as an item along the wire, so set the receiving component input parameter to Item Access and No Type Hint and the data will go right through maintaining its structure:
Using iterable containers to pass around single data items is not ideal, because, to fulfill its strategy as visual programming environment, Grasshopper tries to understand iterables as “Grasshopper lists”.
Therefore, just using a simple, empty object ( derived from (object) if you want) and adding an attribute to it will be much more beneficial and future-proof. In OOP, classes are exactly meant to hold structured data.
I would argue that again depends highly on the specific usecase (as we discussed the other day), and is structurally a very similar solution (i.e. a dictionary is also an object in Python world, meant for structuring data). I agree that it should be considered a “hack” to have to wrap a dict in a list/tuple for it to output as an actual dict. But it does, and at least means that one can inspect what it is in the DAG, as opposed to an arbitrary class instance:
But I suppose this all points to slightly more fundamental/conceptual dataflow issues when combining declarative and imperative programming paradigms like this. Which again perhaps aren’t necessarily that large, if one for instance uses GH as essentially a DAG/GUI/IDE for developing almost purely GHPython pipelines (edit: and thus can step outside of the standard DataTree/GH types dataflow/logic). All that said, the empty class solution is really rather elegant I find
Exactly. You are expecting Grasshopper to understand the first list/tuple as it is (an iterable container) and the second one, the dictionary, as a non-iterable container of values. If we were in C, this might be OK, but in Python we have classes and this is how they are meant to be used…
In other words, you could also create a new curve, and attach all these as user attributes to the curve. But it would not be the right container, right?
Not sure I follow. Classes are defined by having both data/variables and methods. Dictionaries/hashtables are defined by data/variables (accessible by keys), and thus appear (certainly to me) as an highly appropriate data structure for interfacing two processes (i.e. if you do not require custom methods).
Or at least that would be the case in GH, if they didn’t simply output the keys as a list (and thus require the wrapping). I understand why this is from a technical POV. But from a logic position this doesn’t seem to be very coherent (i.e. a dict is not a list and thus should not output as one).
And interfaces. If a class implements the IEnumerable<T> interface (with the exception of a few special cased exceptions such as System.String, Rhino.Geometry.Polyline, …) then Grasshopper assumes that the coder wanted to assign a list of values to the output.
Unlike regular components, there’s no way to specify item, list or tree access for script outputs, so some interpretation is needed. This interpreting step (at least in C# and VB components, I don’t know how it’s handled in Python), will unroll collections.
In C# and VB you’d side-step this interpreter by wrapping your collection inside a GH_ObjectWrapper goo instance, which tells GH that the data can be assigned as-is and need not be prettified.
All dictionaries are classes (meant to store lists of key+value pairs). Not all classes are dictionaries. The point of a dictionary is that it is expandable (iterable/enumerable/a collection/a list of keys/unrollable, any similar concept you want…). As such, it makes sense for GH to try to expand it (David calls it “unroll” above). If you define your fixed type and it’s not iterable, then expansion cannot happen.
Then there’s an implementation detail of GH: iterables of iterables (iterables^2) are not expanded (probably because GH already has a DataTree structure meant for that purpose – or maybe because double-checking would be quite expensive for small operations – and I know you are quite cautions about this subject).
So, finally, it’s more adequate to use a custom type that never will be expanded, in general and not to rely on a type that happens not to expand because of that corner case. If you feel like relying on the GH SDK, you can use GH_ObjectWrapper. If you want to use Python types, you can use the method above. You can also let the expansion happen, and then use list access in the next component.
We do not impose methods, really. So if you wish to double-wrap, then I suppose it will be fine, unless the system is changed for GH2 (I don’t think it will anymore in the GH1 era).
I understand why the expansion/unrolling occurs in terms of the technical implementation(s), but as I said, perhaps this doesn’t make too much sense in this specific case in terms of dataflow logics (that is, unrolling a dictionary to just its keys).
This might have gotten a wee bit into the weeds. I mostly do in fact output flat lists or DataTrees, typically wrapping the contained items in the appropriate Grasshopper.Kernel.Types when outputting large lists/trees (for the performance benefits), or using the GHPython wrapping trick of bundling all the data into something that will pass as en item (i.e. nested list, dict, or custom class instance).
Looking forward to updates on how GH2 will manage dataflow/containers/types etc.
Honestly there are no issues here. If anything will happen, it might be that doubly-nested iterables might be expanded to the corresponding DataTree (I am not sure this will happen, though). However, this will not work for the hack.
No, this is exactly Python behavior. Enumerating a dictionary iterable will yield the keys.
I agree the current solution is pretty hamfisted. Ideally output parameters would have type and access constraints, and ideally you could type them directly into the script method signature. Definitely something to look into for GH2.
I’m thinking you could just edit the RunScript signature so that it reads:
private void RunScript(double radius, double steps, out List<int> indices, out object values)
and the code that runs the script would be smart enough to cast the inputs into the dozen or so most common structures and handle all type conversions. So an input could be:
double Just a straight up individual item, non-nullable.
double? A nullable, individual value-type.
double An array representing all the values in an entire tree branch.
double? An array representing all nullable values in an entire tree branch.
List<double> A list of all the values in a branch.
Pair<double> A value+metadata pair for each individual item in a branch.
Pair<double> All the values and their associated metadata in an entire branch.
Branch<double> The branch instance in the tree, no casting/copying required.
Tree<double> The entire tree.
And I’m sure there are lots of other permutations that would make sense. IEnumerable<T> with an actual yielding iterator, ICollection<T>, System.Array…
All of this would make scripts slightly less lenient and shock-absorbing than they are now, but I suspect the benefits will be worth it.