Filter content by "Path"

What is meant by “path” when using the content filter added in RH8?
I thought it would be path in data tree, but the value returns “null” when queried via python.

It is an inherited attribute of the ModelContent class of Grasshopper’s SDK, but it is undocumented. In reality, it’s wrapping an instance of ModelContent.Attributes and referencing it’s Path property, which is used when nesting ModelObjects (something I’ve never done before and had no idea you could do, but there it is). If you’re trying to get the path of the data tree, you need to set the type hint to allow tree access.

I had a need to filter a datatree and this looked like an elegant way to do that. Normaly I use “treestats” and text filter. Maybe I could create a component of my own to do that. I wanted to get into plugin creation for some time.

It’s a very common use case for the scriptable components in my experience, branch management can be a nightmare on more complex models - the components are also far more approachable than plugin development in an IDE, but some plugins like Ladybug run almost entirely in script components, so it’s a great start.

For learning, I recommend plugging objects into the script str(type(x)) to get an idea of the object’s type, then look that up on the Rhinocommon API or Grasshopper SDK to get an idea of the object’s features. I also lightly recommend getting comfortable with C#, as it being strongly typed exposes a lot of this in a more readable way. It’s a little less accessible than Python, but if you have experience with Java it is not hard to adapt to.

I use Python mostly but I’m slightly familiar with C#. I’ve used it to automate some tasks in NX (CAD SW from Siemens) before offloading that work to AI to save time and sanity since their API is quite byzantine.

In regards to “model object nesting”: Should I imagine that as building more complex objects from simple ones? For example a “Coordinate system” object made up from axe and plane objects?

No, ModelObjects represent geometry in Rhino’s workspace. I wasn’t entirely sure what was meant by Path for the ModelObject, but after digging around it’s more related to block definitions in Rhino in that blocks can contain child blocks and so forth. According to the API:

Represents a Rhino model object. Wraps the functionality of the RhinoObject type.

Itself referring to this, which are objects you would typically find in a Rhino Doc’s Objects collection - this has caused confusion for me in the past. To get the path value to read anything, I set this up:

ch_modelobject_paths.gh (11.2 KB)

You might be able to construct a class in a script component and pass that around. If you’re messing with DataTrees, I again recommend changing the parameter type to allow for tree access (currently the script is parsing each item individually and not as a collection).

That’s interesting but is it useful for anything? In your example it seems that Path just accesses BlockDefinition name. Can you get nested block structure from Path? I imagine reading it in such a “text based” form could be faster than using the ModelBlockDefinition object in reverse. Especially in large models.

In some cases I use nested blocks to represent assemblies and then build BOMs from ModelBlockInstances. Usually I get all model instances grouped by instance definition name. Then I can get one instance from each branch and get its objects via ModelBlockDefinition. It works fine for one nested layer but is difficult to extend.

As to my original post, the python was there just to show that Path reads null. And I know about tree access and treehelpers to transfer gh trees into python nested lists. But thank you for the tip anyway.

After some checking, it seems like the Path property is mostly used by ModelLayers. The path itself is of the type ModelContentName, which seems to just wrap strings.

For blocks, it looks like Grasshopper’s representation is not quite up to par with RhinoCommon, so some custom code sounds necessary. If these blocks are built in Grasshopper, you may be out of luck - if they’re queried from the RhinoDoc, you’re fine. To convert queried ModelBlock definitions into RhinoCommon blocks, you can use this code:

import Rhino

doc = Rhino.RhinoDoc.ActiveDoc
model_id = x.Id
obj = doc.InstanceDefinitions.Find(model_id, False) # InstanceDefinition object

From here you can use any of the methods found here and navigate the hierarchy using GetObjects(). Since blocks can contain both geometry and other blocks, the result may include block instances in which you will need to query their definitions. Matching GUIDs or names is probably the way to go.