Using custom objects

Hi @stevebaer, @dale , @everyone,

I need to understand as depth as possible the behavior of rhino custom objects (or RhinoObject). I have created a fake plugin isolating the issues in question, with comments, in case you can help me with some time. Using C#, it only contains an Entity and EntityCurve classes, a UserDictionary, a CustomCurveObject, two simple commands and a simple ETO panel. CustomRhinoObjectTest.zip (126.6 KB)

What I’m trying to do in my real plugin is to put all the logic of my tools inside Entities, an abstract object with several behaviour patterns, and with this framework automate as much as possible the creation of commands for Rhino and components for GH. That’s something that is more or less done, I’m explaining it for context and because it’s a important constraint, for example, my entities can not inherit from other classes (due to the C# limitation).

Now I’m with getting my geometric entities to sync with Rhino custom objects, I need the states of the two objects to sync correctly in both directions for all the behaviour that a rhino object can have (gumball, control points, etc) and for all the input contexts (commands, UI and the own entities).

My current approach is for each geometric entity (EntityCurve, EntitySurface, etc) to convert it to its corresponding Rhino object (CustomCurveObject, CustomBrepObject, etc) so that the user can play with it, and store inside of it the entity instance. With this I can modify the rhino object and update the state of my entity, via command, UI or with the gumball, but there are some issues that I explain below. But first, do you think this is the best way for my entities to act on the document?

Issue 1
Here I call the command EntityCurve, select a curve, and replace the curve with my custom object. When selected, the panel show its parameters, wich just inflate a boundingbox as a visual reference that it is working. All works well until I create a duplicate. I can modify the copy but when I modify the original, the copy recovers the values of the original object. This is due to the history, but I don’t understand why. How can I prevent this from happening? Rhino6 does not have a method to add a custom curve object without the history.

How do I know if the duplication context is due to rhino replacing the object due to a user modification, or if it is due to a copy of the object being created? Because in the second case I have to duplicate the entity. This is my current code:

 protected override void OnDuplicate(RhinoObject source)
    {
        RhinoApp.WriteLine("OnDuplicate");
        base.OnDuplicate(source); 
        if (source is RhinoEntityCurve rhe)
        {
            Entity = rhe.Entity.Duplicate() as EntityCurve; 
        } 
    }

Issue 2
When I edit the curve from the control points, my custom object seems to disappear, how do I make it work correctly?

Issue 3
For some reason, I had to replace the ObjectTable.Replace method by Delete+Add, bc it would remove the name of the object, as if it were replacing the attributes. Is that intentional?

// This remove rhObj attributes
//doc.Objects.Replace(objRef, rhObj); 
doc.Objects.Delete(objRef, true);
doc.Objects.AddRhinoObject(rhObj, null);

Issue 4
For some reason, overriding ShortDescription() method and calling its base method throws an exception: Rhino.Runtime.DocumentCollectedException: ‘This object cannot be modified because it is controlled by a document.’

//public override string ShortDescription(bool plural)
//{
//    return base.ShortDescription(plural);
//}

Issue 5
From your experience, what other things should I take into account? Should I make my own undo? If I want to include the entity’s metadata in a UserData, should I take something into account to keep it sync as well? Is there anything non-obvious that I should know?

I am very sorry it is so much text. Thank you very much for your time.

Custom objects are a bit of a pain in Rhino and as you can see they can be fragile. You might be better off using custom user data.

Hmm, in that case… I don’t know if I need the UserData. My data is stored in the entities and the document contains a table of serializable entities (which should reference their Rhino object). Including UserData may be redundant, I’m not sure yet.

My reason for using Custom Objects is to handle the relevant events that a Rhino object takes (transform, duplicate, save, add or remove from the document, …anything else missing?). It seems that UserData doesn’t allow me to handle all this right (just transforms and duplication with the same problem as in Issue 1)? I would have to resort to RhinoDoc events and since I have to do this, I can do it all that way (without UserData I mean), right? or do you think that UserData is better for some reason?

So, do you think that my geometry entity could, if it references a geometry in the document, be aware of all the necessary operations (via RhinoDoc events) of the geometry it references to synchronize its states? I should link it to the RhinoObject right? or to the geometry itself or its attributes?

What I have more doubts about is how to keep the reference between my entity and the geometry in the document, which would be a pair of IDs, but what about duplications? there are more operations that modifies these IDs?

I’m two weeks late on this, so any help you can give me to point me in the right direction would be very very helpful…

Thanks :blue_heart:.

Custom objects are pretty fragile. Simple tasks like moving control points on a curve has the potential to call Replace on the document with non-custom curve geometry resulting in a custom object turning into a non-custom object. UserData in this case does properly get duplicated and attached to the new object.

User data does have callbacks for Read, Write, Duplicate and Transform. Are there other events that you need to watch?

We probably need to add functionality to RhinoCommon to be of any serious help with this project. Is this a possibility or are you constrained to a version of Rhino we aren’t going to be adding SDK enhancements to (pre-Rhino 7)?

2 Likes

Since I have a table of entities, it must be aware also when an object is deleted (to delete the entity as well). And I don’t know if I’m missing any other relevant event, this was part of the question.

I would like it to work for Rhino 6, and so I made the decision to wait to go where I wanted to go, but with room to build it later. Then it would be great if for Rhino 7 I can integrate it, because my entities are strongly defined, which allows me to include a lot of generic customization in their representation in the document (custom drawing like including annotations, maybe show interactive panels inside the document when selecting, geometric entities support SpaceMorph and transformations, manage materials, layers or any attributes, custom gumballs/transform widgets…). So being able to put this into a custom object (or a few) instead of resorting to many isolated supports (conduits, document events, UserData…) that have to deal with different types of entities, would be great for smoothing development, making it elegant and with the stability and all the functionality it requires.

I don’t want my users to work with meaningless geometry but with highly constrained objects, for example, if I had the entity chair, composed by other sub-entities (legs, seat, backrest, armrests…), the user would be forbidden to put the legs over the seat, and thanks to these constraints (that simply remove everything that breaks its meaning as a particular real object) make a lot of smart features.

Count on me if I can help in any way.

1 Like

This description helps me better understand what you are trying to do, thanks. It is hard to imagine what your goal is with terms like entities.

In this case it does sound like you should use custom objects. Ignore what I wrote before about UserData :slight_smile:

1 Like

Did anyone find a good solution for this? I need to adjust behavior based on whether a modification or a copy is happening.

@ScottMitchell In case of a modification (move for example) the object generates a replace, delete and add event, the id stays the same.