Pickling Rhino Geometry Objects Fails In Python 3 (Works in IronPython)

I have created my own classes, that contain Rhino geometries as attributes (mostly points, lines, NurbsCurves). I then perform many operations such as offsets, trims and joins. Objects rely on previous objects geometries and so on. On runtime there can be several hundred objects and the associated points, lines, curves, joined polycurves, etc.

It was really nice to be able to just pickle my python class objects to a temp pickle-file on success and/or failure and then retrieve the pickle and look at the attributes/geometries in the objects to look what went right/wrong.
I could analyze all the object attributes with an interactive python grasshopper component and look at complex geometries and their interaction.

Now I am converting the project from Rhino v7 / IronPython to Rhino v8 / python3 and struggle with the python pickling of objects and geometries.

In Rhino v8 the python code refuses to pickle something as basic as a Point3d with the error: cannot pickle ‘Point3d’ object.

While the same works in an IronPython component. Will this be fixed? Do I need to modify the pickle process? What would be another solution for this?

Example below works in IronPython in v8 but fails in Python3 component:

# import pickle in python 3
import cPickle as pickle
import rhinoscriptsyntax as rs
import os
from Rhino.Geometry import Point3d
# create point object
pt = Point3d(0,0,0)

# pickle point to temp file in user folder
temp_file = os.path.join(os.path.expanduser('~'), 'test_pickle.pickle')
with open(temp_file, 'w') as f:
    pickle.dump(pt,f)

# retrieve point from pickle
with open(temp_file, 'r') as f :
    pt = pickle.load(f)
    print(type(pt))

Switching from pickling to file to using the scriptcontext.sticky seems to work with no problems, but I do not know about the speed / robustness of sc.sticky operations.

import scriptcontext as sc
from Rhino.Geometry import Point3d, LineCurve
# create custom class with Rhino.Geometry objects inside
class MyClass:
    def __init__(self):
        self.pt1 = Point3d(0,500,0)
        self.pt2 = Point3d(500,0,0)
        self.line = LineCurve(self.pt1, self.pt2)
        
    def save_debug(self):
        # save full object to sticky dict
        sc.sticky['debug'] = self

my_class = MyClass()
my_class.save_debug()

I can save the full object and then retrieve and access the object attributes from another script via:

import scriptcontext as sc
from Rhino.Geometry import Point3d

my_class = sc.sticky['debug']
pt1 = my_class.pt1
print(pt1)
line = my_class.line
print(line.GetLength())

Python’s great and everything, and pickle is occasionally useful.

But if you’re writing code for Grasshopper within Rhino anyway, why don’t you let Rhino handle the saving of Rhino Geometry objects itself, in any file format Rhino supports?

There’s the opennurbs spec and library too, if you need cross platform support.

@cpbln

pickle-ing Rhino geoemtry types is not yet supported in Python 3. I have a ticket here to get this implemented

RH-77321 Pickle does not work for Rhino.Geometry.Point3d (and potentially all other types)

The point is that I have my custom python objects with mixed data types:

  • Rhino geometry: curves, lines, points, etc.
  • Strings: object names, layer names
  • floats/ints: thicknesses, lengths, …

I would lose all that information saving it to 3dm.

In Rhino v7 with IronPython I could just run my programm and if I have an exception on some join / trim / intersection operation just write the object to a pickle. And then I could retreve the object and figure out what was the problem. Sometimes user input, sometimes tiny gaps between curves.

scriptcontext.sticky seems to do the trick so far. I do not need to store it for a long time, just for debug purposes on runtime.

Thanks for the info Ehsan.

1 Like

Your workflow works for you, so it’s probably not worth changing it.

But for future reference there are straightforward ways of associating strings, floats and ints with Rhino Geometry that can be saved in a .3dm file, and reconstructed. E.g. Usertext.