Building point charts
This recipe builds on the previous one. The previous recipe placed all the codes into one big method. This is fine for the first experiment, but when things get more complicated, you'll lose track of what goes where.
This recipe brings structure into the code and shows how to graph multiple points without repeating yourself.
Getting ready
Make sure you at least glanced over the previous recipe. You'll need it here.
Create a new, blank Recipe2
class, set it as the document class, and copy and paste the code inside the Recipe1
constructor placed inside it. To be sure, run the class and verify that everything looks the same as in the previous recipe.
How to do it...
We will tackle the generalization of the code in a few steps. If you take a look at the code, you'll notice that there are actually two important blocks: one that creates the graph shape and one that draws a point.
- In the first step we will extract these blocks into two methods:
package { import flash.display.Shape; import flash.display.Sprite; public class Recipe2 extends Sprite { private var graph:Sprite; public function Recipe2() { createGraph(); drawPoint(); } private function createGraph():void { graph = new Sprite(); graph.x = 400; graph.y = 300; graph.scaleY = -1; addChild(graph); } private function drawPoint():void { var point:Shape = new Shape(); point.graphics.beginFill( 0xff9933 , 1 ); point.graphics.drawCircle( 0 , 0 , 3 ); point.x = 0; point.y = 0; graph.addChild(point); } } }
The end result should still be exactly the same.
- The coordinates of the current graph are chosen for this one example; it would be much better to parameterize this. As parameters, we'll include the top-left and bottom-right coordinates we'd like to have:
createGraph(-400, 300, 400, -300);
To make sure the method works with all possible choices of coordinates, we need to do some calculations in the
createGraph
method:- Translating the graph to the new center point (keeping in mind the scale).
- Scale the graph to the right size. This can be done by comparing the current width and height against the values we want. The current width and height are fixed when you enter your project properties. But you can also obtain them from the stage object.
- Lastly, if the coordinates are reversed, we must mirror the image.
All these calculations together form this reusable method:
private function createGraph(left:Number, top:Number, right:Number, bottom:int):void { var width:Number = Math.abs(right - left); var height:Number = Math.abs(bottom - top); var scaleWidth:Number = stage.stageWidth / width; var scaleHeight:Number = stage.stageHeight / height; var flipX:Boolean = (left > right); var flipY:Boolean = (top > bottom); graph = new Sprite(); graph.x = scaleWidth * Math.abs(left); graph.y = scaleHeight * Math.abs(top); graph.scaleX = (flipX ? -1 : 1) * scaleWidth; graph.scaleY = (flipY ? -1 : 1) * scaleHeight; addChild(graph); }
- Now that we've got the hard part out of the way, let's make the
drawPoint
method a little more reusable. For now, we want to set all the points to the same style and size, so the only parameters to the method are the location.The resulting code is as follows:
package { import flash.display.Shape; import flash.display.Sprite; public class Recipe2 extends Sprite { private var graph:Sprite; public function Recipe2() { createGraph(-400, 300, 400, -300); drawPoint(0, 0); drawPoint(20, 20); drawPoint(-40,-40); } private function createGraph( left:Number, top:Number, right:Number, bottom:Number):void { var width:Number = Math.abs(right - left); var height:Number = Math.abs(bottom - top); var scaleWidth:Number = stage.stageWidth / width; var scaleHeight:Number = stage.stageHeight / height; var flipX:Boolean = (left > right); var flipY:Boolean = (top > bottom); graph = new Sprite(); graph.x = scaleWidth * Math.abs(left); graph.y = scaleHeight * Math.abs(top); graph.scaleX = (flipX ? -1 : 1) * scaleWidth; graph.scaleY = (flipY ? -1 : 1) * scaleHeight; addChild(graph); } private function drawPoint(x:Number, y:Number):void { var point:Shape = new Shape(); point.graphics.beginFill( 0xff9933 , 1 ); point.graphics.drawCircle( 0 , 0 , 3 ); point.x = x; point.y = y; graph.addChild(point); } } }
This code also shows that the method can be reused to draw several different points. If everything went well, you should see three points along a diagonal, with the bottom-left point being a little farther away from the others.
How it works...
To see how the transformation actually works, add the following line to the end of the graph constructor:
trace(graph.transform.matrix);
Note
If you have issues viewing the trace statements output, it may be due to the Flash player you are using or the FlashDevelop configuration. The FlashDevelop wiki has troubleshooting instructions right here: http://www.flashdevelop.org/wikidocs/index.php?title=AS3:Debugging.
This trace
statement will display the transformation matrix in use. If you still remember your math, you may find this other way of looking at the process enlightening (see http://en.wikipedia.org/wiki/Transformation_matrix for an in-depth discussion on the topic).
The createGraph
method sets up the following transformations:
- It calculates the required scaling based on the target width and height.
- The
flipX
andflipY
variables are used to calculate whether the target coordinate system is mirrored or not. For instance, in this example, the Y- axis of the screen coordinates points down (0 is at the top and positive numbers are at the bottom), while the coordinate system in which we draw the graph works the other way around. This means we need to mirror the Y coordinates.
The drawPoint
method draws a new point and can be called repeatedly to draw multiple points.
It creates a new shape for every point. This is not strictly necessary and might even cause a degraded performance when drawing many points. In the coming chapters, we'll see some uses for this approach, but if you have issues with performance, you can directly draw on the Recipe2
sprite and not instantiate new shapes for every point.
There's more...
Again, this basic program is very powerful. And with a few changes, it allows for lots of experimentation.
Try to change the parameters of the createGraph
method and see if you can predict the results.
Typically, scientific graphs will have their origin (0,0) in the center of the graph. Most business-type charts will have their origin somewhere in the bottom-left corner. All of these can be easily achieved by changing the parameters of the drawGraph
method.
If you change the coordinate system, you may notice that the point scales with the coordinate system. You can fix this by scaling back the point to the original size. Or you can transform all the operations, instead of the shape (refer to the next recipe for the solution).
See also
This recipe closely relates to many of the others in this chapter. So if something isn't clear, go back to the previous one, or skip ahead and come back to it later.