Sign in to follow this  
  • entries
    21
  • comments
    30
  • views
    12003

backlogged entry #2

Sign in to follow this  

115 views

Okay this is a resolution of the problems I asked about in this thread

The problem was.. well there were several problems. *One* of the problems was that I needed a good way to make undoable changes to the model. Saving the entire model state is a reliable way to do this, but this would make my undo stack take up a ton of memory.

So, change #1 was to give all nodes a unique ID, and have a hashtable to find nodes based on their ID. This was a good change, most of the stuff below would have been impossible without it.

For the incremental change thing, I *could* have had every Command object just keep track of how to undo itself. For example, the DeleteNode command could keep track of "nodes I just deleted". But this gets messy. Deleting a node will also kill all connections involving the node, so if you want to undo a deletion, you also need to remember all the connections. And it would be great if I could have a generalized way to do this, so I didn't have to go through the same messy process for every Command.

So attempt #1 was to have an UndoableOperation object that keeps track of every change made. Commands would do operations on the model through this object, and the UndoableOperation would break each action down to a bunch of undoable atomic operations. And it keeps all the atomic operations on a big stack, and when you tell it to undo, it pops everything off the stack and undos each step one at a time. This was to handle things like deleting a node (deleting a node would first produce a bunch of atomic delete connection items)

But this was messy and often failed in unexpected ways. And in certain situations it would be way inefficent. Say I am dragging a node with the mouse (which should be undoable). Every 1/20th of a second when a MouseMotionEvent is generated, the code is going to find that the mouse has moved over a few pixels, and it will tell the UndoableOperation to move the node, which will push a new "move the node a tiny little bit" operation on the stack. This is no good. Granted there are ways I could have improved this, but at this point my confidence in this method had dropped.

So attempt number 2, which will be called attempt number SUCCESS. It's called a ModelDelta object, although "partial memento" might be a better name. You can tell the delta object to "mark" any node or connection. Once marked, it saves that object in a Memento object. If the thing is already marked, it ignores you. And it has an "undo" command, which goes through all the marked objects and restores them to how they were before.

So now, every time to make a change to the network, you make sure to first mark the thing you are changing (and I have a wrapper, also called UndoableOperation, which takes care of marking for you). As long as everything gets marked, you can use ModelDelta.undo to restore the previous state. It works great. It's very clean. Everybody's happy.

Also I don't know if anyone has used this pattern before, but I'm using something I call the Phantom Memento. The phantom memento remembers that a certain object doesn't exist. If you do a command that adds a node, the delta object keeps a phantom memento to remember that this node wasn't here a second ago. "Restoring" a phantom memento means deleting the node. WoooooOOOOOooooOOOOOOOO. Spooky.
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now