What happens to Custom Objects on Save?

Hello i have another question regarding Custom Objects.

When a 3dm file containing custom objects gets saved, all of them get converted in some way (CustomBrepObject becomes BrepObject, CustomCurveObject becomes CurveObject etc.) Can someone explain how this process is happening and what can be changed about it?

For example in my current case i have UserData attached to my CustomObjects and while this userdata survives saving and reopening on “normal” objects, the custom object gets stripped of the userdata. I am following this example which works up to the point where custom objects come into play.

Maybe there is something to override or an event to subscribe to, so i can attach my userdata to those automatic replacement objects and get it saved in the doc.

Thanks for any help

Hi @lando.schumpich,

Custom objects are saved as their inherited types. For example, a CustomCurveObject is saved as a CurveObject. Any extra data on your derived object should be saved as user data.

When Rhino opens a document, you should watch for objects with your user data and convert them back to your custom object.

– Dale

Hey,

I finally got it to work, the problem was i was adding my UserData to the customObject itself when i should have been doing something like:

this.Geometry.UserData.Add(MyCustomUserData);

Subscribing to RhinoDoc.EndOpenDocument now gives me the ability to re-create all my custom objects after saving and reopening.

Thanks a lot!

Hi @lando.schumpich,

Would it be possible for you to provide one quick example demonstrating how to re-create the custom objects after saving and reopening?
Thanks in advance! :slight_smile:

Hi @jeffoulet,

For re-creation i typically do this inside of the plugIn class:

  1. Subscribe to the EndOpenDocument event
        protected override LoadReturnCode OnLoad(ref string errorMessage)
        {
            // Add an event handler so we know when documents are opened
            RhinoDoc.EndOpenDocument += RhinoDoc_EndOpenDocument;

            return LoadReturnCode.Success;
        }
  1. Iterate over all objects in the doc and search for your user data, re-create if found.
        private void RhinoDoc_EndOpenDocument(object sender, DocumentOpenEventArgs e)
        {
            RhinoDoc doc = e.Document;

            foreach (var docObject in doc.Objects)
            {
                // try to find our user data on a given doc object
                var data = docObject.Attributes.UserData.Find(typeof(MyCustomUserData)) as MyCustomUserData;
                if (data is null) continue;

                // Update data to restore auto implemented properties
                data.Update();

                // create a shiny new custom Object from the stored data
                var customObject = data.CreateCustomObject();

                // replace original object in doc
                doc.Objects.Replace(new ObjRef(docObject), customObject);
            }

        }

In this example I defined a method inside of MyCustomUserData to create a custom object.

You can also take a look at this Repository Where i implemented Read Write for the RectangleData class as an example

1 Like

Hi Lando,

Many thanks for your help!
By using the sample you provided here: Custom object for storing internal meta-information

And with the help of your Octopus example, I eventually get the CustomObjects to be re-created on document load time. :slight_smile:
I’m not sure to have fully/deeply understood the events/handlers stories yet, but at least, it works. I’ve now to wrap my mind around all this!

Hi @lando.schumpich,

How to elegantly re-create the custom objects at document opening when you have different kind of CustomObjects with different types of associated CustomUserData?

In your example, we loop through all objects from document, and look if some custom user data is attached to the object. If yes, we recreate the object based on the user data attached to it.

Let say we have these guys in our document:

  • ‘MyCustomBrep1’ associated with ‘MyCustomBrepUserData1’
  • ‘MyCustomBrep2’ associated with ‘MyCustomBrepUserData2’
  • ‘MyCustomCurve’ associated with ‘MyCustomCurveUserData’

Do we also need to loop 3x times through the document objects, each time looking for a special type of user data, first time doing:

docObject.Attributes.UserData.Find(typeof(MyCustomBrepUserData1)) as MyCustomBrepUserData1

a 2nd time:

docObject.Attributes.UserData.Find(typeof(MyCustomBrepUserData2)) as MyCustomBrepUserData2

and so on…

Or is there a way to ‘filter’ and cast the user data type relatively to its type?

Thanks in advance!

One immediate optimization that comes to mind, is doing all your filtering inside of one loop:

// try get all different user data types
foreach obj in doc.Objects {
    var myCbrep1 = obj.Attributes.UserData.Find(typeof(MyCustomBrepUserData1)) as MyCustomBrepUserData1;
    var myCbrep2 = obj.Attributes.UserData.Find(typeof(MyCustomBrepUserData2)) as MyCustomBrepUserData2;
    var myCcurve = obj.Attributes.UserData.Find(typeof(MyCustomCurveUserData)) as MyCustomCurveUserData;

    // at max only one of your variables won't be null
    if(!(myCbrep1 is null)){ // do stuff}
    if(!(myCbrep2 is null)){ // do stuff}
    if(!(myCcurve is null)){ // do stuff}
}

This is just pseudo-code, I’m not sure right now if the as - casting will throw if no userdata is found

I have a better solution which takes advantage of the fact, that all UserData derives from DataBase ins the Octopus project, but it is not implemented as of yet and sadly I don’t really know when I will find the time to do that. I can keep you updated :slight_smile: