Does node-in-code work in Rhino 8 - Cpython 3?

Hi folks, I have asked this in the past, but can anyone from Mcneel tell me if node-incode is intended to work in cpython 3 scripts in Rhino 8?

For example in the attached if I call ghcomp.Move() on some geometry the return values are just strings.


240624 python 3 node in code.gh (12.0 KB)

Does it work in an IronPython 2 component, or an old GHPython one? I’ve found node-in-code was a bit hit and miss even before Python 3 components.

If you’re happy to actually place a Move component on the canvas, I think I have a way to run it ‘remotely’ from Python code in a different component.

I have never had problems with node-in-code before in ironpython 2.7. I haven’t verified in Rhino 8 ironpython 2, will do that now.

I have used a wide variety of ghcomp components references in a previous project which I’d like to update to Rhino 8.

Still, interested to hear your methods

240624 python 3 node in code and Ipython2.gh (19.8 KB)
just for comparison

1 Like

Grand. I’ve got this working for my own purposes (automated testing of a plug-in), and have pacakged it, and put it on PyPi as Cheetah_GH, but make no promises how robust it is yet.

Basically, if you’ve already got a component placed, you can find all the components on the canvas using:

GH_DOC = ghdoc.Component.Attributes.DocObject.OnPingDocument()


GH_DOC_COMPONENTS = {component.NickName : component
            for component in doc.Objects
           }

Place Domain, Random and (Colour) Gradient components manually to test this.
References to them can be found via their visible names, if you turn off view icons (their .NickName attributes), e.g.:

    GHRandomComponent = GH_DOC_COMPONENTS['Random']
    GHGradientComponent = GH_DOC_COMPONENTS['Gradient']
    GHDomainComponent = GH_DOC_COMPONENTS['Dom']

These can be run via something like:


    domain_retvals = run_comp(GHDomainComponent, A=L0, B=L1)   
    random_retvals = run_comp(GHRandomComponent, R = domain_retvals['I'], N=N, S = random_int(0, 250000))
    gradient_retvals = run_comp(GHGradientComponent, L0=L0, L1=L1, t = random_retvals['nums'])

where I defined run_comp as:


def run_comp(comp, **kwargs):
    
    comp.ClearData()
    #    comp.ExpireSolution(False)
    for param in comp.Params.Input:
        if param.NickName in kwargs:
            set_data_on(param, kwargs[param.NickName])
    comp.CollectData()
    comp.ComputeData()

    return {param.NickName : get_data_from(param) 
            for param in comp.Params.Output
           }
def set_data_on(param, val, type_ = str):
    param.ClearData()    
    # "and isinstance(val, Grasshopper.DataTree[object])""
    # mimicks Grasshopper support for passing a list into a Tree access param
    if (param.Access == Grasshopper.Kernel.GH_ParamAccess.tree
        and isinstance(val, Grasshopper.DataTree[object])):
        #
        gh_struct = DataTree_to_GH_Struct(val, type_)
        param.AddVolatileDataTree(gh_struct)
    elif isinstance(val, (list, tuple)):
        for i, item in enumerate(val):
            param.AddVolatileData(Grasshopper.Kernel.Data.GH_Path(0), i, item)
    else: 
        param.AddVolatileData(Grasshopper.Kernel.Data.GH_Path(0), 0, val)

and


def get_data_from(param):


    if param.Access == Grasshopper.Kernel.GH_ParamAccess.tree:
        # return param.VolatileData

        return GH_Struct_to_DataTree(param.VolatileData)

    
    data_tree = Grasshopper.DataTree[object](param.VolatileData)
    
    if data_tree.DataCount >= 1: # Alternative: param.VolatileDataCount >= 1 
        return [convert_GH_type_to_Python_type(x) for x in data_tree.AllData()]
    else:
        branch = data_tree.Branch(0)
        return branch[0] if branch else None

    

    raise NotImplementedError('Unsupported Param.Access value %s. '
                             +'Supported: item, list and tree. '
                             % param.Access
                             )

It was fiddly, but I did get this to work for input params in Item, List and Tree mode.

There are two more functions called from there, so it’s probably easiest just refer to, or import the library:

wow, quite a workaround! Will give it a go when I get the chance. Could be a good get out of jail for my legacy work

You’ll likely have a simpler/better/faster time if you skip the middlemen (i.e. implement RhinoCommon directly):


240624_RhinoCommon_CPython_GHPython.gh (12.1 KB)

yes, I appreciate that, I was really just using move as an example to try and understand from the mcneel team if node in code in principal is “meant” to work in Rhino 8 cpython.

In my legacy project I made a lot of use of certain components that are relatively difficult to implement in rhino common. For example curve region booleans, brep booleans, kangaroo mesh unroll, and quite a few others that I found were either more time consuming to implement or like the curve booleans had more unreliable outcomes.

1 Like

Cheers :). It was a lot of work, but I really wanted to minimise manual testing in Grasshopper (and still do).