Pie Charts in AS3
Sorry it’s been a while since myself or anyone else posted. To make up for it, I thought I’d share my Actionscript 3 code for a pie chart generator from an external xml file. In AS2 there was a wedge drawing class that made this whole thing a lot easier, and while I couldn’t find anything like that for my pie chart generator, I basically just made my own. So, here’s the code:
var values:Array = new Array();var xmlLoader:URLLoader = new URLLoader();
var xmlData:XML = new XML();
xmlLoader.addEventListener(Event.COMPLETE, LoadXML);
xmlLoader.load(new URLRequest(HTTP://YOUR_XML_URL_HERE));function LoadXML(e:Event):void {
xmlData = new XML(e.target.data);
buildPieChart(xmlData);
}
function buildPieChart(xmlData:XML):void {
var titleList:XMLList = xmlData.item.title;
var valueList:XMLList = xmlData.item.value;
for each (var titleElement:XML in titleList) {
titles.push(titleElement);
}
for each (var valueElement:XML in valueList) {
values.push(valueElement);
}
var colors:Array = new Array();
colors.push(0xff0000);
colors.push(0xff9900);
colors.push(0xffff00);
colors.push(0×00ff00);
colors.push(0×0000ff);
colors.push(0xff00ff);
colors.push(0×330000);
colors.push(0×003300);
colors.push(0×000033);
colors.push(0×333300);
colors.push(0×330033);
var radians:Array = new Array();
var totalValue:Number = 0;
for(var i:int = 0; i < values.length; i++) {
totalValue = totalValue + Number(values[i]);
}
for(i =0; i < values.length; i++) {
radians.push(Number(values[i])/totalValue*2);
}
drawlines(250, 250, 120, radians);
function drawlines(centerx, centery, radius, radians) {
var radiansSoFar:Number = 0;
var piechart:MovieClip = new MovieClip();
piechart.x = stage.stageWidth/2;
piechart.y = stage.stageHeight/2;
stage.addChild(piechart);
piechart.graphics.lineStyle(2, 0×000000);
var colorkey:Number = 0;
var coloralpha:Number = 1;
for(var i:int = 0; i < radians.length; i++) {
piechart.graphics.beginFill(colors[colorkey], coloralpha);
piechart.graphics.moveTo(0,0);
piechart.graphics.lineTo(Math.sin(radiansSoFar*Math.PI)*radius, Math.cos(radiansSoFar*Math.PI)*radius);
for(var n:Number = 0; n <= radians[i]; n += .0001) {
piechart.graphics.lineTo(Math.sin((radiansSoFar+n)*Math.PI)*radius, Math.cos((radiansSoFar+n)*Math.PI)*radius);
}
radiansSoFar += radians[i];
piechart.graphics.lineTo(0,0);
piechart.graphics.endFill();
addLabel(radians[i], titles[i], radiansSoFar, radius, colors[colorkey], coloralpha);
if(colorkey == colors.length-1) {
colorkey = 0;
coloralpha -= .25;
} else {
colorkey += 1;
}
}
}
function addLabel(radians, itemtitle, radiansSoFar:Number, radius:Number, color, coloralpha) {
var format:TextFormat = new TextFormat();
format.align = “left”;
format.font = “Verdana”;
format.size = 9;
format.bold = true;
var label:TextField = new TextField();
label.width = 1;
label.height = 1;
label.autoSize = “left”;
label.antiAliasType = “advanced”;
label.text = itemtitle + ” (” + Math.round((radians/2*100)).toString() + “%)”;
label.border = false;
label.setTextFormat(format);
var textRadians:Number = radiansSoFar-(radians/2);
label.x = (stage.stageWidth/2)+Math.sin(textRadians*Math.PI)*radius;
label.y = (stage.stageHeight/2)+Math.cos(textRadians*Math.PI)*radius;
if(textRadians > 0 && textRadians < .5) {
label.y -= label.height/2;
label.y += 10;
label.x += 10;
}
if(textRadians > .5 && textRadians < 1) {
label.y -= label.height/2;
label.x += 10;
label.y -= 10;
}
if(textRadians > 1 && textRadians < 1.5) {
label.y -= label.height/2;
label.x -= label.width;
label.x -= 10;
label.y -= 10;
}
if(textRadians > 1.5 && textRadians <= 2) {
label.y -= label.height/2;
label.x -= label.width;
label.x -= 10;
label.y += 10;
}
if(textRadians == 0 || textRadians == 2) {
label.y += 10+label.height/2;
}
if(textRadians == .5) {
label.x += 10+label.width/2;
}
if(textRadians == 1) {
label.y -= 10+label.height/2;
}
if(textRadians == 1.5) {
label.x -= 10+label.width/2;
}
stage.addChild(label);
}
}
Okay, now to explain the above code. I think any moderately experienced actionscripter can figure it out pretty quickly, if not instantly. One thing I should mention before going on is the structure of the xml, even though again, an experienced actionscripter can figure it out from the above code. Nonetheless, here is it:
<?xml version=”1.0″ encoding=”utf-8″?>
<items>
<item>
<title>First Title</title>
<value>First Value</value>
</item>
<item>
<title>Second Title</title>
<value>Second Value</value>
</item>
etc.
</items>
Okay, now on to the code.
var values:Array = new Array();
These are the arrays that will ultimately hold the titles and values that the pie chart generator will loop through. I like to create them at the outset. It’s just a personal preference.
var xmlData:XML = new XML();
xmlLoader.addEventListener(Event.COMPLETE, LoadXML);
xmlLoader.load(new URLRequest(HTTP://YOUR_XML_URL_HERE));
function LoadXML(e:Event):void {
xmlData = new XML(e.target.data);
buildPieChart(xmlData);
}
The above code deals entirely with retrieving the XML file we’ll be using to build our pie charts. Why use an external xml file? Well, besides all of the advantages of XML, I can use the same flash file to build an infinite number of pie charts. The reason I programmed this pie chart generator was to create a human readable snapshot of data from my Fire-EMS database. There are lots of views I want, so I just need to point to a different php page that generates the view I need and reports that information in formated XML. The last peice of the puzzle that is not included in the above code yet, is taking the url to my xml and making it a variable I can pass to the flash movie when I embed it into an html document or php page. If you need to know how to do that, then by all means leave me a comment asking, and I will let you know. Anyway, what this chunk of code does specifically is create a new loader and xml object, and creates an event listener to tell flash what to do when the xml file is completely loaded (which is to run the LoadXML function). We then load the xml file, and define the LoadXML function that will run when loading is complete. All this function does is take the loaded data and place it into an xml object and then calls the buildPieChart function which we’ll define next.
There’s an awful lot to the buildPieChart function, so let’s take it peice by peice:
var titleList:XMLList = xmlData.item.title;
var valueList:XMLList = xmlData.item.value;
for each (var titleElement:XML in titleList) {
titles.push(titleElement);
}
for each (var valueElement:XML in valueList) {
values.push(valueElement);
}
The above first defines the buildPieChart function. From there we parse out the xml, garnering both our titles and values from the single xmlData object we passed in. The two for-each-loops go through the list of titles and values, respectively, and puts all the information into the two arrays we created at the outset, titles and values.
colors.push(0xff0000);
colors.push(0xff9900);
colors.push(0xffff00);
colors.push(0×00ff00);
colors.push(0×0000ff);
colors.push(0xff00ff);
colors.push(0×330000);
colors.push(0×003300);
colors.push(0×000033);
colors.push(0×333300);
colors.push(0×330033);
This next part defines an array of colors we’ll be filling out wedges with. The colors are as follows: red, orange, yellow, green, blue, violet, deep red, deep orange, deep yellow, deep green, deep blue, and deep violet. You can add or remove whatever colors you like. The more colors you add the more items the pie chart can support before it just starts filling in white or throwing errors. So there are some limits to this pie chart, but as you’ll see later, there is a function that extends this small number of colors into about 4 times as many colors by playing with the alpha. I’ll explain that more when I get there.
var totalValue:Number = 0;
for(var i:int = 0; i < values.length; i++) {
totalValue = totalValue + Number(values[i]);
}for(i =0; i < values.length; i++) {
radians.push(Number(values[i])/totalValue*2);
}
drawlines(250, 250, 120, radians);
If you remember your trigonometry, you’ll surely remember radians. Radians are a measure of degrees made relative to Pi. For example, 2Π = 360°, Π = 180°, and .5Π = 90°. We’ll be using trigonometric functions later to draw our wedges, and they only respond to radians, so we do some quick math to create an array that saves radian values. Here’s the specifics: we create an array to house our radian values; create a variable that represents the total value of all the values added together, which we’ll use to figure out what percent of the circle each wedge represents, or the number of radians; loop through the value array adding each value to our totalValue variable; then loop through each value again, this time figuring out what percentage of the 2Π radians that comprise a circle each wedge is. We push these values into our radian array for use later. As this is all the information we need, I can call the drawlines function, which is the function that really builds the chart, which we’ll define next.
var radiansSoFar:Number = 0;
var piechart:MovieClip = new MovieClip();
piechart.x = stage.stageWidth/2;
piechart.y = stage.stageHeight/2;
stage.addChild(piechart);
piechart.graphics.lineStyle(2, 0×000000);
var colorkey:Number = 0;
var coloralpha:Number = 1;
This isn’t all of the function, but since it’s large we’ll tackle it peicemiel. The first variable is the number of radians we’ve gone through so far. This will be used in a loop later, but what it amounts to is this: when we draw the first wedge, if we don’t add it’s radian value to the next radian value then we’ll draw our next wedge on top of our last wedge, instead of next to it. We then create the piechart movie clip that will house our pie chart, put it’s center in the middle of the stage, define the style for the lines we’ll be drawing, and set the colorkey to 0, since we’ll start going through our color array starting with the first, or 0 key. You’ll see this in action later. Coloralpha is set to 1 since alpha is represented as a value from 0 to 1. We start with the max opacity here. You’ll notice we passed in centerx and centery and haven’t used them. We won’t. They were there from my initial programming, and I didn’t end up using them as I thought I would, but I haven’t taken them out either. Feel free to take it out, just make sure you adjust your drawlines function call above.
piechart.graphics.beginFill(colors[colorkey], coloralpha);
piechart.graphics.moveTo(0,0);
piechart.graphics.lineTo(Math.sin(radiansSoFar*Math.PI)*radius, Math.cos(radiansSoFar*Math.PI)*radius);
for(var n:Number = 0; n <= radians[i]; n += .0001) {
piechart.graphics.lineTo(Math.sin((radiansSoFar+n)*Math.PI)*radius, Math.cos((radiansSoFar+n)*Math.PI)*radius);
}
radiansSoFar += radians[i];
piechart.graphics.lineTo(0,0);
piechart.graphics.endFill();
addLabel(radians[i], titles[i], radiansSoFar, radius, colors[colorkey], coloralpha);
if(colorkey == colors.length-1) {
colorkey = 0;
coloralpha -= .25;
} else {
colorkey += 1;
}
}
}
Okay, there’s a lot going on here. This loop literally builds our pie chart. Generally, we loop through every radian value and draw a line from the center of the circle to the first point at the edge of our circle, which we find using sin and cos. The sin of a radian value will give you your x coordinate on the unit circle, and the cos of a radian value will give you your y coordinate on the unit circle. Now, the unit circle has a radius of 1, but we want our chart to be bigger, depending on what you pass into the function for your desired radius. All we need to do is multiply the sin and cos values by our desired radius. Once we’ve drawn that line, we need to trace the curve of the circle. We can accomplish this with the curveTo function, but the amount of geometry and trigonometry required to do this perfectly isn’t worth the time. Instead we draw a whole lot of really tiny lines - so tiny that it just looks like a curved surface unless you could zoom in further than flash will let you. We do this by increasing our radian value by .01 until we reach however many radians the wedge we’re drawing actually represents. Once we reach that point, we just draw a line back to the center of the circle (0,0). Now, before we started drawing lines, we called the beginFill function, and we passed to it colors[colorkey] and coloralpha. Colors was the array we created with all the colors we wanted to fill wedges with. So the first wedge will get colors[0] (red), the second will get colors[1] (orange), so on and so forth. So what if we have more wedges than colors? Our if statement at the end checks to see if the color we just used was the last one. If it was, we go back to the first color, but we decrease the alpha by a quarter (.25). So now we can use all the colors again, but they’ll be lighter, so we have new colors. If we still have more values, it’ll decrease the alpha by .25 again. Eventually this will start throwing errors as our alpha dips below 0, but that’s an awful lot of pie chart values, which will make it hard to read anyway. You’ll notice before we started into the if statement, we called addLabel, which I’ll explain next. It essentially adds the value’s title to the out of the wedge. Be sure to call endFill, or no wedges will actually get filled with color.
var format:TextFormat = new TextFormat();
format.align = “left”;
format.font = “Verdana”;
format.size = 9;
format.bold = true;
var label:TextField = new TextField();
label.width = 1;
label.height = 1;
label.autoSize = “left”;
label.antiAliasType = “advanced”;
label.text = itemtitle + ” (” + Math.round((radians/2*100)).toString() + “%)”;
label.border = false;
label.setTextFormat(format);
var textRadians:Number = radiansSoFar-(radians/2);
label.x = (stage.stageWidth/2)+Math.sin(textRadians*Math.PI)*radius;
label.y = (stage.stageHeight/2)+Math.cos(textRadians*Math.PI)*radius;
if(textRadians > 0 && textRadians < .5) {
label.y -= label.height/2;
label.y += 10;
label.x += 10;
}
if(textRadians > .5 && textRadians < 1) {
label.y -= label.height/2;
label.x += 10;
label.y -= 10;
}
if(textRadians > 1 && textRadians < 1.5) {
label.y -= label.height/2;
label.x -= label.width;
label.x -= 10;
label.y -= 10;
}
if(textRadians > 1.5 && textRadians <= 2) {
label.y -= label.height/2;
label.x -= label.width;
label.x -= 10;
label.y += 10;
}
if(textRadians == 0 || textRadians == 2) {
label.y += 10+label.height/2;
}
if(textRadians == .5) {
label.x += 10+label.width/2;
}
if(textRadians == 1) {
label.y -= 10+label.height/2;
}
if(textRadians == 1.5) {
label.x -= 10+label.width/2;
}
stage.addChild(label);
}
}
Okay, this is our last function, so this is the final stretch. Considering all the if statements at the end, this can be intimidating, but it’s not all the difficult. We start by creating the formating for the labels we’ll be creating. I’ve set mine to align left, Verdana, 9 pt, and bold. Next we create the actual text field and assign some more parameters. I set the autosize to left after setting the width and height to 1. The idea is that the autosize will expand the text field to the size of the text, and no additional whitespace, which is important for positioning later. The value left means that the text will align left, and the field will expand to the right and down. We then add our text into the field, which is the title, and then the percentage that that item represents of the pie in parenthesis. This requires a little math, which I think is fairly self explanatory. We then calculate which radian value represents the center of our wedge, since we’ll want our label to appear on the outside of the circle, but in the center of the wedge it corresponds to. The final parts of this function position the text field properly, which requires some accounting for separate scenarios, since a label in the top right of the circle needs to be moved up and to the right to not be on the circle, but on the top left the label needs to be moved left and up. So all these if statements do is make sure the label gets positioned correctly. Nothing more, really. You shouldn’t have to play with this, but feel free to, just to get a feel for what I did.
And that’s it. One pie chart generating script. If you have any questions or comments on how to optimize this, please don’t hesitate to leave a comment.


What do I mean by a corner of a third? Well, basically, the idea is if you take a piece of paper and fold it into thirds both horizontally and vertically you’ll have two lines going across the paper lengthwise and width-wise. This creates nine boxes in your piece of paper. The four corners of the center box are the points where traditional artists put the main subjects of their works, because these are the first places the eye sees, and because it’s much easier to create an impression on the viewer. If you look at my crudely constructed example picture to the left, you’ll see four red circles. These are the points I’m talking about. If you didn’t already know this, I invite you to peruse your local art gallery and you’ll see how prolific a technique this is. Want to make your family photos 100% better than they currently are? Take this principle to heart. Enough of putting your daughter at the center of the picture, put her face in that top left corner third, and you’ll have a real winning photo, one assumes.
Every so often I feel the need to go back and reread some of my favorite books, as if I might forget their importance if I didn’t. It’s probably why I don’t get to as much new stuff as most people otherwise do in a year. Nonetheless, I have a great appreciation for the books on my shelf, so much so I’m sometimes afraid to lend them out to anyone. Frodo has his ring, I have my bookshelf (which, ironically, does not contain a single work by Tolkien).
