New version of ghpythonlib.components


(Giulio Piacentino) #1

Hi all,

a new version of ghpythonlib, the library performing NodeInCode, is in the latest version of Rhino 6 WIP.

We decided to rewrite the library to address a few requests and performance problems that were discovered over two years of intense usage by this community.

In the new version, on the feature side,

  • ghpythonlib has now support for Grasshopper DataTrees. It is now possible to feed a tree, and get the computed resulting tree as a result, thereby preserving data origins, and fully being able to recreate a Grasshopper definition in code. The new ghpythonlib.tree.function() version of each function does no longer flatten results.

On the performance side,

  • we corrected the behavior for known painful cases from the Grasshopper forum. You can see a series of test on the definitions below here.
  • then, we tweaked the ghcomponentlib.parallel library to use a new algorithm. While it will not make non-parallelizable computations run faster, this new code showed to improve performance in a series of cases.

Some real-world examples

We tested the new library thoughrouly with existing problem cases, and also with normal cases. The first impressions are encouraging:

  • V5 vs V6: 16.9” vs 2.1” (⅛ the original time)
  • Every new repetition was making the component slower. The problem is fixed now, and it’s also a lot faster the first time.

  • V5 vs V6: 4.1” vs 0.376” (1/11)
  • Here the “parallel” (multi-threaded) version of the script was tested. With this setup (just 3 boxes) I could not have the parallel version run faster than the iterative one; however, as the number of breps increases, the parallel version takes a smaller hit.

  • V5 vs V6: 4.2” vs 0.429” (1/10)
  • Here the code of the user runs unchanged, except for a typo he had in his definition.

  • V5 vs V6: 66” vs 3.1” (1/21)
  • It actually got even slower when the solution was recomputed.

  • V5 vs V6: 0.5” vs 0.2” (1/2)
  • Legendary sample by Steve: it runs unchanged, and it is quite faster too. This is mostly, but not only, due to new intersections code in Rhino

GHPython Slightly Slower in V6

Great news!!

On both subsequent slowdown fix and data trees!!

ghpythonlib has now support for Grasshopper DataTrees.
It is now possible to feed a tree, and get the computed resulting tree
as a result, thereby preserving data origins, and fully being able to
recreate a Grasshopper definition in code. The new _tree() version of each function does no longer flatten results.

This option will be available when third argument in the function is set to False?

(Giulio Piacentino) #3

Parallel is unaware of what you are doing inside it, so you could use the _tree() version of a function. But I would not mix trees and parallel that way, just because merging the resulting trees would become painful. Unless you only have flattened lists as results (last bool parameter), then you could re-create a tree afterwards with some thinking.

On the other hand, I think this will be very useful if you are re-creating a Grasshopper definition that has trees, and want to write it with ghpythonlib. In this case, the _tree() version will allow you to follow the same, original logic.


Can we change the template for the editor? ‘import Rhino.Geometry as rg’ would be very nice to have e.g.

(Andrew Heumann) #5

HOORAY!!! Amazing work. Can’t wait to try it out!

(Giulio Piacentino) #6

Hi Marcus @MarcusStrube

do you mean to ask if the editor would load the default template from a file on disk?
That could be a good idea.


Giulio Piacentino
for Robert McNeel & Associates


If I could change some template I would see something like this when opening a new Python component:

#import ghpythonlib.components as ghcomp
#import Grasshopper as gh
#import Rhino
import Rhino.Geometry as rg
#import rhinoscriptsyntax as rs
#import scriptcontext as sc


Hi Giulio,

I just got the WIP running today. Looks really great!! The first thing I noticed was the GHPython template as well.

+1 on enabling us to edit this ourselves :slight_smile:

Maybe add it here:

C:\Users\USERNAME\AppData\Roaming\McNeel\Rhinoceros\6.0\Plug-ins\IronPython (814d908a-e25c-493d-97e9-ee3861957f49)\settings




Hi Giulio,

Agreed with @MarcusStrube, it would be great to change the template of the editor.
I made the same request here:




Hi Giulio (@piac),

in WIP there is just

when we change manually. (Which then takes 3 seconds the first time being called…)

And I guess componentbase was misspelled?? (Was ‘compiledbase’)


Wouldn’t it be nicer to have sth. like ghcomp.Function(x, y, tree=True/False) instead of ghcomp.Function(x, y) and ghcomp.Function_tree(x, y)?


I would want this too.
Is there a specific reason why we have two types of ghcomp functions? Can they be narrowed down to one function?

(Giulio Piacentino) #13

Thanks for your input, @MarcusStrube @djordje and @PaulPoinet.

The answer is, “yes, there was”. I am still in doubt about this, and I think we can modify the code to handle an extra tree parameter. The first issue is that the only “safe” way will be to always have a tree=True argument in all calls, which is longer than _tree(). Also, the fact that a function behaves quite differently in terms of returned types with a simple Boolean change is a little strange, but understandable.

The second issue is slightly more technical; Grasshopper itself can have any input marked as ‘optional’ (not only the last ones). Optional inputs can appear in any order, not necessarily be placed at the end of the input list.

To maintain constant behavior with the previous version, not assigning a non-optional parameter only produces a warning, and in its place the function uses the default value. E.g., as a result of this, the ‘Pattern’ in the ‘CullPattern’ component is [False,False,True,True] even without mentioning it. The two things together make adding a new optional input slightly confusing.

If we do, CullPattern([1,2,3,4]) will return [3,4], but CullPattern([1,2,3,4], True) will not return the tree representation, but [1,2,3,4], for example. Only CullPattern([1,2,3,4], [False,False,True,True], True), or CullPattern([1,2,3,4], tree=True) will return the tree.

These are the reasons, but the fact that many of you would prefer otherwise is making me rethink this. I’ll work on this in the next days and see if I can make it work smoothly with the additional parameter.


Hey Giulio (@piac),

I somehow feel like I want to tell you what I think. (Hope I don’t sound offensive…)

In my opinion someone using Ghpython in Rhino V5 cannot (and shouldn’t) expect constant behaviour. is more alpha than beta.

Since Grasshopper components return trees as long as the user doesn’t flatten the output, ghcomp.CullPattern() therefore should, too. So in order to behave properly in Rhino V6 it should be ghcomp.CullPattern_flattened() anyway. And that would be longer. Also, if the longer thing is a good reason to make something really ugly, why not just call it CullPattern([1,2,3,4], t=1)? You hate PEP8 anyway! :wink:

Is this perhaps an option?

  • ghcomp.flatten.CullPattern([1,2,3,4])
  • ghcomp.CullPattern([1,2,3,4])

(Giulio Piacentino) #15

Don’t worry, you don’t sound offensive. I did not write the first implementation, and I actually think that the first implementation had its benefits.

Not really. Not if you don’t want to debug and teach tree programming all the times, which is painful, is error prone and has a steep learning curve. If we convert all to arrays behind the scene, then GhPython will be slower, so we don’t want to do that by default either! We are doing some gray magic here, so we need to be careful :slight_smile:

I (or we?) don’t hate PEP8! Again, capitalization is kept consistent with the previous version. There are already a bunch of scripts out there we do not want to break just because of this. t=1 seems a great way to make code unreadable, so this will definitely not be.

Why not! However, to keep it simple for new users, we would need to have it the other way:



Hi Giulio and Marcus.

After reading your discussion I think it would actually make more sense to have only a tree output (or ultimately a nested list) instead of having to choose between two options - if this can make life easier for everyone. The main reason has been mentioned above: it would behave exactly like grasshopper. Of course triggering a boolean would be great in some cases, but it seems to me that the user should deal with it and flatten the list by him/herself if needed.

Or, why not being able to choose between tree and nested list instead of tree and flatten list?



Recently I wrote this example, reversing every second branch without having to rebuild the tree.
[Data.Branches[i].Reverse() for i in xrange(1, Data.Branches.Count, 2)]

Here I think, following would have been nicer, also for new users, because it is all Python and no .Net stuff:
[reversed(Data[i]) for i in xrange(1, len(Data), 2)] Data = ghpythonlib.treehelpers.list_to_tree(Data)

So, having a ‘Nested Python List Access’ in the GhPython component’s context menu I would like very much, too.

Inside the component, when calling ghcomp, now that ghcomp is no longer slow, I don’t understand why you are willing to flatten nested lists using Python, which is very expensive, but building yor own nested list is not good enough?

points = [ghcomp.ClosestPoints(point, Pt2, 3)[0] for point in Pt1] # No longer slow

(Giulio Piacentino) #18

At present, this is flattening every branch result first, getting the first output ([0]), then creating a list of lists. Pt1 cannot be itself a tree to begin with.

In any case, Thank you! I think I got a good amount of feedback, now it’s time to try testing how well these options can be implemented. It might take a little time to have them ironed out, but I hope they can make it for V6SR0 with a few tests.

Besides, it’s important to remember the goal of ghpythonlib: to help new scripters who know Grasshopper use their Python skills, as well as access Grasshopper-only functionality (like, say, Delaunay).


Thank you for the detailed explanation Giulio.
I guess I understand now the concerns that this approach may bring.

(Giulio Piacentino) #20

Just a quick heads-up: now all tree-supporting functions are in a tree holder, inside the ghpythonlib.components module.

@djordje @MarcusStrube @PaulPoinet