So it's been a bit busy recently at work writing up literature review notes, preparing for a visit to Trondheim, Norway, and continuing my research into operational modelling and planning for NHS hospitals. One of the key concepts of the development work I am currently undertaking to solve a research aim is the use of Rhino 3d to model, simulate, and predict what will happen in a hospital with ease. As part of this, I have been working in Rhino looking to develop a layout manager - a plugin which will allow estates managers to view their model and make minor changes and switch between these changes with ease. For example, it may be the main layout on one tab, with an identical layout but with an extra reception desk in the second. They might wish to compare the performance of both layouts and visually represent them in the Rhino viewport. So my work has been looking at ways to essentially take a copy of the Rhino active document object table, store it, and rebuild it on request without losing any unsaved changes in the background.
For example, let's say I have three models, MA, MB, and MC. Of these, MA and MB are saved within my layout manager but MC is not. If I want to view the MA model, I wish to do so without losing the changes in the unsaved MC model. I might then want to switch to view the MB model, before returning back to the MC model. Unfortunately it's not as simple as saving and switching object tables, because there is custom data attached to the geometry which impacts how they save/reload themselves, and the ability to simply set the object table doesn't exist.
So the process I have taken is as follows:
- Take a copy of the Rhino Objects in the current object table
- Remove all objects from the object table
- Load in the objects from the selected model into the object table
This was working, somewhat, until I encountered the error in the title of this post - "This object cannot be modified because it is controlled by a document."
Very frustrating. I could switch between models happily until I made some change to the MC model (deleted geometry, or moved it) and when then trying to view MA or MB, I would get that error. With a little digging, I figured that the problem lay with the Rhino Object not being correctly handled - a saved copy in MA or MB could not be modified if it was removed from the document which owned it in the MC model. So I changed the process slightly, and instead of taking a copy of the Rhino Objects, I now take a copy of the object geometry instead.
Take the geometry, not the object
Taking the object leaves behind a link to the active document which owned it prior to the modification of the object. But the object table will allow you to add geometry, as well as Rhino Objects. This allows us to take a duplication of the Geometry Base geometry and re-add it as a new Rhino Object later. If we keep a copy of the custom data as well, we can then re-add this separately where necessary (but for this example we're just focusing on the geometry).
So now the process is as follows:
- Take a copy of the object geometry - see below for the method
- Remove all Rhino Objects from the object table
- Load in the saved geometry base objects into the object table - see below for the method
This first code example is for step one, taking a copy of the object geometry from the Rhino Objects rather than the objects themselves.
List<GeometryBase> objectGeom = new List<GeometryBase>();
foreach (Rhino.DocObjects.RhinoObject o in Rhino.RhinoDoc.ActiveDoc.Objects)
objectGeom.Add(o.DuplicateGeometry()); //Important to duplicate the geometry - not just a reference to it
The comment in the code above highlights the use of the 'DuplicateGeometry' method, rather than just taking a reference of the geometry itself. If we take the reference we run into similar problems with taking the Rhino Object. Thankfully, Rhino Common gives us the method to duplicate the geometry easily, so we might as well take advantage of it!
The next segment of code shows how we can remove the objects in the object table and reload in our saved objects from the previous function.
//Empty the object table
foreach (Rhino.DocObjects.RhinoObject obj in Rhino.RhinoDoc.ActiveDoc.Objects)
//Load in saved objects
foreach(Rhino.Geometry.GeometryBase geom in objectGeom)
Once I switched over to using this method of deleting/reloading geometry I haven't had the error reappear. If you're having the same issue - check what you're referencing and see if you can take and work with a duplication instead.