Dropping this here, hoping this helps others that run into a similar problem
I’m working on code that allows me to procedurally generate other rhino objects from a source object. I was having a lot of trouble getting undo working correctly until I found the UndoRecordingEnabled flag on the document. Now it’s working great. If you don’t disable recording events, then you get into situation where undo/redo also adds and removes the dependent objects and because you can’t control the ordering of that, you occasionally get duplicate objects:
But in the meantime working dependent objects:
Some general psuedo code for how it’s built, note that it glosses over handling a changing under of dependent objects, preserving selected geometric modifications, and dissociating a dependent object from the source object in order to keep it when the source is deleted:
Event Listeners:
OnRhinoObjectAdded()
{
GenerateObjectDependentGeometry()
}
OnRhinoObjectRemoved()
{
if(ObjectBeingReplaced) return;
RemoveDependentGeometry()
}
Main Generation
List<ObjRef> CurrentDependentObjects;
GenerateObjectDependentGeometry()
{
StartDependentObjectModification();
List<GeometryBase> newGeometry = GenerateGeometry();
int index = 0;
for(; index < newGeometry.Count; index++)
{
if(index < CurrentDependentObjects.Count)
{
UpdateObject(CurrentDependentObjects[index], newGeometry[index]);
}
else
{
CreateNewDependentObject(newGeometry[index]);
}
}
RemoveDependentGeometry(index);
CompleteDependentObjectModification();
}
Helper Functions
CreateNewDependentObject(GeometryBase newGeo)
{
var id = Doc.Objects.Add(newGeo);
var objectRef = new ObjRef(Doc, id);
CurrentDependentObjects.Add(objectRef);
}
UpdateObject(ObjRef dependentRef, GeometryBase newGeo)
{
var obj = dependentRef.Object();
//if a user modified the subobject by deleting it, respect that
if(obj is null || obj.IsDeleted) return;
/* I'm using replace instead of just delete and add to preserve other data
* that may be attached to the dependent objects in between modifications
* to the source object.
*/
Doc.Objects.Replace(obj, newGeo, true);
}
RemoveDependentGeometry(int startIndex = 0)
{
StartDependentObjectModification();
for(int i = startIndex; i<CurrentDependentObjects.Count; i++)
{
Doc.Objects.Delete(CurrentDependentObjects[i]);
}
CompleteDependentObjectModification();
}
And Finally Undo Recording Gaurds
int _dependentObjectGaurd = 0;
bool _cachedUndoRecordingState = false;
StartDependentObjectModification()
{
if(_dependentObjectGaurd == 0)
{
_cachedUndoRecordingState = Doc.UndoRecordingEnabled;
Doc.UndoRecordingEnabled = false;
}
_dependentObjectGaurd++;
}
CompleteDependentObjectModification()
{
_dependentObjectGaurd--;
if(dependentObjectGaurd > 0) return;
_dependentObjectGaurd = 0;
Doc.UndoRecordingEnabled = _cachedUndoRecordingState;
}