Sunday, August 14, 2011

GSoC11 - Numerically editing keyframe properties

One issue which I know has been bugging people for a while is about how the editing the "Active Keyframe" settings in the Graph Editor properties panel is a bit clunky, if not downright nasty to use.

Since I started writing this post, I have had (long) since committed a fix for these problems.

AFAIK, there are were 2 major problems UI-wise:
1) Handle updates don't occur, leaving things potentially outdated
2) Units displayed for the key values don't match up with the visual representation

At the time when I finally added this support, I'd only really added it as a kind of fine-tweaks refinement capability, for getting high-precision tweaks to keyframe positions which may have been tedious to have done interactively. For that, at least problem 1 would have generally been of negligible status.

What I didn't really expect though was an apparent eagerness to actually go in there and edit everything numerically via text boxes, typing in floating point values instead of visually transforming, and using this as the primary (!) way of tweaking keyframe timing and/or values in the Graph Editor. Weren't artists supposed to be a pretty visual bunch?! Insights into why this seems to be such common (?) practice are welcome :)

So why do did these problems exist, and can we do anything about them?

All of these troubles are in one way or other rooted in how the RNA system works, with PointerRNA's only knowing about the source ID block and a pointer to some nested struct reachable from that source ID block. As it turns out, in quite a few places, we have data which is nested several layers deep, but usually within that nesting, there are a few levels which are particularly "important" when it comes to operations such as performing updates -  i.e. F-Curves in the ID -> AnimData -> Action/Drivers -> FCurve -> Keyframes hierarchy. In this particular case, we're dealing with PointerRNA for keyframes not "seeing" the F-Curve they belong to.
As an aside:
- Personally, I have a tendency (*) to prefer defining such search/access tree nodes with parent backpointers where possible, as it avoids problems like this at the cost of perhaps a bit more fussiness when editing links and extra (though relatively inexpensive when compared to the time lost time performing repeated needle-in-haystack searches to work around the lack of this information) memory cost.
- That is, unless faced with a one-off, coding-time-limited situation, in which case the tediousness of the extra link fixing may prove to be too much trouble for a few operations. Then again, I do still have to keep reminding myself that "appropriate data structures make the job easier", by allowing ready access to important stuff. Hence, "use as little memory as possible" should really be "use as little memory as necessary" ;)
Anyways, the limitations of PointerRNA's is a problem here (much like it is when trying to construct data paths for F-Curves, i.e. for animating properties of FModifiers, we have such a problem, hence one of the reasons we don't have that yet, but that's really for another day) as the UI code for those buttons used to be using the 2.5 layout-engine version of the widgets. Now, as anyone who's tinkered with the layout py files will know, these widgets use PointerRNA's + a property identifier to automatically determine what property that widget should affect. As such, any changes made using these widgets can only use the RNA-based updates and setters defined for that property, which are limited to affecting only the things that they can "see" by looking at the PointerRNA.

Here lies our problem. For keyframes to update properly after they're edited, some F-Curve level functions need to be run to validate their handles within the context of the greater F-Curve (i.e. taking into account the keyframes surrounding one that has been edited). But due to the limitations mentioned, we cannot obtain the parent F-Curve to run these operations on.

Similarly, for the keyframe values to display in terms of the units that the property affected by the F-Curve that they're defined for (e.g. rotations in degrees for rotation F-Curves), we need again need the F-Curve but this time to resolve the property that the keyframe affects, and hence figure out the unit type for that property.

The solution
To solve these troubles, I eventually decided on using a slightly "lower-down" UI widget set, which was the one that the layout engine used internally. By doing so, I could attach additional callbacks for modulating the way that the widget behaved, such as overriding the units displayed for values and also attaching update callbacks which knew about the F-Curve in addition to the keyframe.

1 comment:

  1. Hello Aligorith,
    to try to answer your question about using numerical input to keyframes to animate is imho mostly when doing mechanical (ie robotic) movements.
    Imagine a complete production line with many robots (conveyors, arms, machines etc) that need to be timed in sync.