Monday, December 22, 2008

Oh, Devoxx!

Oh, Devoxx!
or
Odevoxx
(An Ode to Devoxx)

In December, Antwerp's miserable,
With wind and rain both terrible,
But beer and fries flow every night,
So, in the balance, it works out right.

Though changing names caused such pains
The geeks still flock and the show remains
The hottest conference in the coldest place
(Now, to thaw out my frozen face).

Devoxx is the place to be,
Especially if you're in BE,
Where talks on Java, Flex, and such,
Are thankfully given in English, not Dutch.

I was over in Antwerp a couple of weeks ago, giving various talks on Flex and related technologies at the Devoxx conference (formerly named JavaPolis). This conference is always fun, with lots of great talks and speakers, so that we all manage to forget how awful the December weather is in northern Europe and enjoy ourselves.

All of my talks were recorded and will eventually be posted to Parleys.com, but in the meantime, I'll post the slides in case you're interested.

The first talk I did was on Effect-ive Flex (apologies to Josh Bloch for shamelessly ripping off his Java book title for a completely different purpose). I wasn't planning on giving this talk at all, but due to a last-minute cancellation on Monday afternoon, Romain and I found ourselves giving surprise talks on Android and Flex. MAX attendees may recognize some of this content from the presentation I did there, although this one was focused purely on Flex3 and Gumbo effects.

Anyway, here is the PDF of the presentation.

Stay tuned for other slides; I'll dribble them out one by one over the next few weeks. And I'll post pointers to the parleys.com videos as they are posted; the videos combine shots of the speakers, the slides, and the demos. And by the way, the Parleys player is an awesome Flex/AIR application!

Wednesday, November 12, 2008

min(MAX)

Besides furiously hacking new effects logic for future Flex releases, I'm also busy getting ready for the Adobe MAX conference next week. I'll be giving a talk on Monday evening, so if you're interested in effects, and in 'Filthy Rich Clients' capabilites of Flex in general, c'mon along to that talk. But there are several other talks that should also be interesting to Flex developers that want to know where we're taking the platform and how to take advantage of rich graphics capabilities of Flex.

Note that I'm not trying to highlight all of the cool and useful talks at the conference: there are plenty of other talks on Flex3, introductory and advanced, and other related technologies such as Flash player 10 and new graphics capabilities such as Pixel Bender. I'd encourage you to check out the conference schedule for those other talks.

Here is a [small] sampling of the talks that I'm interested in attending (especially "Filthy Rich Flex Clients". It would be tacky to miss my own talk):

Monday/Tuesday Keynotes: These keynotes should cover a lot of interesting and related technologies. These should be a great place to see what's coming, and what we'll be talking about in greater detail in some of the sessions and labs.

Monday 11:30-12:30: Looking Ahead to the Next Version of Flex
Ely Greenfield (Flex architect) will be giving an overview of the new component model we've been working on, along with a host of other improvements and new features. This should be a great place to get a quick take on what the next version will do for you.

Monday 3:30-4:30 (or again at 5-6): Introduction to Thermo and the Next Generation of Flex
Steve Heintz (Flex tools Product Manager) and others will be previewing and discussing early versions of Thermo and FlexBuilder. For folks interested in these tools, this should be a great talk. But go to the 3:30 one, because you'll want to save your 5:00 slot for...

Monday 5-6: Filthy Rich Flex Clients
This is my talk. Actually, it's about three talks in one. The main focus of the talk is the idea of 'Filthy Rich Clients', which are techniques that you can use to create better user experiences. But along the way, I'll be talking about the graphics and animation capabilites of the current platform (Flex 3), and also covering some of the new Effects capabilites that I've been working on for Gumbo. Since I only have an hour, I may need to either move very quickly or simply give different sections of the talk in parallel (I haven't worked out how to do this yet, but it will probably involve a <Parallel> tag and a new <FxChetPresenting/> effect). Either way, it should be a lot of information, with lots of demos, code, and whizzy stuff.

Tuesday 1:30-2:30: Creating New Components in Flex 3 and Beyond
Deepa Subramaniam (Flex SDK team engineer) will be talking about how to create custom components, including showing how to do it with the new, more flexible component model we have in Gumbo.

Hope to see you next week at the conference...

Monday, November 10, 2008

Video: Custom Flash Graphics

Adobe TV has posted my final Flex in a Week video. This one is on the use of Flash graphics objects in Flex applications, covering both drawing directly into the Graphics object for custom rendering in your updateDisplayList() method as well as creating and adding Flash shapes to your components. (Note that you have to have a completely cusom component, subclassing UIComponent, in order to add non-component children such as DisplayObjects or Sprites to the child list of your component; other Flex components only understand Flex component children).

It's not terribly advanced, but is meant more as an introduction to some of the graphics basics, including Flash's use of retained mode rendering and why you always need to clear() the graphics display list.

The source code for both examples is available here.

Monday, October 27, 2008

Flex Specific[anim]ations Posted

We've just posted a couple of specifications on the Flex open source site about the new animation work going into the next release of Flex ('Gumbo'). Check them out if you're interested, and give us feedback if you have any suggestions (on the forums if you'd like, but I'd be happy with comments on this blog as well, since I'm all about the new effects for this release).

The New Effects spec discusses the new animated effect base class, FxAnimate, along with several subclasses that implement the new effects. Many of these effects should look familiar to those already using Flex (FxMove looks an awful lot like Move, FxResize is strangely similar to Resize, etc.). The main change is that the new effects will act on both components (like the old effects) as well as the new graphics elements (which the old effects don't handle, as discussed in the spec). In fact, targets of the new effects could be any types of objects at all, although many of the effects do assume particular public properties (such as FxMove assuming that the target has "x" and "y" properties). For example, FxAnimate can be used to animate an arbitrary set of properties on the target, with no assumption of what type it is.

There is also a fairly big change in the Effect internals, in the way that FxAnimate actually changes properties, and in its use of the new Animation class, which is discussed in ...

The Arbitrary Type Interpolation spec covers the new Animation class, which is basically a rewrite of the old Flex Tween class. It changes the way that time and value are eased (splitting the old 'easing' process into separate time-interpolation and value-interpolation steps), which allows both more flexibility and the ability to animate properties that are not simple Numbers. One example of this capability is seen in the new ColorInterpolator class, which is used in the new FxAnimateColor effect covered in the first spec above.

Intrepid readers will notice that these specs, and the underlying code, bear a remarkable resemblance to the Flexy library posted here a few weeks ago. Barring some name changes since then and other small API tweaks, the functionality is exactly the same. The capabilities exposed in Flex are greater than those in Flexy, in particular at the higher 'effects' level, because Flex can perform more work when it knows what it's animating (so FxMove can query the state-transition system to automatically derive from/to values, for example). But the underlying classes (especially Animation and the interpolation and easing classes) are the same between these projects.

Please check out the specs, check out the code, play with it, and let us know what you think.

Note that the APIs, and the specs, will change over time to reflect more API modifications and additions. But the essential ideas of the effects architecture are in there and ripe for building upon.

In the meantime, I'll be working on more functionality for the effects system. Ideally, I would be writing some blogs and demos about all of the new stuff now, but I'm focusing on getting stuff working for MAX now and can't take a break for demoing the goodies just yet. They call them deadlines for a reason (make it to the finish line or you're dead). I'll definitely have some stuff to show at MAX in late November, however, and also at Devoxx in Antwerp in December, and will certainly get around to posting some blog entries on it after that.

Wednesday, October 22, 2008

Video: Glow For It

Here's another Adobe TV video in the Flex in a Week training series. This one's about using Flash filters in Flex applications. Specifically, I discuss instantiating and animating a Glow filter on a button, but the techniques discussed are applicable to using filters in general. The source code is fairly self-explanatory, but feel free to grab the two files I cover in the video: GlowingButton.mxml and AnimatedGlowingButton.mxml. Glow ahead, check it out!

Wednesday, October 15, 2008

Video Time

Here's another video to watch, on some of the details about using the Flash Timer. I shot this video as a part of a just-posted series for the Flex in a Week training shows. This is the same topic and application that I cover in my earlier Time's Up blog entry, but in video form. It's so much more dramatic this way. And video seems so much more fitting for the material. I'm talking about time, after all, so why not cover the topic using time-based media? It was either this or a song, and I thought I'd save you that torture.

Wednesday, September 24, 2008

And Now, a Word from Our Sponsor

Codedependent is my technical blog, where I focus purely on geeky things. No idle chit-chat (or chetchat, even), just code, demos and technical stuff. But I may occasionally break ranks to mention something when I feel it's potentially interesting to folks reading this blog. Today, for example, I feel compelled to mention that I've just published a book of material pilfered from my humor blog, called When I am King.... The book is a collection of articles that I've been writing for the past couple of years. I haven't actually ascended to the King's throne yet, but at least I haven't been throne out, either. In any case, I have been able to write about the matter instead, which is probably more enjoyable (for everyone). Anyway, if you're interested in another side of my writing, check it out. Now, back to our regularly scheduled technical programming...

Monday, September 15, 2008

Video: Sorted Details

I shot a video tutorial recently of my Slide Sorter demo (which is also covered in this earlier blog entry), and it just got posted on Adobe TV. Here it is:
To see the running demo in action (which is better than a static picture of the demo, which would be the demo inaction), as well as the source code, see my earlier Sorted Details blog entry.

Tuesday, September 9, 2008

Flexy: A Flexible AS3 Animation Library

I'm currently working on new animation capabilities for the next release of Flex. We have some great stuff in there already, such as the effects infrastructure and state transitions, but I'm rounding out the functionality and rethinking some of the APIs to make the system more powerful and usable by both tools and developers.

For my effects work, I'm mostly concerned about Flex developers because, well, that's what the library is for.

But it occurred to me, through conversations and through random web perusals, that an animation system that could also be used outside of Flex might be interesting to the people doing ActionScript3 applications without the Flex framework, or to people doing both Flex and non-Flex AS3 programming. It also occurred to me that much of my code was Flex-agnostic. Of course, the code that is tied to Flex effects and transitions is necessarily dependent upon Flex, but the core animation engine really doesn't care what it's animating or how; it's simply varying values over time.

So I took a pass over the code to make it truly Flex-less, which involved removing a total of like 5 lines of code from my existing classes. I also added a class called ObjectAnimator, which is a simplified version of the new Flex Animate effect class.

But probably more interesting to the people reading this, I am putting out the results here as something for people to try out.

Caveat: (I love using the word Caveat. For one thing, it's a great hedge. But also, it's the only Latin word I know and surveys show that people who speak Latin are smarter. Except the Romans, when they let their city get overrun and their empire trampled) This is not a released product, either from Adobe or from me. Instead, it's a technology demo that shows you what the new Flex animation system does. My main focus is, and should continue to be, making Flex animation rock, which means that I can't spin up effort supporting an animation library that's a separate entity. Having said that, the code that I'm delivering here is about a 99% (taking a complete guess because I don't want to bother diffing and counting lines of code) copy of the code in Flex, so it's not like this is just some random code project spew.

There are various reasons I'm doing this:

  • Nice guy: This is just another example of what a great guy I am. A real pal. Your source for fun code and good CPU times.
  • Feedback: I'd like to get people, especially animation-savvy developers, interested in what I'm doing for Flex and giving me some feedback on things they think I'm missing, or things they would also like to see, or problems that they are having. I don't want Flex to be a constraint that makes it hard for those people to check out the animation code and play with it.
  • Share the wealth: If the stuff I'm doing for Flex can be useful in other Flash contexts, great! As I said, my main focus needs to be Flex. But if I'm doing generic work that can be used elsewhere in the Flash world, that's okay by me.

Demos are usually good, so here's one:

And here's the source code. The demo was built in Flash authoring (although you should be able to get the same result in either Flex or pure AS3 programming - I just used Flash as a proof-of-concept for this non-Flex animation library). The creation of the ball and button aren't obvious from the code because I did it in the authoring tool, but they aren't the main course of that demo anyway, and it would be easy to write something similar purely in code.

I was about to post this article, but then I realized that maybe you'd be interested in what the animation library actually does. Here are some quick explanations, but feel free to check out the demo code and the ASDocs for more concrete details:

  • Property Animation: Animating one or more properties on a given object for a set duration. This means calculating the in-between values and setting them on the object directly.
  • Arbitrary Type Interpolation: Animation of properties of any type: as long as you supply an IInterpolator implementation to the animation it can figure out how to calculate the in-between values. For example, there is a ColorInterpolator supplied with the library that handles per-channel interpolation for RGB uint values.
  • Easing: There is a simple IEaser interface with several built-in implementations to enable various linear and non-linear time easing calculations, providing for more realistic animations. For example, the Bounce demo above uses a cubic Power ease that accelerates for the full duration down.
  • Repetition. I repeat: Repetition: Animations that automatically repeat, either looping or reversing, for specified numbers of times. For example, the Bounce demo specifies a repeatCount of 2 that reverses each time to get that in-out behavior of both the bounce-down/up and the squish out-in effect.
  • Size Matters: The library is 10k. 10k! That's like less memory than Bill Gates thought anyone would ever want on the PC! Less than the word count of this blog (if I'd repeated each word 10 times)!
  • Multiple Leves of Control: ObjectAnimator exists to make creating animations even easier; you don't have to handle the animation events, but rather just tell it what properties to animation on which object between which values, and ObjectAnimator does the rest. But if you'd like to get your hands dirty, it's not that much more work to create the Animation object directly. Here's source code for an ObjectAnimator-less version of the same demo. Note that the steps to creating the animation are just about the the same as in the other version, but that we also have to set up and handle the update events in order to actually move the target object.

In the future, I may try to integrate this side-project more closely with Flex. For example, it's possible to build parts of the Flex framework (or any Flex project) as a separate library. For now, just to expedite getting this out there, I copied the code and created this project in Flash (that also helped me ensure that there were no Flex dependencies). But stay tuned to see if/when I start using the same source/build base for this project as for the rest of my Flex work.

So take the library. Try it out, with Flex projects and non-Flex projects. Check out the ASDocs. Check out the demo code. Check out the Flex opensource site for the actual code (only ObjectAnimator does not exist in Flex and most of that is culled from the Animate/AnimateInstance classes). Let me know how it works for you and what else you'd like to see in a core animation package.

Oh, and tell your friends what a nice guy I am.

Friday, September 5, 2008

Presentation: Filthy Rich [Flex] Clients

The video for my recent presentation at the 360Flex conference was just posted. Here it is for your viewing pleasure. Grab some popcorn, turn down the lights, and settle back in your chair for this action-packed Flex thriller:

Thursday, August 28, 2008

Search Me: Code for SearchTransition

Finally, here is the source code and demo for SearchTransition, an application that demonstrates animated transitions that's shown in this video that got posted a few weeks ago. First, let's see the demo: And here's the source code. Note that it's just the one file: no image assets, no other ActionScript code; it all works by simple MXML code and use of the standard Flex effects. I'll leave the explanation of the code and application to the video, so check that out if you haven't seen it yet. Enjoy!

Tuesday, August 5, 2008

SpringItem

Someone asked, in the comments to my Springtime posting, how that effect could be generalized to apply to DataGrid items. I came up with this simple demo to show the same effect at work for each item in the grid:

The only change I needed to make to the underlying Spring effect was to make it deal with IUIComponent, instead of UIComponent. The original demo only works on components. In the case of DataGrid items, however, we have item renderers, which are not UIComponents, but which do implement the IUIComponent interface. So I changed the code in SpringInstance accordingly, modifying UIComponent to IUIComponent and changing a call to componentToGlobal() to localToGlobal() (to get the mouse coordinates in terms of the global location) instead, and voila, it's a beautiful, springy data grid.

I had to make one change to my default DataGrid component to make it look reasonable: I centered the text in the cells with the text-align="center" style attribute. Without this change, it looked like the spring effect was happening off-center, just based on the default position of the text on the left that would then go shooting further to the left as it sprang out. Also, I enabled double-click events and added the spring effect based on double-clicking on an item, grabbing the itemRenderer from the event as the source for the effect.

The abbreviated MXML code for the application is here:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:effects="effects.*" layout="absolute" width="600">
    <effects:Spring id="spring" duration="500"/>
    <mx:DataGrid textAlign="center" width="100%" doubleClickEnabled="true"
        itemDoubleClick="spring.play([event.itemRenderer])">
        <mx:ArrayCollection>
         <mx:Object>
            <mx:Label>Thunderbird</mx:Label>
            <mx:Price>$2.99</mx:Price>
            <mx:Rating>8</mx:Rating>
            <mx:Notes>Hint of cough syrup</mx:Notes>
         </mx:Object>
         <!-- other items omitted for brevity... -->
      </mx:ArrayCollection>
    </mx:DataGrid>
</mx:Application>

The code for the effect itself can be obtained in the zipped source files.

I expect a similar approach could be taken with other list-based controls, although the details of doing so are left as an exercise for the reader (I love when books do that, implying that it's easy an obvious, but really it's just an indicator that the author didn't get around to trying it themselves yet).

Thursday, July 24, 2008

Video: SearchTransition

The video for my SearchTransition demo has just been posted on Adobe TV:
Check back in a few days for the source code to this demo (dangit, more code to go clean up...).

Friday, July 18, 2008

Springtime

Here, finally, is the source code for the Spring effect seen in the video posted on Adobe TV last week. I took my time posting it because I didn't want to spring it on you too suddenly. Or maybe it was the wrong season (not the winter of my discontent, but the summer of my abject laziness). Or more probably, I just had other stuff to do, like code to actually write and check into Flex 4.

But also, tink brought up some good points in the comments to that blog that I wanted to look into and it turns out he was completely correct; there were two problems with my previous version of Spring, as shown in the video. Both are related to memory, although one is more significant than the other.

The first, and most important, issue is that I should have dispose()'d the BitmapData object, in order to release the memory that it had allocated for the bitmap copy of the component. You might think (as I did) that this would be automatically released when its referring objects go away, but that's not the case. Perhaps this is because the BitmapData object can be shared among multiple objects (as in the case of the Bitmap object, which I now use for the sprite), so it's more difficult for the Flash engine to tell when it's not needed anymore.

In any case, you should dispose() a BitmapData object when you no longer need it, particularly in a case like the Spring effect, where we create a new one every time we play the effect. If you bring up TaskManager on Windows, you can see the memory leaking away in nice 64k chunks, which makes sense given that the icons are 128x128, and a bitmap is typically 4 bytes/pixel, so that's 65,536 bytes, or 64k.

Note, however, that you have to wait until you don't need the BitmapData any longer. This is really only the case when the effect ends, as it is used as a source for the drawing while the effect is playing. So the new version of the code declares bmData as an instance variable and calls dispose() in onTweenEnd(), after we're done with it:

        override public function onTweenEnd(value:Object):void
        {
            super.onTweenEnd(value);
            component.stage.removeChild(springer);
            // dispose of the bitmap to release its memory
            bmData.dispose();
        }

The second change issue in the code is much more minor, but could be important in applications that use many more objects than this one. I chose, for no particular reason, to use a Sprite object to hold my bitmap data. But a Sprite is more than I need. All I really want is an object to hold the BitmapData; a Bitmap object is much simpler (both to create and in functionality). So I've changed the code to use that instead of Sprite:

        springer = new Bitmap(bmData, "auto", true);

Check out the source code, or play the demo for yourself:

Thursday, July 10, 2008

Spring is in the AIR?

Adobe TV has posted another of my 'filthy rich' videos. My favorite part of this one (at least on the Adobe site - not sure if it holds true for the embedded version below) is the still frame in the thumbnail on the Adobe TV home page. I look like I'm holding up a field goal for the extra point in a game of paper football.
In this video, I show the "Spring" effect, with a Flex app that I wrote based on an original Java application by Romain Guy. Check out the video for now, and I'll get around to sanity-checking the Spring source code and posting it Real Soon Now.

Wednesday, June 25, 2008

MAX Out

The MAX 2008 conference schedule and details are posted: http://max.adobe.com/na/experience/. I'll be giving a talk on "Filthy Rich [Flex] Clients," just one of many talks on Flex and other development goodies. Check it out and join us November 16-19 in San Francisco (non-US MAX conferences to follow later) to hear about Flex, Flash, tools, and all that designer/developer stuff.

Thursday, June 19, 2008

Pulse of the Geek Nation

You saw it on Adobe TV, now you can see it in code....

Here is the running demo and source code for the Pulse application seen on the Developer channel of Adobe TV.

Rather than spend time here reiterating on what I said on the video, I'll just refer you to the video to get the motivation for the effect as well as the code walkthrough.

The Video

In case you didn't manage to make it to Adobe TV, here's the video presentation for Pulse:

The Demo

And here's the running demo itself:

The Code

And finally, here's the source code. I'm tempted to put snippets of the source code inline and go over them, but I would be basically duplicating what I already did in the video, so watch the video instead.

Thanks to Romain Guy for the original (Java) version of the demo, which he wrote for our book.

Tuesday, June 10, 2008

Video: Pulse

Adobe TV surprised me today by posting my Pulse demo much sooner than I thought they would. Four videos posted in just over a week. I feel like Ben Affleck, where everyone will probably get sick of me when I'm just getting started. Except that Ben has a way bigger jaw and whiter teeth. And I bet he can't program graphics applications. This demo is the first in a series of "Filthy Rich Client" applications, where I look at what it would take to enable specific graphical or animated effects on the Flex platform. The Pulse demo in particular is a Flex take on an application originally written in Java by Romain Guy. The only problem is that I wasn't quite ready to post the source (just have to go through it and make sure it's ready for leaving the nest: nicely commented, teeth brushed, lunch packed...). So watch the video for now and check back here soon if you're interested in seeing the source code and application. By the way, this kind of content is also being posted on the Flex Developer Center, so you might also put that site on your list of Flex learning bookmarks.

Friday, June 6, 2008

Video: Top Drawer (Parts II & III)

Adobe TV has posted the second and third installments of the Top Drawer videos that we made, showing how we turn mouse events (Part II) into graphical shapes (Part III). Be sure to also check out my original Top Drawer postings (here, here, here, and here) that link to the source code for the project.

Monday, June 2, 2008

Video: Top Drawer (Part I)

The folks at Adobe TV have been shooting some videos recently of Adobe people doing quick techy presentations and these are starting to get posted on the Developer area. I've done some episodes on "Top Drawer" (the same application that was the subject of some of my earlier blogs here, here, here, and here) and various "filthy rich" effects. My videos should be rolling out on the Adobe TV site in the next few days and weeks. I'll link to the videos as they get posted. For now, check out Top Drawer, Part I.

Tuesday, May 27, 2008

Splinterview

Sorry, nothing technical today. Lots of stuff in the works, but nothing ready. Instead, here's a tardy link to an interview I did with the JavaOne folks a couple of weeks ago: http://java.sun.com/javaone/sf/2008/articles/rockstar_chethaase.jsp By the way, "Rock Star" is a term that that conference uses to refer to speakers whose sessions were rated highly in previous years. Either that, or they were referring to my bad hair, sullen disposition, and penchant for destroying hotel rooms.

Monday, May 12, 2008

Properties: A Matter of Privacy

I'm going to take a weird tangent today and talk about something non-graphical. Odd, I know, and perhaps a bit disturbing. Like Britney Spears writing a book about effective parenting. Or a politicians working on legislation instead of their re-election campaigns. Nevertheless, I feel compelled to discuss this language feature thatI encountered into it in my recent transition to writing ActionScript3 code.

For someone coming from the Java world, the way that properties work in ActionScript3 requires a mental shift. AS3 properties are pretty cool, but they encourage a different pattern of programming that's new to me, so I thought I'd spend a few words on it for anyone that is also new to properties.

The Ways of Java

In Java, the general rule is to make the properties (or variables) of a class private, preserving encapsulation (if I were a better Java acolyte, I'd quote you the chapter and verse where Effective Java discusses this pattern). If you need to expose the properties through public or protected API, you should create a setter/getter for the property with the appropriate access. For example, instead of this:

    public class Blah {
        public Freen freen;
    }

you would typically do this:

    public class Blah {
        private int freen;
 
        public void setFreen(int newVal) {
            freen = newVal;
        }
        public int getFreen() {
            return freen;
        }
    }

Callers of the API would then call setFreen() and getFreen() instead of referring directly to the instance variable freen.

One good reason for taking this approach in Java, even for simple situations where the getter/setter do nothing besides getting/setting the value, is to make the API future-proof. That is, suppose you decided to track the number of times that freen is set. In the first example above, there is no obvious way to do this other than adding new API that callers must now use. But in the second example, all we have to do is add the necessary logic to our setter function, and we're set:

    public class Blah {
        private int freen;
        private int numSets;
 
        public void setFreen(int newVal) {
            freen = newVal;
            numSets++;
        }
        public int getFreen() {
            return freen;
        }
    }

Notice, in particular, that there is no change to the public API for Blah to add the functionality of incrementing numSets; we buried that new functionality inside the existing API that your users are already calling.

Taking Action[Script3]

Now consider the case of ActionScript3. You could take the approach above and implement setFreen() and getFreen() exactly as we've done in the Java code, modulo differences in how types and functions are declared:

    public class Blah {
        private var freen:int;
 
        public function setFreen(newVal:int):void {
            freen = newVal;
        }
        public function getFreen():int {
            return freen;
        }
    }

But this would be silly because you'd be missing out on all of the advantages of the built-in Property capabilities of the language. It would be like going to France and continuing to eat McDonalds every day because that's what you eat at home. Sure, it does the job, but there are probably better and more interesting ways of accomplishing that task in the new place. Besides, their ketchup tastes weird.

In ActionScript3, there are two special qualifiers on functions, set and get, that make dealing with properties much more flexible. When you declare a function with set as a qualifier, callers call that function by simply specifying the name of it as they would any instance variable. For example, given this class defintion:

    public class Frong {
        private var _glar:int;
 
        public function set glar(newVal:int):void {
            _glar = newVal;
        }
        public function get glar():int {
            return _glar;
        }
    }

callers would set and get the value of _glar like this:

    var frong:Frong = new Frong();
    frong.glar = 5;
    var someInt:int = frong.glar;

So how does this factor into our discussion of private/public API? Because we can migrate our class over time to use public properties and public setters/getters as appropriate, without affecting the API. Let's take the original example, where we simply wanted a public variable freen:

    public class Blah {
        public var freen:int;
    }

Now suppose, as before, we want to add a mechanism to track the number of times freen has been set by the caller. We can change the above code to the following:

    public class Blah {
        private var _freen:int;
        private var numSets:int;
 
        public function set freen(newVal:int):void {
            _freen = newVal;
            ++numSets;

        }
        public function get freen():int {
            return _freen;
        }
    }

This is almost like our earlier ActionScript example where we defined setFreen() and getFreen() functions, but here the function names are just the name of our original variable, freen. But the really important thing is that our public API did not change. We were able to go from a public variable, freen, to a public setter/getter pair for that variable with no perturbation in how the callers of our API interact with that API. With either approach above, callers would do the following to get and set freen:

    var blah:Blah = new Blah();
    blah.freen = 5;
    var someInt:int = blah.freen;

Why It Matters

The whole reason for this post, and perhaps for this capability in the language, is that ActionScript3 developers can just think in terms of properties in their API, instead of in setters and getters for those properties. Developers can, and probably should, use the more terse declaration of public properties, versus the Java approach of public setters/getters, knowing that if more functionality is needed at set/get time, it can be done without changing the API that callers use.

p.s.

As Josh pointed out in the comments after I first posted this entry, it is not possible to override a variable with a function in a subclass. So if you are developing an API and expect or want your developers to be able to modify the behavior of a property via set/get, then you should declare your properties via the set/get functions.

Monday, April 28, 2008

Sorted Details

Q: What did the layout manager say to the anxious component?
A: Contain Yourself!

Q: Is it possible for components to be arranged without a layout manager?
A: Absolutely!

Q: How did the component feel about its first animated layout?
A: It was a moving experience.

Q: What do you call the animation objects owned by a component?
A: The component's personal Effects.

Q: Why is an animating container a risky real estate investment?
A: It's a transitional area.

Motivation

I've been playing around with animation and layout recently, and I wondered how I could combine the two. That is, when a container changes state and wants to re-layout its children, wouldn't it be nice if it could animate that change instead of simply blinking them all into place?

Suppose you have a picture-viewing application, where you're looking at thumbnails of the photos. Sometimes you want them to be quite small so that you can see more pictures in the view. But sometimes you want them to be larger instead so that you can see more details in any particular picture. So there's a slider that allows you to dynamically control the thumbnail sizes.

By default, in this application as well as other real applications that it's modeled on, changing the thumbnail sizes causes the application to re-display itself with all of the changed thumbnails in their new location. But what I want is for the change in size to animate, where all of the thumbnails will move and resize to their new location and dimension.

It turns out that this is not that hard to achieve, using the built-in effects of Flex.

Application: SlideViewer

First, the application (photographs courtesy of Romain Guy):

Usage: Move the slider around, which changes the sizes of the pictures. See the pictures move and resize into their new locations.

So how does it work?

How It Works

The application uses the built-in Move and Resize effects of Flex on each of the pictures. These effects, when combined in a Parallel effect, do exactly what we need - they move and resize their targets. But there are a couple of additional items that you need to account for in order for it to work correctly for our animated layout situation.

For one thing, you have to disable automatic updates to the container of the pictures during the animation. If you don't do this, the container will be trying to lay the pictures while you're trying to move them around. It's like watching an inverse of that video game anomaly, where your screen is having a seizure watching you.

Another trick is one that Flex transitions use internally; we first record the properties of all of the animating objects at their start location, using the Effect.captureStartValues() function, then we change the thumbnail sizes, force a layout to occur,l and run the effect. This approach causes our effect to automatically animate from those cached initial values to the new values that our container has imposed on the resized images.

The Code

There are three source files for this application (downloaded from here):

  • Slide.as: This class handles the rendering of each individual photograph on a custom background.
  • SlideViewer.as: This subclass of the Tile container loads the images and creates Slide objects to hold them.
  • SlideViewer.mxml: This is the main application class, which arranges the basic GUI elements and creates and runs the layout animation when thumbnail sizes change.

Slide.as

This class is a simple custom component that acts as a container for the Image object created from each photograph. It also adds some custom rendering to get a nice slide-like background for the image.

First, there is a simple override of addChild(), so that Slide can cache the Image object that it is holding. This caching is not necessary, since we could retrieve this object from the component's children list at any time, but it seemed like a better way to cache a commonly-access object:

    override public function addChild(child:DisplayObject):DisplayObject
    {
        var retValue:DisplayObject = super.addChild(child);
        image = Image(child);
        return retValue;
    }

The real reason for the custom subclass is the custom rendering that we perform to get the fancy gradient border for each image. This happens in the updateDisplayList() function. The first part of the function is responsible for sizing and centering the contained Image object appropriately. The remainder of the function handles the custom rendering to get our nice gradient border:

    override protected function updateDisplayList(unscaledWidth:Number,
                                                  unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);
        // Set the image size to be PADDING pixels smaller than the size
        // of this component, preserving the aspect ratio of the image
        var widthToHeight:Number = image.contentWidth / image.contentHeight;
        if (image.contentWidth > image.contentHeight)
        {
            image.width = unscaledWidth - PADDING;
            image.height = unscaledWidth / widthToHeight;
        }
        else
        {
            image.height = unscaledHeight - PADDING;
            image.width = unscaledHeight * widthToHeight;
        }
        image.x = (unscaledWidth - image.width) / 2;
        image.y = (unscaledHeight - image.height) / 2;
 
        // Draw a nice gray gradient background with
        // a black rounded-rect border
        graphics.clear();
        var colors:Array = [0x808080, 0x404040];
        var ratios:Array = [0, 255];
        var matrix:Matrix = new Matrix();
        matrix.createGradientBox(width, height, 90);
        graphics.beginGradientFill(GradientType.LINEAR, colors,
                null, ratios, matrix);
        graphics.lineStyle(.04 * width);
        graphics.drawRoundRect(0, 0, width, height, .1 * width, .1 * width);
    }

SlideContainer.as

This class creates all of the Slide objects that will be in the view and also handles later resizing events due to movement of the slider component.

The images are added to the application through the addImages() function. This function uses a hard-coded list of embedded images. The original version of this application was written with AIR, and I used the new File APIs to allow the user to browse the local file system and load images dynamically. That's important functionality, and it's preferable to the hard-coded approach below, but it's also orthogonal to the effect I'm trying to demonstrate here, so I dumbed-down the application to just load in some known images instead. Perhaps in a future blog I'll release the code and app for the AIR version. Note also that I'm only using embedded images here to simplify things for the weblog; it's easier to deploy this thing standalone (with embedded images) rather than having it refer by pathname or URL to images that live elsewhere, just because of the limited nature of this blogging engine (Grrrrr. Don't get me started...).

addImages() iterates through the list of embedded images (stored in the bitmaps[] array - check the source file for details), creating a Flex Image component for each one with the bitmap as the source property, and then creates a containing Slide object for each resulting Image.

    public function addImages(event:Event):void
    {
        // Create an Image object for each of our embedded bitmaps, adding
        // that Image as a child to a new Slide component
        for (var i:int = 0; i < bitmaps.length; ++i)
        {
            var image:Image = new Image();
            image.source = bitmaps[i];
            image.width = slideSize;
            image.height = slideSize;
            image.cacheAsBitmap = false;
            if (!slides)
            {
                slides = new ArrayCollection();
            }
            image.addEventListener(Event.COMPLETE, loadComplete);
            var slide:Slide = new Slide();        
            slide.addChild(image);
            addChild(slide);
            slides.addItem(slide);
            slide.width = slideSize;
            slide.height = slideSize;
        }
     }

Note in the above function that we added an event listener, loadComplete(), that is called when each image load is finished:

    private function loadComplete(event:Event):void
    {
        var image:Image = Image(event.target);
        // Enable smooth-scaling
        Bitmap(image.content).smoothing = true;
    }

We added the loadComplete() functionality so that we could enable smooth-scaling for each image. This makes the thumbnails look better in general, but it also vastly improves the animated effect when the images are being scaled on the fly. Without smooth-scaling there can be some serious 'sparkly' artifacts as colors snap to pixel values. Unless all of your pictures are of fireworks and disco balls, the scaling artifact without smoothing enabled is probably not one you'd like.

When the slider value changes in the GUI, that value gets propagated as the new size of the Slide objects through setter for the slideSize property:

    public function set slideSize(value:int):void
    {
        _slideSize = value;
        for (var i:int = 0; i < slides.length; ++i)
        {
            var slide:UIComponent = UIComponent(slides.getItemAt(i));
            slide.width = value;
            slide.height = value;
        }
    }

SlideSorter.mxml

Now let's look at the code for our main application file, SlideSorter. This class is written mostly in MXML, with some embedded ActionScript3 code to handle starting the animation.

First, there's our Application tag. There's nothing amazing that happens here, but you can see that we define a nice gray gradient background for the application as well as a callback function for the creationComplete event, which will call the addImages() function we saw earlier to load the images:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:comps="components.*" layout="absolute"
    backgroundGradientColors="[#202020, #404040]"
    creationComplete="slides.addImages(event)">

The GUI of our application is quite simple; there is just the container for the slides and the HSlider that determines the thumbnail size. The container is defined by this simple declaration:

    <comps:SlidesContainer right="10" bottom="45" top="10" left="10" id="slides"/>

The HSlider is a little more interesting. It defines a data binding expression for the slider value, to ensure that the location of the slider will be set according to the thumbnail size. This is really only for the first view of the application, so that the slider control is in the correct initial location; after that, it is the change in slider value that will change the thumbnail size, so they will automatically be synchronized through that other mechanism. Also, note that change events on the slider will call the segue() method, which is where we set up and run our animation. Note that I changed the slideDuration property from the default 300 down to 100; this makes our animation start sooner, rather than waiting for 300 milliseconds just for the slider animation to complete before we receive the change event.

    <mx:HSlider id="sizeSlider" bottom="10"
        width="100%" height="27" value="{slides.slideSize}"
        change="segue()" slideDuration="100"
        snapInterval="25" minimum="25" maximum="400"/>

Our animation for the layout transition is defined by a simple Parallel effect, which composes a Move and a Resize effect together such that they play at the same time. It is important to note that both Move and Size have built-in intelligence that can determine what values to animate from and to in the right situation. For example, when we set up our animation to collect the start values and then move all of the thumbnails to their end locations/sizes (by calling validateNow() on the container), the effects are clever enough to sense that we want to animate from those initial values to the values that they currently detect.

    <mx:Parallel id="shifter">
        <mx:Resize/>
        <mx:Move/>
    </mx:Parallel>

Finally, we have the ActionScript3 code that we use to set up and run our animation. Note that we already have "set up" our animation by declaring the shifter Parallel effect above. But we need to tell this animation which targets to act on when it runs. (In the situation of this particular demo, we could probably just do this once, since the components in the view are hard-coded at startup. But for a more general case where there might be image changes between transition runs, I wanted the behavior to be more dynamic).

This little snippet of code sets the targets to animate (the slides in our SlidesContainer object) and collects the starting location/sizing info for those targets. It then sets the final slideSize property that we want to animate to and forces a layout validation on the container to position the objects in place. (Note, however, that this layout is not yet visible. It runs the logic internally, but those locations are not changed onscreen immediately; we're safe as long as we're still in this code block). It then sets autoLayout to false; this statement tells our layout manager that we don't want it interfering with trying to move and resize our slides while we're in the middle of animating them. This allows us to do absolute positioning of the objects inside of our container even though our container normally wants to position the slides in a flow-layout style. We then add an event listener to call our segueEnd() function when the animation is complete, which we'll see below. Finally, we play() our animation, which does what we want. Internally, the Move/Resize animation collects the end values for our slides and runs an animation from the start to the end values.

            public function segue():void
            {
                shifter.targets = slides.getChildren();
                shifter.captureStartValues();
                slides.slideSize = sizeSlider.value;
                slides.validateNow();
                slides.autoLayout = false;
                shifter.addEventListener(EffectEvent.EFFECT_END, segueEnd);
                shifter.play();
            }

Finally, we handle the callback when the animation finishes in our segueEnd() function. This simply resets the autoLayout property on our container, to make sure that it resumes its normal behavior.

         public function segueEnd(event:Event):void
         {
             slides.autoLayout = true;
         }

100 goto end

That's it. I frankly expected this to be much harder than it was. It took a bit to work through the details of how to set up the animation appropriately (Effect.captureStartValues()) and how to get the layout manager out of my way during the animation (autoLayout = false), but otherwise all of the intelligence for doing this kind of transition was built into the existing Flex framework.

Note that there are some limitations to this kind of transition. For example, the animation will detect changes only at the top-level children of the container. So, for example, I originally used a Box container to hold each slide, but I found that the images inside the slides were being resized immediately and were not being animated from start to finish. Making the change to simply add the images as direct children of the Slide component fixed this. Ideally, a more robust animated layout system would work with hierarchies of layout; that's something I'll have to think a lot more about.

Complete source code for this application is available here.


Tuesday, April 22, 2008

JavaOne, Too

Romain Guy and I will be returning to JavaOne this year to give another talk this year. We thought about presenting on horticulture, or the effect of air travel on the Amazon rain forest, but in the end we decided at random to talk about this topic: Filthy Rich Clients: Filthier, Richer, Clientier. And though it was tempting to just present stuff we'd already written (with 550 pages of material in the book, there's a lot to, well, steal for presentations), we thought it would be more fun and to do new stuff. Who needs sleep? We hope you can make it to the session.

For anyone that signed up for the session already, we hope you got word that the conference has shuffled us out of our original slot on Tuesday and put us on Thursday afternoon instead (4:10 - 5:10 pm). Maybe they just realized that everyone enjoys pre-enrolling for sessions so much that they figured you'd want another chance to log in and see how that change affected your schedule. And if you didn't pre-register for the session yet, you might want to do so; I've heard that the room is filling up (it's not clear why - maybe it was the "free money!" mention in the abstract. Or maybe it's Romain's groupies. Again.).

There's other stuff happening as well. For one thing, there's an author signing for our book. Maybe someone can explain to me why you actually want us to deface your book. It's a nice book, clean and professional looking, and you want us to write in it? I understand having a famous author sign a book they wrote; heck, it'll be worth more when they're dead. But the authors of Filthy Rich Clients? The only thing worth more when I'm dead will be my life insurance policy. Anyway, we'll be there at the bookstore, signing anything you put in front of us except a blank check.

Finally, there's an Adobe event on Wednesday night, starting at 5:00 at the Metreon. I'm giving a short presentation with James Ward. Come see some of the Flex stuff I'm working on now. And then come to the Adobe party at Jillian's. Go to the Adobe booth on the show floor for details.

I'll also be working the Adobe booth off an on, doing a session or two and milling around trying to look useful. Stop by and say hi.

Monday, April 14, 2008

Time's Up

In trying to figure out the wacky results I was seeing from running the BubbleMark application, I discovered that timer events are constrained in Flash (and therefore Flex) when running inside the browser. This limitation extends to all methods of timing, including: the Timer class, handling ENTER_FRAME events, and manually calling callLater().

Meanwhile, using these timing methods outside of the browser, either in the standalone Flash player or in an AIR application, returns very different results because Flash is no longer constrained by the browser's throttling.

I wrote a simple application, TimerBenchmark, to show the differences between the different timing mechanisms inside the browser and out. The user selects the timing mechanism desired, optionally enters a resolution value (the milliseconds between timing events) for the Timer case, and the test automatically runs. As the test runs, it counts how many timing events are executed per second and displays that result in the "FPS" field at the top of the application. For example, here's the application, runnin in your browser:

How it Works

First, let's see the code. Everything is in the TimerBenchmark MXML file, which you can download and play with. The GUI is set up with a combination of labels, radio buttons, and a TextInput control for the Timer resolution:

    <mx:Label id="fps" x="75" y="48"/>
    <mx:Label x="10" y="48" text="FPS"/>
    <mx:Label x="114" y="76" text="Timer resolution"/>
    <mx:TextInput x="218" y="74" width="39" id="resolution"
        enabled="{timerButton.selected}" text="1" enter="restart()"/>
    <mx:RadioButtonGroup id="benchmarkType" change="restart()"/>
    <mx:RadioButton id="timerButton" x="10" y="74" label="Timer" groupName="benchmarkType"/>
    <mx:RadioButton id="enterFrameButton" x="10" y="100" label="enterFrame" groupName="benchmarkType"/>
    <mx:RadioButton id="callLaterButton" x="10" y="126" label="callLater" groupName="benchmarkType"/>

Any change in the Timer resolution input or one of the radio buttons will call restart(), which will reset any running benchmark and then execute the appropriate test:

            private function restart():void
            {
                if (timer)
                    timer.stop();
                removeEventListener(Event.ENTER_FRAME, enterFrame);
                callLaterRunning = false;
                runBenchmark();
            }
            public function runBenchmark():void
            {
                switch (benchmarkType.selection) {
                    case timerButton:
                       startTimerTest();
                       break;
                    case enterFrameButton:
                       startEnterFrameTest();
                       break;
                    case callLaterButton:
                       startCallLaterTest();
                       break;
                }
            }

All of the tests call benchmarkCallback() to do the actual work of timing the mechanisms:

            private function benchmarkCallback():void
            {
                ++numFrames;
                var nowTime:int = getTimer();
                var delta:int = (nowTime - startTime);
                if (delta > 1000)
                {
                    fps.text = String(numFrames * (delta / 1000));
                    numFrames = 0;
                    startTime = nowTime;
                }
                if (callLaterRunning)
                    callLater(benchmarkCallback);
            }

benchmarkCallback() counts the number of times that it's been called since the last measurement time. Every second, it calculates the fps result, based on numFrames and the time elapsed since the 0th frame. It updates the fps label appropriately and then resets numFrames and startTime. Note that our benchmark function doesn't actually do anything useful. It's a bit like our politician, except that runBenchmark() is intentially unproductive because we only want to time how fast the timing mechanisms can run, not how fast we process anything when they get there. It's important to be able to disentangle these concepts from each other, lest we run into the confusion I encountered in the BubbleMark program, where the throttled rate of Timer was affecting the perceived computation or rendering performance of the application.

startTimerTest() is the function called when the Timer button is pressed:

            public function startTimerTest():void
            {
                if (timer && timer.running)
                    timer.stop();
                timer = new Timer(Number(resolution.text));
                timer.addEventListener(TimerEvent.TIMER, timerEvent);
                timer.start();
            }
            private function timerEvent(event:TimerEvent):void
            {
                benchmarkCallback();
            }

This mechanism works by asking the Flash runtime to call our callback function, timerEvent(), every n milliseconds, where we set n to be equal to the value in our "Timer resolution" text input control.

The enterFrame mechanism looks like this:

            private function startEnterFrameTest():void {
                addEventListener(Event.ENTER_FRAME, enterFrame);
            }
            public function enterFrame(event:Event):void
            {
                benchmarkCallback();
            }

This mechanism works by telling the Flash player to call our callback function, enterFrame(), whenever the ENTER_FRAME event is processed (which is, by definition, once per 'frame' that Flash renders).

Finally, the callLater() benchmark is run by the following code:

            private function startCallLaterTest():void
            {
                callLaterRunning = true;
                benchmarkCallback();
            }

in addition to the final two lines of our benchmarkCallback() function:

                if (callLaterRunning)
                    callLater(benchmarkCallback);

This mechanism works by asking the Flash runtime to call our function, benchmarkCallback(), at the next available opportunity, which occurs twice per frame.

Resolution Results

Here are some sample results with the application running inside the browser on my Windows Vista system:

As you can see, the Timer and EnterFrame mechanisms are both throttled at about 65 frames per second. Meanwhile, the callLater mechanism is getting serviced at about twice that rate.

The reason for the throttling is that the timing system that Flash uses inside the browser is pegged at a maximum, which is a combination of a limitation of the timing mechanism of the browser itself and the refresh rate of the monitor (which for my laptop is 60 fps). The main reason for this limitation is to avoid pegging the CPU just to run animations that really don't need to run faster than the monitor's refresh rate. After all, why waste time and CPU resources updating something that the user will only see updated at the refresh rate of the monitor?

Both the Timer mechanism and enterFrame are maxed out to this same limit. The callLater mechanism sees about twice this rate because it is actually called exactly twice per frame in Flash. It is called once when leaving the enterFrame() call and another when processing the actual frame rendering, so an application that requests a callback through callLater can expect to get that callback twice per frame.

Meanwhile, here are some results from running in the standalone Flash player:

Now we can see that the Timer approach gets close to our ideal fps number; since the resolution was set at 1 and we set the internal Flash frameRate property to 1000, the ideal result is 1000 frames per second. The enterFrame mechanism seems to be throttled at a much lower 250 fps, although this is still significantly better than the browser limit of 65 fps. The callLater mechanism is again twice that of enterFrame, which is what we would expect.

I wrote an AIR version of the benchmark as well. I won't bother to post that version, since I got the same results as the standalone Flash player. This is as expected, since the AIR version is basically running the Flash player standalone internally, freed from the browser constraints. So it is essentially equivalent to the standalone Flash player version.

Resolution Resolution

So where does that leave things? What are we to make of the different timing results?

Well, we mainly need to be aware of the issues, especially when running timing-sensitive code. What mechanism should you use? What frame rate do you want? Are you really getting the results you think you are, or is there some environmental effect that is having an impact on the timing of your application?

In most real-world situations, your application need not (and probably should not) run at a faster rate than the resolution of the screen, so the limitation of the timing mechanisms to refresh rates is perfectly reasonable. In particular, in graphical applications any updates to the scene at greater than refresh rates is simply a waste of cycles, since the user won't see that many updates. But if you need to run some function at greater than this rate for some reason, be aware of the factors called out above and maybe even do some benchmarking of your own to make sure you're getting what you think you're getting.

For More Information

Here's my previous post on Timer's effect on the BubbleMark results

Here's James Ward's post on the same topic, with some alternate benchmark applications and results

Here'sa blog post by Adobe's Tinic Uro on the throttled frame rate in Flash

Here's the original BubbleMark site

Here's the source code for my application, TimerBenchMark

Here's the Flash SWF file for the benchmark

Wednesday, April 9, 2008

Off the [Bubble]Mark

James Ward has been looking into the BubbleMark application recently, and we were talking about some of the strange results we saw in different situations. For example, I was able to take the existing application, which gets something like 65 frames per second in IE7 on my Vista laptop, recast it as an AIR application, and get about double that frame rate, or 130 fps. James posted some variations of the application on his blog; check them out to see what I mean.

(Some background, for those that didn't avail themselves of the awesome opportunity of clicking on the recent link to the site: BubbleMark is an application that moves a number of 'balls' around in a window, and bounces them off one another while doing so, and then measures how fast this critical and awe-inspiring process happens per second).

What's going on? Is that gremlin in my keyboard back to haunt me? Or have I been staring at the screen too long and I'm mis-reading the numbers? Or maybe age is creeping up on me and I'm just getting things wrong more often, as a prelude to slipping on the ice and breaking my hip.

Circular Reasoning

The fundamental problem with the current test is that it is limited by the resolution (the time between events) of the timing mechanism used by the application. In this case, the timing mechanism is a Flash Timer object that is scheduled to call our code at frequent intervals (supposedly far more frequent than the frame rate we're seeing). So while the application purports to test the number of frames that it can render per second, it actually tests the number of times per second that the Timer sends out events. In the situation where Flex can render the balls much faster than the Timer frame rate, this means that the frames-per-second (fps) reported has very little to do with the speed at which the balls are being moved or rendered.

By producing an alternate test that runs on AIR (which takes Flex out of the browser and runs Flex applications in a standalone window on the desktop), we remove a constraint where the Timer resolution tends to be more limited in the browser than in standalone applications. Since the Timer can call our ball-moving routine more often in this AIR version, and since we were taking less than a frame's worth of time to move the balls before (although that wasn't reflected in the fps result), we get a higher effective frame rate and better results.

So the AIR version is closer to the ideal, where we're actually measuring more of what the test was presumably intended for; moving and rendering the balls.

But there's still a problem here, which has been implied by the wide variation in the results. The test is confusing three orthogonal issues and trying to combine them into one result (fps). The three things actually being tested are:

  • Calculation speed: How fast can the runtime position the balls, including moving them, calculating collisions, and offsetting the collided balls? This comes down to VM and math performance, since it's just raw computations.
  • Rendering performance: There is a certain amount of time spent each frame actually drawing the balls on the screen. That seems to be insignificant in our case, compared to the other factors here, but you would think that a graphics benchmark might actually care about this item them most. How fast can we draw the balls? I don't know, because we're spending most of our time just waiting around for Timer events and calculating the balls' positions. That's like timing how long it takes you to get dressed in the morning, but starting the timer the previous night when you go to sleep.
  • Timer resolution: This is the primary issue called about above. I don't believe it was ever the intention for the benchmark to measure the rate at which the various runtimes could schedule callbacks. Because, well, that'd be a pretty dull graphics benchmark. But that's exactly what's happening here; we're not measuring graphics performance at all in the default test.

To add to the problem of these three factors getting each others' chocolate stuck in the others' peanut butter, you've got the problem that all of the different platforms (Flex, Java, .net, etc.) probably have different reliance on these three factors (are they faster at rendering but slower than computation? Do they have more limited Timer resolution? How do browsers affect these factors on each platform? Or hardware? Or operating systems?)

I think that if you wanted real results that you could use and compare from this benchmark, you'd have to de-incestuate the factors and measure them all separately. I could envision three competely separate tests coming out of this:

  • Calculation speed: Let's have a non-graphical test that simply moves/collides the balls as fast as possible and reports how many times it was able to do this per second. No need to render here; we only care about how fast we can do the math and related setting of properties.
  • Rendering performance: Render a given number of balls as fast as you can and report how often you can do it per second. In order to not run up against the Timer resolution issue, you'd have to adjust the number of balls so that you could be assured that you would be spending very little time waiting around between callback events. In fact, you could even try to render far more than could be done in a single frame, but then report a number like balls/second instead of frames/second.
  • Timer resolution: Write a test that simply issues callbacks from the Timer mechanism, without doing any work in those callbacks, to see how many times this happened per second. (I wrote this test and posted a blog about it; see that post for more information on this no-op timer test).

With results from these three tests, it would be easier to understand where a platform was being bottlenecked: at the runtime, rendering, or resolution level.

At this point, I'd love to say, "...and here's that benchmark!" But I'm not going to, because I've already spent a pile of time just trying to understand the weird results I was seeing from Bubblemark. If someone else wants to take a whack at it, that'd be great. But at the very least, I hope this helps you understand and filter the results you see from the BubbleMark tests a little better.....

Tuesday, March 25, 2008

Lines and Tigers: Customizing ComboBox

In the Top Drawer application that I posted recently, wanted a custom combobox that showed the line widths as graphical lines, not as text descriptions. Text is a pale substitute for graphics, don't you think? Think how much more boring Hamlet would have been if it had just been a bunch of words. Or Anna Karenina. Or think how exciting and immersive everey episode of Sponge Bob Square Pants is, all because it's drawn in such meticulous detail.

I got part-way to my goal in Top Drawer, with the drop-down list showing lines instead of labels. But the main combobox button still showed a text label, instead, "width 1":

Partial Achievement

Now that I finally finished the exposee on Top Drawer, it's time to revisit the combobox and see if we can spruce it up a bit.

Take I: Default ComboBox with Boring Text Labels

First, let's see the default we'd get if we just coded up a normal ComboBox to handle line widths. In this case, I want to assign a width between 1 and 5 to my lines; I create a ComboBox with a list of objects that have width attributes.

    <mx:ComboBox id="lineWidth" x="10" y="189" width="87"
            editable="false">
        <mx:ArrayCollection>
            <mx:Object width="1" label="width 1"/>
            <mx:Object width="2" label="width 2"/>
            <mx:Object width="3" label="width 3"/>
            <mx:Object width="4" label="width 4"/>
            <mx:Object width="5" label="width 5"/>
        </mx:ArrayCollection>
    </mx:ComboBox>

The width attribute is used as a source in a databinding expression for the ArtCanvas object, where it sets the current stroke width based on the width value of the currently selected item in the ComboBox.

    <comps:ArtCanvas id="canvas" 
            drawingColor="{drawingColorChooser.selectedColor}"
        strokeWidth="{lineWidth.selectedItem.width}"
        left="105" top="10" right="10" bottom="10"/>

This ComboBox does the job, allowing the user to set the width of the lines interactively through the GUI with the small amount of code above to hook it up. But the user experience is not quite what a graphics geek might want:

Default Line Width ComboBox

Take II: Graphics in the Drop-Down

Now we come to the version that I developed in the Top Drawer application; I attacked the drop-down list to make the elements in that list graphical instead of the boring text-based "width x" versions above.

To do this entails the Flex notion of an "item renderer." Item renderers are used by the list-based components, such as ComboBox, allowing applications to specify objects which will perform custom rendering of each item in the list. Specifying the item renderer in MXML is done by simply naming the class that will be used to render the items. In our application, we use the class LineWidthRenderer, which is specified in the same mx:ComboBox tag as above:

    <mx:ComboBox id="lineWidth" x="10" y="189" width="87"
            itemRenderer="components.LineWidthRenderer" editable="false">
        <mx:ArrayCollection>
            <mx:Object width="1" label="width 1"/>
            <mx:Object width="2" label="width 2"/>
            <mx:Object width="3" label="width 3"/>
            <mx:Object width="4" label="width 4"/>
            <mx:Object width="5" label="width 5"/>
        </mx:ArrayCollection>
    </mx:ComboBox>

The code in our LineWidthRenderer class (written in ActionScript3) that handles rendering each item is in the standard setter for the data field; this method will be called by Flex whenever it needs to display a particular item. In our case, we know that each data item will contain a width attribute, so we check the value of that attribute and perform our rendering appropriately:

        override public function set data(value:Object):void
        {
            _data = value;
            renderLine(graphics, 5, 50, _data.width);
        }

        public static function renderLine(graphics:Graphics, 
                x0:int, x1:int, lineWidth:int)
        {
            graphics.clear();
            graphics.lineStyle(lineWidth, 0x000000);
            graphics.moveTo(x0, 10);
            graphics.lineTo(x1, 10);
        }

Here, our data setter is calling the renderLine() method in the same class, which takes a Graphics object, endpoints for the line, and a line width value and draws an appropriate line into the display list of the graphics object. The result is much better than before:

Better Line Width Rendering

Finally, we're rendering our line width choices as actual lines. The user can see visual representations of what they will get in the shapes they draw and can make a better selection between the UI choices. Besides, it just looks cooler.

But it's still not quite good enough; what's with that "width 1" label in the ComboBox's button?

Take III: ComboBox Nerdvana

Not only does the button of the ComboBox display text when the drop-down list is nicely graphical below it; the closed ComboBox displays only that text label. Gone is all of the lovely wide-line artwork that we slaved over for the drop-down list.

The final step, then, is to customize our ComboBox further to provide a graphical representation of the currently selected item in the top-level button.

This turns out to be a simple problem to solve. So simple, in fact, that I felt silly that I didn't solve it before I shipped the initial version of the application. But what would life be like if all our TODOs were impossible? Here's the solution:; we need a subclass of ComboBox with customized rendering for the button.

We create a subclass of ComboBox in ActionScript3, LineWidthComboBox, as follows:

package components
{
import mx.controls.ComboBox;

public class LineWidthComboBox extends ComboBox
{
    override protected function updateDisplayList(
            unscaledWidth:Number, unscaledHeight:Number):void
    {
        LineWidthRenderer.renderLine(graphics, 10, 55, selectedItem.width);
    }
}
}

Not very complex, is it? The subclass exists to override the single method updateDisplayList(), which is where a component creates the rendering for its contents. In this case, we call the same static renderLine() method as we used before in our LineWidthRenderer class to draw a graphical representation of the currently-selected line width into our component. But apparently this isn't enough, since this is what results:

Where'd the Buttoin Go?

The problem is that we're not bothering to render the actual button object of our ComboBox, so the typical button background that we expect is not there. Since we're lazy and don't want to do all of that work in our code, we can ask the superclass to take care of the rendering for this component for us by simply calling the superclass' updateDisplayList() method first, and then performing our custom rendering on top of it:

    override protected function updateDisplayList(
                 unscaledWidth:Number, unscaledHeight:Number):void
    {
        super.updateDisplayList(unscaledWidth, unscaledHeight);
        LineWidthRenderer.renderLine(graphics, 10, 55, selectedItem.width);
    }

This is better, but still not quite what we want:

Label and Line

Now we have our button and a graphical line, but we also have the default text label which our superclass is kindly drawing for us. We want the superclass to draw the button decoration, but we don't want it to draw the text label. How can we fix this?

There are probably several ways to do this, but one easy way is to simply supply a label that won't get drawn.

By default, ComboBox looks for a "label" attribute in each item in the list and renders the string associated with that attribute. In our original mxml code for the ComboBox, we supplied items with both a width property (which supplies the information we use to actually determine the stroke width) and a label property, as follows:

        <mx:ArrayCollection>
            <mx:Object width="1" label="width 1"/>
            <mx:Object width="2" label="width 2"/>
            <mx:Object width="3" label="width 3"/>
            <mx:Object width="4" label="width 4"/>
            <mx:Object width="5" label="width 5"/>
        </mx:ArrayCollection>

We could simply remove the label:

        <mx:ArrayCollection>
            <mx:Object width="1"/>
            <mx:Object width="2"/>
            <mx:Object width="3"/>
            <mx:Object width="4"/>
            <mx:Object width="5"/>
        </mx:ArrayCollection>

but then we wouldget the following instead:

Bad Label and Line

The problem this time is that ComboBox is determined to find a text label, or if it doesn't find one it will create one of its own, which is obviously not what we want.

What our button really needs is an empty label; so let's provide it:

        <mx:ArrayCollection>
            <mx:Object width="1" label=""/>
            <mx:Object width="2" label=""/>
            <mx:Object width="3" label=""/>
            <mx:Object width="4" label=""/>
            <mx:Object width="5" label=""/>
        </mx:ArrayCollection>

With this change, we've pointed the ComboBox to label properties that it can use, but when our superclass goes to display the text, nothing will happen - which is just what we want:

Just the Line

Finally, with our custom ComboBox subclass, our reliance on the superclass rendering facilities for the button basics, and our fabulous new empty labels, in addition to our previous use of a custom item renderer for the drop-down list, we have what we set out to achieve: a completely graphical line-width control:

Completely Graphical Line Width Control

Here are the update sources Top Drawer. Stay tuned for more improvements in upcoming posts.

Wednesday, March 19, 2008

Top Drawer, Part III: Taking Shape

Today, we finish our walkthrough of the Top Drawer application.

But first:

Q: Why did the Shape leave the canvas?
A: It had had its fill.

Q: What do they call boxing matches between vector-art objects?
A: Stick-fighting.

Q: Why don't Shape parents allow their kids to watch stick fighting?
A: Too much graphic violence.

Q: How are Shapes kept informed?
A: First, they're given an outline, then they're filled in completely.

Q: Why was the drawing application so tired?
A: For every four lines drawn, it was a complete rect.

Q: What do these jokes have in common with inidividual vectors drawn wth TopDrawer?
A: They're both one-liners with little point.

In Part I, we went over the code for TopDrawer.mxml, which contained the GUI layer for our drawing application. In Part II, we saw the code for ArtCanvas.as, which handles the interaction and most of the logic of the application. Now, in Part III, we will see the code for creating and rendering the actual drawing shapes.

More importantly, in this installment we'll actually finish the series (at least until a later post where I'll go over some design improvements).

To refresh your memory, here's the amazing drawing application again:

Helper Classes

Before I get to the [he]art of this article (the shape classes), I wanted to cover a couple of other small classes to show how they do their thing.

LineWidthRenderer.as

This class is repsonsible for rendering each item in the drop-down menu on the line-width combobox in the main GUI. Instead of the typical text-based combobox, I wanted to have a graphical representation of the lines, like what you see in most drawing applications. An "item renderer" is a class that can be installed on many of the Flex list-based components to render each individual item in those components. It does this by receiving calls to set the data member of the item and producing an appropriate rendering. In our data-setting method, we simply take the information embedded in that data item (which includes the width field, set in TopDrawer.mxml) and create a display list for our LineWidthRenderer class which will draw a line of that width:

        override public function set data(value:Object):void
        {
            _data = value;
            var lineWidth:int = _data.width;
            graphics.clear();
            graphics.lineStyle(lineWidth, 0x000000);
            graphics.moveTo(5, 10);
            graphics.lineTo(45, 10);
        }

[To make this even better, I'd like to enhance the combobox so that the button that's displayed also has a graphical line and not the text label it has now. Look for that improvement in a future installment.]

DrawingShapeIcon.as

DrawingShapeIcon is a simple subclass of Image which has the added functionality of displaying a red highlight border when it is the currently selected drawing mode. We saw in Part I how the DrawingShapeIcon objects get created in TopDrawer.mxml added as event listeners when the drawing mode changes. Now we'll see how the highlight border is implemented.

The drawing mode assigned to each DrawingShapeIcon is set by assigning the mode variable:

        public var mode:int;

The border is a static object (we only need one for the application since all icons can reuse the same one):

        private static var highlightBorder:Shape;

Finally, the modeChanged() method is called when the DrawingModeEvent is propogated. This method creates highlightBorder if it does not yet exist and then adds the border to the instance whose mode matches the new mode that's been set. Note that adding highlightBorder as a child on to one icon will automatically remove it as a child from any other icon; we do not need to bother calling removeChild() for the previously-highlighted icon.

        public function modeChanged(event:DrawingModeEvent):void
        {
            if (!highlightBorder) {
                highlightBorder = new Shape();
                highlightBorder.graphics.lineStyle(5, 0xff0000, .8);
                highlightBorder.graphics.drawRect(0, 0, width, height);
            }
            if (event.mode == mode) {
                addChild(highlightBorder);
            }
        }

ArtShape.as

Now, we're into the meat of this article: the drawing shapes. All of the shapes are subclasses of ArtShape, which provides the public API for shape creation and performs some common functionality that is used by most subclasses. ArtShape is a subclass of the flash class Shape, and ArtCanvas uses capabilities of that class, such as being able to add it as a child of the canvas and being able to detect whether a given point on the canvas hits the object (we saw both of these capabilities exercised in Part II, when we explored ArtCanvas).

Properties

Each shape keeps track of its start and end points, which comprise the points where the mouse first started dragging and the last drag location of the mouse. These are currently only used during object creation, when endPoint may change with every mouse drag. Once a drawing is complete, the rendering of a shape is not changed so we do not actually need these points again. A more complete drawing program might allow these points to be edited after the fact, in which case caching them would be useful.

        protected var startPoint:Point;
        protected var endPoint:Point;

The strokeWidth and color properties are set at construction time and cached for later use when creating the display list for the shape. Note that strokeWidth is only used in the non-filled primitives (a more exact class hierarchy might differ slightly to avoid caching an instance variable at this level that is not used by some subclasses):

        protected var strokeWidth:int;
        protected var color:uint;

The start() and drag() methods are called upon first mouse-down and each drag operation, respectively. The start() method merely caches the current start and end points, whereas the drag() operation, in addition to setting a new end value, also calls renderShape(), which is where all subclasses (except Scribble, which we will see later) create the display list which renders the shape:

        public function start(value:Point):void
        {
            startPoint = value;
            endPoint = value;
        }
 
        public function drag(value:Point):void
        {
            endPoint = value;
            renderShape();
        }

The addPoint() method is called on mouse-up. Once again, the new endPoint is cached and renderShape() is called. We also return a value that indicates whether this shape is complete. The reason for this is that a possible future feature would allow drawing of complex primitives such as curves, which might take several mouse clicks/drags to create a single shape, instead of the single drag operation that the current shapes take. This return value would tell ArtCanvas whether to record the shape as finished or to continue in a mode of adding information to the current shape. It's not a part of the current application, but I added this little bit of infrastructure in case I added it later.

        public function addPoint(value:Point):Boolean
        {
            endPoint = value;
            renderShape();
            return true;
        }

The validShape() method is called by ArtCanvas after the shape is complete to determine whether this shape should actually be added to the list of shapes on the canvas or whether it should be deleted. The simple check in ArtShape simply checks whether the start and end points are the same. Subclasses may choose to have more complex check (which is the case in the Scribble shape).

        public function validShape():Boolean
        {
            if (endPoint.equals(startPoint))
            {
                return false;
            }
            return true;
        }

renderShape() is called during object creation to create a rendering of the shape. Most subclasses (except Scribble) override this method and create a display list appropriately.

        protected function renderShape():void {}

getSelectionShape() is called by ArtCanvas to retrieve a new shape that will be used to show that a given ArtShape is selected. It is common to show filled-rectangle handles on the corners of the bounding box of a shape, so that's what this superclass implements. Some subclasses (such as Line) may choose to override this method and return a different selection shape.

        public function getSelectionShape():Shape
        {
            var shape:Shape = new Shape();
            var bounds:Rectangle = getBounds(parent);
            shape.graphics.beginFill(0);
            shape.graphics.drawRect(
                    bounds.left - HANDLE_SIZE_HALF, 
                    bounds.top - HANDLE_SIZE_HALF,
                    HANDLE_SIZE, HANDLE_SIZE);
            shape.graphics.drawRect(
                    bounds.left - HANDLE_SIZE_HALF, 
                    bounds.bottom - HANDLE_SIZE_HALF,
                    HANDLE_SIZE, HANDLE_SIZE);
            shape.graphics.drawRect(
                    bounds.right - HANDLE_SIZE_HALF, 
                    bounds.bottom - HANDLE_SIZE_HALF,
                    HANDLE_SIZE, HANDLE_SIZE);
            shape.graphics.drawRect(
                    bounds.right - HANDLE_SIZE_HALF, 
                    bounds.top - HANDLE_SIZE_HALF,
                    HANDLE_SIZE, HANDLE_SIZE);
            shape.graphics.endFill();
            return shape;
        }

Now, let's take a look at the ways that some of the subclasses build upon the capabilities of ArtShape.

ArtShape Subclasses

In Line.as, we override both the renderShape() method and the getSelectionShape() method. getSelectionShape() is not terribly interesting; it simply draws two filled rectangles at the endpoints of the line. But renderShape() is where we build the display list to render the line with the current endpoints:

        override protected function renderShape():void
        {
            graphics.clear();
            graphics.lineStyle(strokeWidth, color);
            graphics.moveTo(startPoint.x, startPoint.y);
            graphics.lineTo(endPoint.x, endPoint.y);
        }

Ellipse is even simpler, since it does not override getSelectionShape() (it uses the ArtShape implementation to draw the selection handles on the bounding box corners). It has one additional item in the constructor, to determine whether the ellipse is filled or not:

        public function Ellipse(width:int, color:uint, filled:Boolean)
        {
            super(width, color);
            this.filled = filled;
        }

Ellipse (and the other filled shape subclass, Rect) uses this filled variable to determine whether to fill or stroke the shape in the renderShape() method:

        override protected function renderShape():void
        {
            graphics.clear();
            if (filled) {
                graphics.beginFill(color);
            } else {
                graphics.lineStyle(strokeWidth, color);
            }
            graphics.drawEllipse(startPoint.x, startPoint.y,
                    endPoint.x - startPoint.x, endPoint.y - startPoint.y);
        }

Most of the shapes are similarly simple, depending on ArtShape for most functionality. The exception, as you might have guessed from the plethora of parenthetical mentions of it above, is Scribble.

Scribble.as

This shape is different than the others because it does not simply draw itself between the start and end points of the mouse drag, but rather adds a new line segment for every new mouse position during the drag.To do this, it creates the display list dynamically, starting it at mouse-down and adding to it during every drag operation. This means that there is more happening during the actual point-adding methods, but that renderShape() is noop'd (because the display list has already been created).

Here is the start() method, where we begin the display list based on the first mouse location:

        override public function start(value:Point):void
        {
            points.addItem(value);
            graphics.lineStyle(strokeWidth, color);
            graphics.moveTo(value.x, value.y);
        }

Note that we also cache, here and in the other dragging methods, the intermediate points in our internal points object; this is for later use in detecting whether a shape is valid. It would also be useful if we allowed advanced editing of the individual points of a Scribble object. But that's a feature for another day...

Subsequent calls to drag() and the final addPoint() method add lines to the display list, which are drawn from the previous point in the display list (either the first moveTo() point for a new line or the point in the last lineTo() operation):

        override public function drag(value:Point):void
        {
            points.addItem(value);
            graphics.lineTo(value.x, value.y);
        }

Scribble's other tweak on ArtShape is an override of validShape(). It's not good enough to detect whether the start and end points are coincident, as ArtShape does; we need to walk the entire list of our cached points in a Scribble to see whether they are all the same:

        override public function validShape():Boolean
        {
            var firstPoint:Point = Point(points.getItemAt(0));
            for (var i:int = 1; i < points.length; ++i)
            {
                if (!firstPoint.equals(Point(points.getItemAt(i)))) {
                    return true;
                }
            }
            return false;
        }

Finally!

That's it. The whole application. We saw bits of nearly every class in the source base, only skipping those too simple to be interesting or where the functionality was similar to code already shown. But I encourage you to download the source tree, look at all of the files, build it, run it, and play with it. And if you discover any problems, let me know; I'll fix them and update the project. (This doesn't include major feature updates; I purposely kept the project small so that I could easily show various features of Flex, Flash, ActionScript3, and MXML. Sure, I'd love to write a 3D modeling tool, but that's not happening in TopDrawer).

A future installment will address some of the design aspects that I called out along the way. The current code is not bad, but there are definitely some improvements that could make the code and application just a tad nicer.

I look forward to writing more of these kinds of articles. I still have a big learning curve ahead of me with the full stack of Flash, Flex, and AIR, and I'll be writing applications to teach myself how things work. And I'll be posting that code here so that you can learn right along with me.