Friday, July 4, 2008

Flash Player: Text Around A Circle

I received a rare and very welcome comment about my most recent postings about the new Flash Player 10 (FP10) and my 1st demo, FontWheels.  In his comment, OLMSTJ, mentioned he was currently working on a project where he was placing text around a circle and therefore curious how I approached doing it in my demo.

Unlike my previous published code, for example my sort function for XML, this code is not at the point where it is generic.  It was actually more generic when I first built it for Flash Player 9 (FP9) when I was rotating text first converted to bitmaps.

 So I’m going to release the FP9 code, a single static function wrapped in a simple demo.  The framework (oh, I hate that word) for the old and new functions are similar but there are differences in how I place each character.  If at some point, I reduce the FP10 code to a similar scope, I’ll probably publish that code.  The FP10 code has a lot more functionality due to the demo’s requirement to stuff an entire circle with different font fields and other minor details that wouldn’t be needed outside the demo.

I did stripped a little code from the FP9 function.  It converted the complex Sprite to a single centered bitmap in a Sprite.  Each text character is implemented with a Bitmap inside its own Sprite.  Since I tend to make my pages very detailed, I like to strip away the complexity by converting content to a Bitmap whenever I can.  After reviewing the code, due to writing this blog, I decided building the conversion to a centered Bitmap in a Sprite into this static function was not a good design.

Code Narrative

The function has 3 arguments: 1) the radius; 2) a TextField containing the desired text and the formatting information; and 3) a Boolean where true means to place the text centered on the top and false means to center on the bottom.

I use a TextField object to specify the input data.  I wanted to be able vary the format of each character.  Therefore I build the multi-formatted text outside of the function.  However it turns out the results of doing this are varied.  It is somewhat dependent of the actual font.  For example changing the 1st character font size or setting it to bold sometimes does not look that great.  Of course, it might not look that good when displayed in a straight line either.  On the other hand changing the color works well (despite potential bad taste).

Don’t set the background color unless you are looking for some special effect.  The underline I created in my link to this blog, in the picture to the right, was drawn by hand.  This was a feature I tacked on at the end which contributes to the process not being as generic as I would like.  In order to know where to begin and end the underline circle you need internal information that is known at the time it was drawn.

As I said providing the underlined circular text was a last minute requirement for the FP10 project.  I needed a way to change the color when the mouse hovers over the link text.  I chose to not convert the text and underline to bitmapData, but instead I left it as a complex sprite.  I drill down to each TextField and change each of their colors.  I also redraw the underline to change the color.

Now that I’m done, I’ve had a chance to 2nd guess the approach.  My feelings at the moment would be to convert it to bitmapData and use its class functions to change the color.  This, of course, conflicts with the ability to restore different colors on the each character when the mouse rolls off the link.  A link shouldn’t be multi-colored but it just shows the complexity when you try to make one solution for all (like frameworks end up doing).

I hope this much detail in the design is useful to someone and doesn’t put everyone else to sleep.

The first thing I do is convert the input TextField to a bitmapData object.  I will use this object to extract each character into a bitmap.

Next I compute how many degrees the text will consume.  Using that number I figure where I need to start placing the text based on the fact that I want the text centered at the top or bottom.

Then, I loop through each character of the argument TextField.  I ignore spaces.  For each character, I create a Sprite and add a Bitmap containing that character to that Sprite.  Now the code is different depending on whether you want characters at the top or bottom.  Notice that the when the characters are at the top you are placing the bottom of the character around the circumference of the circle.  Then look at the pictures above and notice my name at the bottom of the circle.  Here, the top of the each character is drawn around circumference of the circle.  It took a while to get my head straight on this concept.

Each character is then extracted into a new little bitmap.  These bitmaps are each inserted into their own Sprite.  I rotate each of these Sprites to the correct location.  I compute this location by getting the location from the input argument TextField.  Earlier I computed the number of degrees the whole field needs so that I could compute where to put the 1st character after centering.  I took the easy way out and simply apply the same fractional character positioning of the flat text field and apply that to the total number of degrees the field consumes on the curve to find the rotation to apply to the sprite.  I’m sure most of you have taken math more recently than the 60’s and would have done it a different way, but this way I only had to look up how to compute the circumference of a circle.

But first where do you put the Bitmap character in the Sprite.  Imagine each Sprite to be like a spoke in a wheel with a character attached to the end and the other end at the center of the wheel.  As I mentioned earlier it depends on whether you are drawing the text around the top or on the bottom.  If on the top, the spoke connects to the bottom of the character and to the top of the character if the text goes on the bottom.

There is a short coming in using the spoke in a wheel to describe this.  The hardest part of writing this code was placing the text on the bottom of the circle.  I’m sure I won’t explain this properly but if you need to dive into the code maybe this will give you a head start.  I kept trying to calculate the rotation of the Sprite’s when the text is on the bottom by computing the rotation at the bottom.  But that is wrong.

Imagine the spoke for characters at the bottom to extend across the entire wheel instead of just to the center like the spokes for the top.  They still rotate at the center of the wheel but you control them by moving it at the top which is at the opposite end of where the Bitmap character is.  When you rotate the spoke at the top to the right the Bitmap at the bottom moves to the left.

Obviously, you can tell I’m not a mathematician by describing it that way.  But that’s they way I figured it out.

That’s basically it.  It isn’t a large function.