Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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)
@piac

This comment has been minimized.

@piac

This comment has been minimized.

Copy link
Owner Author

@piac piac commented Feb 16, 2015

This is an extension of a sample by Benjamin Golder posted on the GH forum. The original sample worked on a single list with lists inside (2D); this sample works on arbitrarily nested lists of lists (nD).

@piac

This comment has been minimized.

Copy link
Owner Author

@piac piac commented Jun 22, 2017

@epignatelli

Hi, do you see those "source"/"retrieve_base " parameters in the two functions? They refer exactly to aberrant trees that actually represent "forests". (two tree that have {1} and {2} paths actually are not one "tree", but two different "trees")

@piac

This comment has been minimized.

Copy link
Owner Author

@piac piac commented Jun 22, 2017

Also, in Rhino WIP and Rhino 6, it will be possible to use

import ghpythonlib.treehelpers as th

a_tree = th.list_to_tree(a_list)
a_list = th.tree_to_list(a_tree)

that reference internal and maintained versions of the script above.

@xarthurx

This comment has been minimized.

Copy link

@xarthurx xarthurx commented Mar 16, 2019

This function doesn't work when the tree index doesn't start on 0.
image

@laurend

This comment has been minimized.

Copy link

@laurend laurend commented Feb 25, 2020

I didn't realize this at first so I want to mention in case it helps others: it seems that in order for tree_to_list to work properly (the version included with Rhino 6) the incoming tree must have the same dimensions as your target list. For instance, I had a list with 9 branches {0}...{8} and 40 items in each branch. I had to use path mapper to remap the incoming paths as {0;0}...{0;8} to output a 9x40 list of lists.

@piac

This comment has been minimized.

Copy link
Owner Author

@piac piac commented Feb 25, 2020

@laurend I think this topic:
https://discourse.mcneel.com/t/treehelpers-with-simplify/92531/2
will explain more in detail for you.

@laurend

This comment has been minimized.

Copy link

@laurend laurend commented Feb 25, 2020

@piac yes, I wish I had found that earlier!

@cromlyngames

This comment has been minimized.

Copy link

@cromlyngames cromlyngames commented Apr 1, 2021

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

This comment has been minimized.

Copy link
Owner Author

@piac 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

This comment has been minimized.

Copy link
Owner Author

@piac 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

This comment has been minimized.

Copy link

@yjl-add 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