Undo and size of undo stack

In Rhino, it is possible for the user to set a memory limit to the undo stack (somewhere in the options pages). I was wondering if it is possible, or could be made possible, to do the following 2 things:

  1. add memory pressure to the undo stack when adding custom undo actions
  2. be notified when, due to the undo stack going over size, that custom undo items are removed from the bottom of the undo stack so as to free up memory.

Reason I’m asking is that in our plug-in the undo stack for custom undo actions can now grow to (much) larger sizes than the size the user allows. Even though each individual undo action is maybe a couple of kB in additional memory pressure, each time a user e.g. moves a control point (which happens freakishly often with some users) a new undo action is added.

Rhino does trigger an Undo event when a object purge operation occurs. I’m not sure Undo events are exposed in RhinoCommon. But they could be added if they were deemed to be helpful.

If you are storing a lot of user data on objects, then you might want to consider moving the user data to some structure inside of your plug-in. Then, just add some small piece of user data to the object that references back to your data structure. This would reduce the memory foot print of your user data.

Ok, maybe I was not completely clear about this. I have a situation that describes your suggestion, that is, an object data model that contains data about the geometry in the document. This object data model is kept as a member variable on my plug-in.

Now, when the geometry in the document changes, this means that the object data model must change too. And, when the geometry change is undone, the object data model must change back. To that end I have used RhinoDoc.AddCustomUndoEvent(…) and a self-made undo stack that holds the undoable actions.

This means that each change to the geometry is accompanied by an undoable action being pushed onto my own undo stack. For this I typically use the Memento pattern to store the state of the object model. Now each memento is maybe a few kB in size, but when a user starts nudging like there is no tomorrow, these memento’s add up to a sizeable amount of memory.

On the Rhino side, the size of the undo stack is managed with the setting I mentioned in the first post. When many or large undoable actions are placed on the Rhino undo-stack, the stack is resized by removing items from the bottom of the stack.

What I would like to do is to somehow add memory pressure to the Rhino undo stack to reflect the size of my undo stack and, when undoable items are removed from the Rhino undo stack that have a counterpart in my own undo stack, to be able to remove the items in my own undo stack too.

Referring to the picture below, when “Rhino: Undo1” gets removed due to the Rhino undo stack growing too large, I want to be notified and also remove “My: Undo1” from my own undo stack.

I suppose that this would require an id to be returned when RhinoDoc.AddCustomUndoEvent is called and the creation of an additional event called e.g. RhinoDoc.CustomUndoEventDeleted where this id is exposed in the event arguments.

I’ve had a similar issue. The way that we solved it was that the pointer into our internal data was a ON_UserData subclass that was attached to geometry in the document. As such, old versions got pushed into the undo stack when a user moved control points, and new copies created.

We implemented the ON_UserData::SizeOf function to report its size as being equal to the delta. This puts the desired memory pressure on Rhino, so it knows when it is storing more undo data than the user has specified. Then we implemented ref counting, so that when Rhino decided to purge all the old undo information, including our userdata, then we could trim the length of our internal undo/redo stack.

I’m not sure if a userdata works for your interaction model, but if it does it was pretty straightforward to get what you’re looking for.

Thanks @tomfinnigan that is a very useful suggestion. I have not used the UserData object structure yet, so far I have used the UserDictionary to store a few values. I’ll look into overriding the UserData class together with refcounting or similar bookkeeping.