FacebookTwitterGoogle+Share

Choreoheritance

This post was written by Karl.

Shaun is totally beating me at posting. I guess I’d better get on the ball. By the way, if you consider yourself a part of the GC community and want to be able to post, you should contact Brad on google talk.

So, I’ve been working on this thing, project name ‘Choreography’. It’s really just a utility — an interface into a part of a personal library I’m developing: ‘kdance’. The intent of kdance is to do for me everything I would ever need to do with skeletal animation. The problem is that it is a set of functions instead of a set of buttons. Choreography resolves this.

Unfortunately, in order to perform the function -> button transition effectively, one needs the arcane concept of a ‘gooey’ — a common device for interfacing with functions in a visual way. This device is the largest blocker between me and creating useful programs.

I really didn’t want to slowly create separate user interfaces for interacting with all the different various classes and capabilities of kdance, so I had a brilliant idea: I will extend the flex tree control to read from a declarative object that defines how to provide an interface for the various different classes. This changes the task of a lot of busywork coding, which I find relatively unpleasant, to the coding of just a few smart algorithms and the writing of a summary of all my classes in one place.

I did this, partially. At the moment one can walk through the object structure of what’s been loaded and view it all on the tree. I still have to figure out how to get one actually calling the functions, but at least now there’s an interface for picking something to work with. Part of the object for declaring Choreography’s interface looks something like this:

"k10.dance::IKeyFrame": {
	fields: { time: { label: "Time" } }
},
"k10.dance.variance::KeyFrame$Number": {
	label: "Simple Key Frame",
	fields: { value: { label: "Value" } }
},
"k10.dance.variance::LinearKeyFrame$Number": {
	label: "Linear Interpolated Key Frame"
},
"k10.dance.variance::QuadraticKeyFrame$Number": {
	label: "Quadratic Interpolated Key Frame",
	fields: { weight: { label: "Weight" } }
},
"k10.dance::Joint": {
	label: function(joint:Joint):String { return joint.name; },

 

As you can see, the object is keyed by the name of the class in question. Each class-name is associated with information on how to depict the class when it is encountered. As I implement the extension to the Tree control that reads this declaration, finding a class from a given class-name is a simple matter of calling the getDefinitionByName() function, which will successfully return the Class object associated with the fully-qualified name of any class that happens to have been linked in. Finding this and similar functions really saved the day for me.

A problem I ran across was inheritance. It’s pretty simple to use the ‘is’ operator to identify all entries in the declaration object that apply to a given actual Object at runtime, but sometimes duplicates need to be dealt with. For example, when an interface component is displayed it should only have one label, but an object whose class inherits from the LinearKeyframe$Number class will have two labels — the label “Linear Interpolated Keyframe” from LinearKeyFrame$Number, and the label “Simple Keyframe” from KeyFrame$Number, which is a base class for linear keyframes. The label which is shown should be the one lowest on the inheritance tree, but how to figure that out?

My first thought was simply to use the ‘is’ operator to test both of the potential classes with each-other, but this turns out to be a dead end because Class objects don’t inherit from each-other, they just inherit from Object. All right, then I guess I’ll have to instantiate each class with the new operator — but each constructor takes different arguments! I would have to trap errors until I found the right number of arguments, unless I wanted to bloat my descriptor object with unnecessary information, which I’m trying to avoid at all costs — and even then the constructor will probably fail because the arguments are not real.

I let this problem stew for some time. I left Brad a message on it — he said it was probably possible, but he didn’t seem to know how. Eventually I decided to grit my teeth and tackle it, and I started reading up on the describeType function, which returns a set of xml fully describing a given type. I figured I’d have to parse all the xml, map out the inheritance tree of each object and class, and plug them together. It’d be a relatively large task.

However, down in the ‘See also’ section of that documentation was a function that ended up being just what I needed. The getQualifiedSuperclassName function has a quirk hidden its documentation that makes these kinds of comparisons possible — when passed a Class object, rather than returning the superclass of the Class object, it returns the name of the class which the passed Class extends. Now all I have to do is walk the inheritance tree of any given object, and the first class that matches a class I’m aware of is the class lowest in the heirarchy it inherits from:

for (var sc:* = getDefinitionByName(getQualifiedClassName(obj));
     sc != Class && sc is Class && sc != testClass1 && sc != testClass2;
     sc = getDefinitionByName(getQualifiedSuperclassName(sc)));

 

Perhaps not the most efficient solution on the planet, but it’s UI code for a utility, so I don’t really care yet. Also, note this code does not allow for interfaces. I don’t need interfaces so I didn’t worry — but they might require a bit more work.

 

Comments

  1. Brad says:

    That is cool! We should get Shaun using actionscript 3?

    You are so much more dedicated to figuring these things out than I am. I am not a fan of reading docs. I hate them more than can be reasonably explained.

    Also, the last for loop code confused me until I started counting brackets. 😛

  2. k says:

    Hum, what would make that code clearer? Spaces around each getQualified*lassName() call? Dropping the final parenthesis to a fourth line?

  3. Brad says:

    haha i was prob just tired. If I was writing the code, i’d format it exactly as you have it? I might have done it like this, but probably not.

    for (
         var sc:* = getDefinitionByName(getQualifiedClassName(obj));
         sc != Class && sc is Class && sc != testClass1 && sc != testClass2;
         sc = getDefinitionByName(getQualifiedSuperclassName(sc))
    );
    
  4. Shaun says:

    @Brad
    I like AS3, I think its syntax is pretty.

    @k
    Ignore Brad when he complains about code. He just doesn’t like looking at other people’s code.

  5. Shaun says:

    Also Choreoheritance is an awesome psuedo-word.

You must be logged in to post a comment.