undo/redo behavior

Ideas, enhancements, feature requests and development related discussion.

undo/redo behavior

Postby sascha » Fri Nov 23, 2007 4:53 pm

How do you think undo/redo should behave in JPatch.

In the old version, basically only actions that changed a model (or animation) have been recorded, and thus have been undoable.
It gets a little more complicated with tool changes (e.g. the user switching from the move to the rotate tool). When undoing or redoing e.g. a rotate-object action, you usually want to see what's going on, so it helps if (in this case) the rotate tool is visible when the user clicks undo or redo. Thus we have to record tool changes as well.

This alone leads to some choices:
* Should the tool change (e.g. changing from move to rotate tool) be an edit on its own, or should it be merged with the following edit (actually using the tool). To clarify this, here's what would happen:
User selects model, clicks on translate tool, moves model, clicks on rotate tool, rotates model, rotates model again.
Here's what would be happen on subsequent clicks to undo in this case.
Case 1)
- undo last rotation
- undo first rotation
- switch back to translate tool
- undo translation
- switch back to select tool
- undo selection
Case 2)
- undo last rotation
- undo first rotation and switch back to translate tool
- undo translation and switch back to select tool
- undo selection

I think the latter might be more userfriendly, and it would "swallow" unnecessary tool-changes (e.g. switching back and forth between rotate and move tool without actually using the tools would not generate undoable edits).

Another choice concerns which actions should be recorded. The new design of JPatch allows to track almost everything, so it would be easy to add undo/redo support e.g. for viewport changes (even to changes of the window size, if you'd like). So the question is, does it make sense to only record changes that affect models, or would it make sense to be able to undo e.g. a viewport-zoom or a viewport-rotation?

When working on multiple files, it doesn't make much sense to view the entire session as a single, "one dimensional" sequence of edit-actions. If you've got two models loaded, edit model 1, and then edit model 2, it would IMHO not make much sense to have to undo all edits on model-2 before you can undo the edits on model-1. Thus, there should be different "undo managers", at least one for each open file.
This would (at least I think so) automatically rule out a solution where things like viewport-rotations can be undone - which undo-manager should record the viewport change, the one for model-1 or the one for model-2. There could be a separate undo-manager for the viewport, but this would get messy and quite counter-intuitive from a user's perspective.

Thoughts?

Ok, before I forget, undo/redo is working just fine in the new JPatch version, and it's much easier to make something "undoable" now (for me, as the developer). Both rotate and translate tools work as expected (even on models transformed in the scene-graph) and are fully undo/redoable. I'll upload a new development-snapshot this weekend. What I'd like to add first is support for the object mode (in this case, the tools wouldn't act on the vertices, like in vertex mode, but on entire scene-graph-nodes). And then the scale tool is still missing, but as it looks very similar to the translate tool (and behavior is quite similar as well), it should be possible to forge a common base class for both tools and thus quickly implement the scale features. Stay tuned...
sascha
Site Admin
 
Posts: 2792
Joined: Thu May 20, 2004 9:16 am
Location: Austria

Re: undo/redo behavior

Postby sascha » Sat Nov 24, 2007 8:20 am

*bump* :)
sascha
Site Admin
 
Posts: 2792
Joined: Thu May 20, 2004 9:16 am
Location: Austria

Re: undo/redo behavior

Postby dcuny » Sun Nov 25, 2007 4:36 am

sascha wrote:*bump*

I'm not ignoring you, it's just a long Thanksgiving weekend over here in the States. :)

Besides that, you asked a hard question. I'm not sure I've got a good answer for this.

From an abstract viewpoint, I think of undo as returning something to a prior state. So if I can make changes and undo back to a prior state, I'm pretty happy.

Should the tool change (e.g. changing from move to rotate tool) be an edit on its own, or should it be merged with the following edit (actually using the tool).

I don't think a tool change by itself really matters.

So the question is, does it make sense to only record changes that affect models, or would it make sense to be able to undo e.g. a viewport-zoom or a viewport-rotation?

Would it be that difficult to support both? By default, I suspect most people would want the more detailed undo option. But I don't really have that much experience with modeling, which is where I think most of this becomes important.

Thus, there should be different "undo managers", at least one for each open file.

I think this makes sense as well. Of course, it would help if JPatch enforces this separation via the interface. That is, if you edit in the "Edit" view, then the undo would be associated with the "Edit" mode in the "Edit" window.

Ok, before I forget, undo/redo is working just fine in the new JPatch version, and it's much easier to make something "undoable" now (for me, as the developer).

:)

Stay tuned...

I'm looking forward to playing with it!
dcuny
 
Posts: 2902
Joined: Fri May 21, 2004 6:07 am

Re: undo/redo behavior

Postby sascha » Sun Nov 25, 2007 9:27 am

'm not ignoring you, it's just a long Thanksgiving weekend over here in the States.

Sometimes the "new post" flags aren't shown (at least phpBB2 had this sort of problem, that's why sometimes I've responded to messages weeks later).
Happy Thanksgiving!

In the long run, it would be nice if undo/redo played back a (very short, say 200-300ms) animation showing what is actually being undone (e.g. translate the object from A to B and showing this by moving it on the screen. Similar animations could be played e.g. when switching from 4- into single-viewport mode (to show which viewport goes where). I think these are much more than eye-candies, it makes it much easier for the user to see what's going on. While not all too hard to implement, these effects are in no way mandatory, so I'll think about it again after version 1.0 has been released. Apple has got a lot of such effects in their OS, and there's also a timing and animation framework for Java (presented in Filthy Rich Clients) which does exactly that. But as I said, right now there are more important things to do (so don't worry, this won't distract me for another month or two ;-))

Back to undo/redo. I'll implement it in a way which I see fit, it should be easy to adapt or change in the future. For the tools I've found a nice solution: They remember which tool has been used to modify an object. If a tool is used (not just selected) again, it checks if it has been used in the previous modification as well, and if not, it adds a "tool change" event to the edit chain.
It will become more complicated once there are more than one undo-managers involved, I'll have to think about it. Ideas are welcome, especially about this aspect (multiple open files and multiple undo managers). I think a straightforward approach would be to store as much "program state" as necessary when the user switches to a different file or context (including which tool was active, etc.). This way switching back and forth between files (and modes, e.g. modeling vs. animating) would also restore the program state last active with the file in question - from an undo/redo perspective it would be as if different independent applications are in use, but for the user everything is integrated in one GUI. It would be comparable to a text editor with multiple files. Undo/redo works independently for each file, and it also remembers some "state" for each file (e.g. the cursor position, the selected text, etc.) Applying that to JPatch is a bit more complex, but I think the basic idea is sound.

Thoughts?
sascha
Site Admin
 
Posts: 2792
Joined: Thu May 20, 2004 9:16 am
Location: Austria

Re: undo/redo behavior

Postby dcuny » Sun Nov 25, 2007 6:36 pm

sascha wrote:It would be comparable to a text editor with multiple files.

That's the "mental model" that I'm using as well. But I also think it's helpful to divide JPatch up into functional bits. For example, should Undo in the Animator effect a change made in the Editor?

My initial thought would be that it shouldn't, but what happens when you undo away some sort of state, like a morph? So (to some extent) these are interrelated on a time axis. :?

So I agree - implement something sensible now, and worry about the details later when you've got more experience using it.

I had looked at the Filthy Rich Clients code when writing the drag and drop for the video editor. Of course, I'm leaving that feature out (for now) until Java supports it correctly. That whole project is on hold for the moment, since I'm back working on a complete rewrite of wxBasic. But at the point where a semi-stable version of JPatch comes out, I'll see what you think about me trying to integrate it into JPatch.
dcuny
 
Posts: 2902
Joined: Fri May 21, 2004 6:07 am

Re: undo/redo behavior

Postby John VanSickle » Thu Feb 21, 2008 3:08 am

sascha wrote:How do you think undo/redo should behave in JPatch.

As someone who has struggled with undo/redo in C (and graduated to much easier implementations in C++), I have some definite thoughts.

At the moment I have only implemented undo and redo for things that get saved to the project file. View and mode changes are only one or two mouse clicks away, so I didn't feel that undoing them was terribly important. But the state of things in a project are just as much data as are the user-level objects, so they can be undone and redone by the same means; if these data are kept in a bundled class, that class can be blessed with the same functions that the vertex and polygon classes have, and you'll have the same result; then it becomes just one more function to be called in the project-level snapshot, undo, and redo functions.
John VanSickle
 
Posts: 189
Joined: Sat Feb 16, 2008 2:17 am

Re: undo/redo behavior

Postby sascha » Thu Feb 21, 2008 1:34 pm

I asked the question more from a user-interface perspective than from a technical one. But of course there are implications. For things like tool changes, I found out that making the active tool part of the "state" that can be un- and re-done makes sense, because otherwise it would simply look odd. Imagine you applied three rotations, followed by a translation. The currently active tool is the translate tool, so the viewports would show the typical x/y/z translation arrows. If you now undo the last 4 operations, you'd see the object rotate, but you'd also still see the translate-tool overlay. There are two solutions:
1) Switch to a null tool whenever undo or redo is clicked.
2) Make the tool part of the state - so undoing a rotation would automatically switch to the rotate tool.

The other, more tricky question, is how many of these undo-lists should be there. The simplest solution is to have just one. This is straightforward to implement, but not very user friendly. If two models are loaded, undoing operations performed on model 1 should not imply undoing all operations performed on model-2 in the meantime. This would make handling of things like tools a bit more complicated (you'd need one active tool per model), but that's nothing that can't be solved. It becomes more tricky with model- and animation files: Changing a model file could have an impact on animations: What should happen when e.g. a bone was deleted from a model, but this bone has a motion curve associated with in a (probably open) animation file? The only solution I could think of is this:

When opening animations, ignore any channels that are defined in the animation file, but can't be found in the model (e.g. if the animation defines a motion-curve for "lower-arm-bend", but the model has no "lower-arm", simply ignore it - or perhaps output a warning message - but otherwise load the file normally). Such "unbound" channels would not be saved though (at least per default, I could make it an option).
Changing models (e.g. deleting bones) will not affect animations. If a motion-curve becomes obsolete in an animation, so be it. If a new bone with the same name is created (or the delete operation undone), the motion-curve will "bind" again.
sascha
Site Admin
 
Posts: 2792
Joined: Thu May 20, 2004 9:16 am
Location: Austria

Re: undo/redo behavior

Postby John VanSickle » Thu Feb 21, 2008 9:44 pm

sascha wrote:The other, more tricky question, is how many of these undo-lists should be there.

What I did was to make each object responsible for its undo/redo behavior:

* Each object has a function which saves the current state of the project in a linked list; each link also has an integer member which specifies the change number. If any changes have been undone, those are deleted from memory.

* Each object also has a function to restore the object to a certain change number (the highest-numbered change that is not higher than the specified change number).

The project itself requires a member to track the highest change, the current change, and these functions:

* A snapshot function, which increments the current change number, sets the highest change number to the same value, and calls the snapshot function of every object in the project.

* An undo function, which decrements the current change number, and calls the restore function for each object in the project.

Objects added after project load/creation get a change number greater than zero, if the current change is reduced below the change number for object creation, the object is moved to the inactive pool and is no longer displayed. In a like manner, deleted objects are moved to the inactive list, but when the undo level goes to a change prior to deletion, the object is brought back to the active list of objects. Actually I merge the active and inactive lists (an O(1) fuction for double-linked lists), and then pick through the merged list, looking for objects created after, or deleted before, the current change number; these are moved to the active list.

The undo/redo functions also rebuild any data that is derived from user-specified data (only the user-specified data needs to be kept in the change list).

When the current change number equals zero, there is nothing left to undo, so I diable the undo button, and when the current change equals the maximum change, I disable the redo button.

Voila, immediate undo/redo for all projects, with undo levels limited only by the memory available.

I have a page on this here: h**p://www.geocities.com/evilsnack/undo2.htm
John VanSickle
 
Posts: 189
Joined: Sat Feb 16, 2008 2:17 am

Re: undo/redo behavior

Postby sascha » Fri Feb 22, 2008 12:40 pm

Well, as I said, technically undo/redo is not that much of a challenge - there are many ways to implement it. JPatch records "atomic" edits (i.e. state changes) in an edit-list. Thus, not the state, but only state changes are recorded (saving some memory). Such an edit list (containing any number of atomic edits) makes up an undoable-edit (calling undo simply undoes everything in the list in reverse order), and an "undo manager" keeps track of undoable-edits (again storing them in a list). I've been using the same concept (with minor modifications) since JPatch 0.1.
In the latest version, each method that changes an object's state can, optionally, create atomic edits and add them to an edit list, so each state change can easily be undone. An atomic edit implements an interface that defines two simple methods: void undo() and void redo(). This heavily relies on polymorphism, so I don't know if it's 1:1 applicable to C.

The question raised in this thread was whether to use a single "undo manager" to keep track of all edits, or to use per-model, per-file or per-project "undo managers", and how they should interact without confusing the user.
sascha
Site Admin
 
Posts: 2792
Joined: Thu May 20, 2004 9:16 am
Location: Austria

Re: undo/redo behavior

Postby dcuny » Fri Feb 22, 2008 7:11 pm

sascha wrote:The question raised in this thread was whether to use a single "undo manager" to keep track of all edits, or to use per-model, per-file or per-project "undo managers", and how they should interact without confusing the user.

Think of the following scenario:
  1. The user has two windows open - the Modeler and the Animator.
  2. While working in the Animator, they realize they're missing some morph target. They pop over to the Modeler and add the morph target.
  3. Back in the Animator, they select the new morph track and add a key.
When if they return to the Modeler pane and choose Undo, which of the following happens?
  1. The Undo is global, so the key they added to the morph track goes away.
  2. The Undo is local to the window, so the morph goes away. This causes the morph track to be removed from the Animator pane.
  3. They can't toggle between the Modeler and Animator, so the issue is moot. The Undo stack is cleared when the mode changes, so there's nothing to Undo.
  4. Something else?
dcuny
 
Posts: 2902
Joined: Fri May 21, 2004 6:07 am

Re: undo/redo behavior

Postby pndragon » Fri Feb 22, 2008 7:56 pm

dcuny wrote:Think of the following scenario:
  1. The user has two windows open - the Modeler and the Animator.
  2. While working in the Animator, they realize they're missing some morph target. They pop over to the Modeler and add the morph target.
  3. Back in the Animator, they select the new morph track and add a key.
You've made an assumption here that both windows are syncronized. Currently, Jpatch does not synchronize across separate windows. If a model is loaded into the Animator, to get updated info, it would have to be reloaded which would then carry all of the new undo/redo stack info with it. Logically, then, this:
When if they return to the Modeler pane and choose Undo, which of the following happens?

1. The Undo is global, so the key they added to the morph track goes away.
2. The Undo is local to the window, so the morph goes away. This causes the morph track to be removed from the Animator pane.
3. They can't toggle between the Modeler and Animator, so the issue is moot. The Undo stack is cleared when the mode changes, so there's nothing to Undo.
4. Something else?
doesn't apply in multiple window situation.
"We're so sorry, Uncle Albert,
But we haven't done a bloody thing all day."
--- Paul McCartney
pndragon
 
Posts: 591
Joined: Sun Dec 05, 2004 1:27 am
Location: North Carolina

Re: undo/redo behavior

Postby pndragon » Fri Feb 22, 2008 8:01 pm

I think I basically got all of that right. Sascha's going to need to check me. If you close the Animator then you will lose the morph track... at least that's the way it looks to me

--- Jim
"We're so sorry, Uncle Albert,
But we haven't done a bloody thing all day."
--- Paul McCartney
pndragon
 
Posts: 591
Joined: Sun Dec 05, 2004 1:27 am
Location: North Carolina

Re: undo/redo behavior

Postby dcuny » Fri Feb 22, 2008 9:48 pm

pndragon wrote:You've made an assumption here that both windows are syncronized. Currently, Jpatch does not synchronize across separate windows. If a model is loaded into the Animator, to get updated info, it would have to be reloaded which would then carry all of the new undo/redo stack info with it.

Yes, I'm aware of that. One of the options reflects that:
3. They can't toggle between the Modeler and Animator, so the issue is moot. The Undo stack is cleared when the mode changes, so there's nothing to Undo.

However, we've had discussions in the past of synchronizing the windows, so I figured I'd reopen the question and see if anything's been changed.
dcuny
 
Posts: 2902
Joined: Fri May 21, 2004 6:07 am

Re: undo/redo behavior

Postby John VanSickle » Sat Feb 23, 2008 2:59 am

What I was considering for my own work:

* Base all scene objects on the contents of the files, without worrying about whether that object is being edited.

or

* Give the Model object a method (and appropriate data) so that a Scene object can call it to be displayed. Then detect if a chosen model is currently being edited by the user; then ask the user if he/she/it wishes to base the scene on the file contents or on the current edit in the modeler. The advantage is that any changes to the model are immediately reflected in the scene if the user chooses to reflect the in-memory version of the model. Another advantage is that a large model would only need to be loaded once into memory. A number of issues with this approach require handling (what to do if the user quits out of the modeler without saving, etc.).

If scene objects are based on files, the scene editor should notify the user when the model files used are modified.
John VanSickle
 
Posts: 189
Joined: Sat Feb 16, 2008 2:17 am

Re: undo/redo behavior

Postby sascha » Sat Feb 23, 2008 9:48 am

When if they return to the Modeler pane and choose Undo, which of the following happens?

Finding an answer to this question is the purpose of this thread :)

Eventually there should be four levels of undo/redo:
1) When working on a model or animation, each edit can be un-/redone as usual.
2) "Layers" to support nondestructive editing.
3) When saving a file, JPatch keeps track of the old revisions, and you can at any time load an old revision (this is equivalent to Eclipse's local history feature). There's a maximum number of "history" files to keep on disk, e.g. 30. Loading a file will flush the undo stack (for that file).
4) Projects can be managed in a CVS or Subversion repository.

1 and 3 are implemented (although you can't access 3 at the moment). I'm sure that there are Java libraries for CVS and Subversion access, so 3 shouldn't be too difficult to implement. If all else fails, it could use external commandline tools.

I also like the concept of projects. A project is a directory structure that contains model files, animation files and some other resources (shaders, texture images, sounds, etc.). Only one project can be open at any time, but within the project, you can open multiple files (models and animations). Even if an animation file is not open, JPatch would scan over all files (or perhaps keep an extra file with project metadata) to know which models and which avars are used in which animations. So, when e.g. removing a morph from a model, JPatch would open a warning dialog, telling that this morph has associated motion-curves in some animations - the user can then decide to either not delete the morph, or to delete any references to it in the entire project. The same should be done with undo: If you first create a morph in a model file, then add some keys for it in an animation file and later undo the morph creation in the model, it should detect that and also popup the warning dialog.

To keep things simple, this should only work within a project. If another project uses the same models, you'll have to copy the files from one project dir into the other (of course JPatch could add something like an "import files from another project" wizard), but as far as Jpatch is concerned, these are different files. If you change the model in project A, it will not affect anything in project B. There will be no cross references between projects.

Once this is working, there could be one undo manager per file (model or animation), and they'd be independent. Note that an edit in a model could still affect animations though (as described above).

JPatch would have to handle "open" and "closed" files behind the scenes - from a user perspective all models and animations within a project are "open" - although JPatch would only load files into memory if necessary. Modified files would be marked with somthing like an asterisk (*), and when exiting or closing a project, all files would be saved. You can also choose to manually save one or all files to take advantage of the file history feature.

There are some problems remaining: How should a "rollback" (loading an older file from the local history) be done? There are two options:
If the old file does not contain certain objects (e.g. morphs) referenced from an animation file, the same warning dialog could pop up. But what if the user rolls back to an older animation file that contains tracks of objects (e.g. morphs) that have been deleted from the models in the meantime? I think it should also output a warning and let the user choose to either rollback the model files in question to (to the point where the morphs were still present) or to simply ignore the orphaned tracks and not load them.
Alternatively a rollback could be only possible on the project level - so you could rollback the entire project to some earlier state, but not individual files. This is a bit more inflexible though, I don't really like it.

There's still a safety net, so if all fails, JPatch would simply ignore any inconsistency by simple removing orphaned tracks to are bound to e.g. a deleted morph (and of course show a warning note). This would ensure that you can't "break" a project in a way that JPatch won't be able to open it anymore.

Thoughts?
sascha
Site Admin
 
Posts: 2792
Joined: Thu May 20, 2004 9:16 am
Location: Austria


Return to Development

Who is online

Users browsing this forum: No registered users and 2 guests

cron