Skip to content

Instantly share code, notes, and snippets.

@piac
Last active October 31, 2022 20:01
Show Gist options
  • Save piac/ef91ac83cb5ee92a1294 to your computer and use it in GitHub Desktop.
Save piac/ef91ac83cb5ee92a1294 to your computer and use it in GitHub Desktop.
Transforms DataTrees in Grasshopper to nestings of lists, and vice versa
def list_to_tree(input, none_and_holes=True, source=[0]):
"""Transforms nestings of lists or tuples to a Grasshopper DataTree"""
from Grasshopper import DataTree as Tree
from Grasshopper.Kernel.Data import GH_Path as Path
from System import Array
def proc(input,tree,track):
path = Path(Array[int](track))
if len(input) == 0 and none_and_holes: tree.EnsurePath(path); return
for i,item in enumerate(input):
if hasattr(item, '__iter__'): #if list or tuple
track.append(i); proc(item,tree,track); track.pop()
else:
if none_and_holes: tree.Insert(item,path,i)
elif item is not None: tree.Add(item,path)
if input is not None: t=Tree[object]();proc(input,t,source[:]);return t
# written by Giulio Piacentino, giulio@mcneel.com
def tree_to_list(input, retrieve_base = lambda x: x[0]):
"""Returns a list representation of a Grasshopper DataTree"""
def extend_at(path, index, simple_input, rest_list):
target = path[index]
if len(rest_list) <= target: rest_list.extend([None]*(target-len(rest_list)+1))
if index == path.Length - 1:
rest_list[target] = list(simple_input)
else:
if rest_list[target] is None: rest_list[target] = []
extend_at(path, index+1, simple_input, rest_list[target])
all = []
for i in range(input.BranchCount):
path = input.Path(i)
extend_at(path, 0, input.Branch(path), all)
return retrieve_base(all)
@cromlyngames
Copy link

Minor bug, using the list_to_tree.py above in rhino 6 R30 and R34 I get the same 'error' message on opening the file. The component works fine, but the opening error message is offputting for other people coming to the script.

image

@piac
Copy link
Author

piac commented May 3, 2021

@cromlyngames what type are you passing to the script? Can you email me a sample?
Thank you! giulio@mcneel.com

@piac
Copy link
Author

piac commented May 31, 2021

What can we do when the "tree" has been simplified and represents a forest?
(for example, it contains {0,1} and also {3,1}, which do not have a "trunk", or first index, in common).

You can use retrieve_base and source like this:

import ghpythonlib.treehelpers as TH
nested = TH.tree_to_list(data, retrieve_base=lambda x: x)
a = TH.list_to_tree(nested, False, source=[])

Background

The key here is to notice that Grasshopper will never create forests, when it does normal tree handling. Forests are "trees" that have distinct trunks, or first branch indices. That's why, the default of the functions does not create an empty list to hold these distinct trunks, -- otherwise, with standard components, there would be one more [0] to deal with all the time.

Please also note that trees that have overlapping branches, that is, a branch with a path that is equal to the beginning of the path to another branch, cannot be rendered as instances of this type of lists of lists. Just keeping paths to the same depth will fix this problem.

@yjl-add
Copy link

yjl-add commented Sep 9, 2021

For total beginners who is having trouble with replicating the script as I had,
You need to set "Tree Access" type hint for the input.

Screenshot 2021-09-09 022003

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment