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.

Hey Steve, does RH8 have anything new related to custom objects?

@Dani_Abalde I’m actually adding functionality to custom objects right now for an upcoming Version 8 service release, in particular with respect to bounding box which I think comes into play with a couple of your issues. I believe that I see what you were trying to do in the above case and will use your sample to see if I can add the functionality you need, as this is a good time to do so. If there is an update to your specific needs, please let me know because I should try to address them while I am doing this.

Thanks,
Bill Cook /)

1 Like

Great! :smiley: This thread looks very complicated so let me reformulate.

What I need is to let the user operate with my entities as document objects. Real world objects not only have geometry and metadata, they also have programmable behaviour. That’s why there are so many plugins. With Rhinocommon is hard and incomplete to manipulate custom objects from the viewport. Custom objects like an arrow that you can drag on its axis, or a custom spline, or a rail of profiles that forms a pipe with known/named sides, or a chair whose backrest can only be rotated on discrete values…

Consider I want to create a custom curve type, a rail entity. This rail is like a curve but it allows you to control its normal and bi-normal functions so that you can define how to extract perpendicular planes of it, for example, to force its Y axis always perpendicular or parallel to other geometry. For other side, the build function of a wire entity would be: extract perp. planes on a rail, put profiles on them and create a sweep surface with caps, so the wire is algorithmically aligned to other geometry thanks to the rail behaviour, and its wire methods can be call and its wire properties are updated after any rail change. I want the user to modify the control points of the rail curve for example then rebuild the wire using the new plane profiles and in between update all neccesary entities.

Would be awesome if:

  • From a command, create an entity and add it to a document as a custom object, modify the entity from the viewport or parameters (like in the project provided in the thread), save it and load the entity/custom object in the state it was saved.
  • Any user operation (such as transforming, modifying control points, deleting because it was purged or trimmed, when it is duplicated and for what reason, or when is selected, undo/redo…) can change the state of the custom object and therefore must update the state of the entity.
  • Perform a transformation constrain, for example, prevent to move the custom object in an arbitrary axis.
  • Create an entity that has multiple geometry types on the same plane reference, so when one is transformed, the custom object must update the other with that transformation.
  • Get all documents objects that inherits a custom object type.
  • A RhinoGet< T> where T is a custom object.
  • Have the Rhino.DocObjects.Custom.CustomGenericObject class.
  • Have a hierarchy of custom objects (objects that contain other objects) or some kind of linking.
  • Notify with event or a OnUserDataChanged method when UserData is modified.
  • Synchronise state changes from the entity to the custom object and from the custom object to the entity.
1 Like

I was investigating why I couldn’t inherit from RhinoObject and I found this:

Out of curiosity, is just hierarchy preventing custom objects? what are the problems?

Also would be cool to have some control over serial numbers, but I’m not sure what’s the actual scope of this. I guess it is a per-instance unique constant that is preserved on the document? Or it changes if I import that object to a different document? What I have in mind is the idea of ownable objects that can only be changed if you have the right permissions over that object (if GetCommitFunc() can be overridden).

Hey, some update about this?