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.
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.
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!
For re-creation i typically do this inside of the plugIn class:
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;
}
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
And with the help of your Octopus example, I eventually get the CustomObjects to be re-created on document load time.
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!
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?
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
Sorry to jump in here, but my problem is related to this topic.
I have no problem replacing the scene objects with my custom objects after document has been loaded.
(I can see great examples for this in this topic.)
But what if my custom object is part of a block object. And it is used in the scene only as part of the block object (or block objects).
In this case my object is not in the doc.Objects list. I can find it from the InstanceDefinition (GetObjects) but because it is not in doc.Objects, doc.Objects.Replace doesn’t work.
What is the correct solution for this problem?
maybe this function could help:
doc.Objects
bool ReplaceInstanceObject(ObjRef objref, int instanceDefinitionIndex);
Again necrobumping… Dear @dale would you mind to point me what i can do possibly wrong as in my case seems protected override bool Write is never called. My UserData derived class have public override bool ShouldWrite => true; and it is attached to an object:
It’s my custom object which derives from CustomPointObject in constructor I’m adding UserData like
public PivotPoint(Point3d at) : base(new Rhino.Geometry.Point(at))
{
var pivotPointUserData = new PivotPointUserData();
pivotPointUserData.Plane = new Plane(at, Vector3d.ZAxis);
Attributes.UserData.Add(pivotPointUserData);
}
While editing document I can easily get this UserData from this object via UserData.Find. But on Read nothing happens so I investigated and set a breakpoint inside Write override but it never hits it. What is missing? I’ve run out of ideas to be honest.
the first thing that comes to mind, is custom UserData needs to have an annotated GUID, see the definition of my RectangleData class which implements read/write:
[Guid("894CD4BF-7838-4053-8B20-1F07B4FB13DA")]
public class RectangleData : DataBase
{
public double Width { get; set; } = 1; // X
public double Height { get; set; } = 1; // Y
// code ommitted ...
// You must define a Guid attribute for your user data derived class
// in order to support serialization. Every custom user data class
// needs a custom Guid
But I also forgot to annotate many times, it is quite annoying that no error is thrown in that case, would save some time debugging everytime it happens