Graph.as |
/* author: Sammy Joe Osborne date: 09/15/06 Graph Class Renders a graph to the stage using a blank movie clip in the library as it's base. Parameters are in the following order: Width of graph including margin, Height of graph including margin, x axis maximum number, y axis maximum number, x axis step number, y axis step number All of this can/should? later be calculated automatically based on the data being entered, then accessible to change via get and set methods. */ import mx.transitions.Tween; import mx.transitions.easing.*; import flash.geom.*; class Graph extends MovieClip{ //Constants private var decimalRound:Number = 100; //number to round decimals. 100 to round to 100's place, etc. //private instance variables private var dataSetCount:Number = 0; //the number of sepparate sets of data (lines) being graphed on this graph private var xUnit:Number; //the number in pixels equal to one x unit on the graph private var yUnit:Number; //the number in pixels equal to one y unit on the graph private var yMargin:Number; //the number in pixels between the edge of the movie and the actual start of the graphing area private var xMargin:Number; //the number in pixels between the edge of the movie and the actual start of the graphing area private var gridLinesOn:Boolean; //whether gridlines should be on or not private var xAxisMax:Number; //highest number to display on the x axis private var yAxisMax:Number; //highest number to display on the y axis private var xAxisIncrement:Number; //number to increment the next mark on the x axis private var yAxisIncrement:Number; //number to increment the next mark on the y axis private var xAxisIncrementPixels:Number; //number in pixels to increment on the x axis private var yAxisIncrementPixels:Number; //number in pixels to increment on the y axis private var bgColor:Number; //background color for graph private var graphHeight:Number; //the height of the graph, including margins private var graphWidth:Number; //the width of the graph, including margins private var xDashLength:Number; //the length in pixels of the dashes used on the x axis to show increments private var yDashLength:Number; //the length in pixels of the dashes used on the y axis to show increments private var xGraphGridLines:Boolean = false; //true for x axis (verticle) grid lines on, false for off. Default is false private var yGraphGridLines:Boolean = true; //true for y axis (horizontile) grid lines on, false for off. Default is true private var graphGridLines:String = "y"; //Turns gridlines for x, y, or both on or off. Valid values are: "x", "y", "both", or "none". Default is "y". private var xAxisDashes:Boolean = true; //turns x Axis ticks either on or off. Default is on private var yAxisDashes:Boolean = true; //turns y Axis ticks either on or off. Default is on private var graphBackgroundType:String; //the type of background used in the graph. Can only be set through the fillBackground() method private var dotColor:String; //determines the dot style (color) of dot for all dots created after this is set private var lineColor:String; //determines the line style (color) for all lines created after this is set private var dataSets:Array = new Array(); //an array to hold the names of all dataSets on the graph //constructor public function Graph(){ trace("Graph created"); } public function init(xSize:Number, ySize:Number, _xAxisMax:Number, _yAxisMax:Number, _xAxisIncrement:Number, _yAxisIncrement:Number){ xMargin = 30; yMargin = 30; graphWidth = xSize; graphHeight = ySize; xAxisMax = _xAxisMax; yAxisMax = _yAxisMax; xAxisIncrement = _xAxisIncrement; yAxisIncrement = _yAxisIncrement; yUnit = int(((graphHeight - yMargin) / yAxisMax)*decimalRound)/decimalRound; xUnit = int(((graphWidth - xMargin) / xAxisMax)*decimalRound)/decimalRound; xAxisIncrementPixels = int((xUnit * xAxisIncrement)*decimalRound)/decimalRound; yAxisIncrementPixels = int((yUnit * yAxisIncrement)*decimalRound)/decimalRound; yDashLength = 5; xDashLength = 5; trace("Graph initialized"); drawGraph(); return this; } public function updateGraph(_xAxisMax:Number, _yAxisMax:Number, _xAxisIncrement:Number, _yAxisIncrement:Number){ xAxisMax = _xAxisMax; yAxisMax = _yAxisMax; xAxisIncrement = _xAxisIncrement; yAxisIncrement = _yAxisIncrement; yUnit = int(((graphHeight - yMargin) / yAxisMax)*decimalRound)/decimalRound; xUnit = int(((graphWidth - xMargin) / xAxisMax)*decimalRound)/decimalRound; xAxisIncrementPixels = int((xUnit * xAxisIncrement)*decimalRound)/decimalRound; yAxisIncrementPixels = int((yUnit * yAxisIncrement)*decimalRound)/decimalRound; } //------------------------------------Graphing Rendering Functions----------------------------------------- private function drawGraph(){ //initializes height and width by drawing blank lines moveTo(0, 0); lineTo(graphWidth, 0); moveTo(0, 0); lineTo(0, graphHeight); drawOutlines(); this.fillBackground("gradient", 0xcccccc, 0x999999); drawGridLines(); drawXAxisDashes(); drawYAxisDashes(); drawXAxisNumbers(); drawYAxisNumbers(); //NOTE: DELETE THIS. Temporary to show margins /*moveTo(0,0); lineTo(0, graphHeight); lineTo(graphWidth, graphHeight); trace("Height: " + _height); trace("Width: " + _width);*/ } //fills in a background with specified color. Default is 0xDBDBDB. Doesn't fill if "none" is specified by user public function fillBackground(_type:String, _color1:Number, _color2:Number):Void{ graphBackgroundType = _type; var movieName:String = this._name + "Background"; if(this[movieName]._x == undefined){ this.createEmptyMovieClip(movieName, this.getNextHighestDepth()); } this[movieName].clear(); if(graphBackgroundType == "none"){ this[movieName].clear(); } else if(graphBackgroundType == "solid"){ var color:Number = _color1; this[movieName].clear(); this[movieName].moveTo(xMargin+1, 0); this[movieName].beginFill(color, 100); this[movieName].lineTo(xMargin+1, (graphHeight - yMargin-1)); this[movieName].lineTo(graphWidth, (graphHeight - yMargin-1)); this[movieName].lineTo(graphWidth, 0); this[movieName].lineTo(xMargin+1, 0); this[movieName].endFill(); } else if(graphBackgroundType == "gradient"){ var colors:Array = [_color1, _color2]; this[movieName].clear(); var fillType:String = "linear" var alphas:Array = [100, 100]; var ratios:Array = [0, 255]; var matrix:Matrix = new Matrix(); matrix.createGradientBox(graphWidth - xMargin, graphHeight - yMargin, -67.5, 0, 0); var spreadMethod:String = "pad"; this[movieName].moveTo(xMargin+1, 0); this[movieName].beginGradientFill(fillType, colors, alphas, ratios, matrix, spreadMethod); this[movieName].lineTo(graphWidth, 0); this[movieName].lineTo(graphWidth, (graphHeight - yMargin-1)); this[movieName].lineTo(xMargin+1, (graphHeight - yMargin-1)); this[movieName].lineTo(xMargin+1, 0); this[movieName].endFill(); } else{trace("you must enter either \"none\", \"solid\", or \"gradient\" in the background fill function.");} } //draws graph axes lines private function drawOutlines():Void{ var movieName:String = this._name + "Outlines"; if(this[movieName]._x == undefined){ this.createEmptyMovieClip(movieName, this.getNextHighestDepth()); } this[movieName].clear(); this[movieName].moveTo(xMargin, -1); this[movieName].lineStyle(2, 0x000000, 100, false, "none", "none"); this[movieName].lineTo(xMargin, (graphHeight - yMargin)); this[movieName].lineTo(graphWidth + 1, (graphHeight - yMargin)); } //draw x axis increment dashes (vertical) private function drawXAxisDashes():Void{ var movieName = this._name + "XAxisDashes"; if(this[movieName]._x == undefined){ this.createEmptyMovieClip(movieName, this.getNextHighestDepth()); } this[movieName].clear(); if(xAxisDashes == true){ this[movieName].lineStyle(2, 0x000000, 100, false, "none", "none"); var i = 0; for(var j = xAxisIncrementPixels; i <= (xAxisMax / xAxisIncrement)-1; j += xAxisIncrementPixels){ this[movieName].moveTo((xMargin + j), (graphHeight - yMargin)); this[movieName].lineTo((xMargin + j), (graphHeight - yMargin + xDashLength)); i++; } } } //draw y axis increment dashes (horizontile) private function drawYAxisDashes():Void{ var movieName = this._name + "YAxisDashes"; if(this[movieName]._x == undefined){ this.createEmptyMovieClip(movieName, this.getNextHighestDepth()); } this[movieName].clear(); if(yAxisDashes == true){ this[movieName].lineStyle(2, 0x000000, 100, false, "none", "none") var j = 0; for(var i = 0; j <= (yAxisMax / yAxisIncrement); i += yAxisIncrementPixels){ this[movieName].moveTo(xMargin, (0 + i)); this[movieName].lineTo((xMargin - yDashLength),(0 + i)); j++; } } } //draw numbers along x axis private function drawXAxisNumbers():Void{ var movieName = this._name + "XAxisNumbers"; if(this[movieName]._x != undefined){ //recreates the movieclip the numbers are housed in and loads it over the old one, incase they need to be updated this.createEmptyMovieClip(movieName, this[movieName].getDepth()); } else{ this.createEmptyMovieClip(movieName, this.getNextHighestDepth()); } var axisTextFormat:TextFormat = new TextFormat(); axisTextFormat.font = "Arial"; axisTextFormat.size = 14; var xAxisText:Number = Number(xAxisIncrement); var i = 0; for(var j = xAxisIncrementPixels; i <= (xAxisMax / xAxisIncrement)-1; j += xAxisIncrementPixels){ this[movieName].createTextField(("xAxisNum"+j), this[movieName].getNextHighestDepth(), (xMargin + j), (graphHeight - yMargin + yDashLength), 10, 100); this[movieName]["xAxisNum" + j].autoSize = "left" this[movieName]["xAxisNum" + j].setNewTextFormat(axisTextFormat); this[movieName]["xAxisNum" + j].text = xAxisText; this[movieName]["xAxisNum" + j]._x = this[movieName]["xAxisNum" + j]._x - (this[movieName]["xAxisNum" + j]._width / 2); //aligns text properly to the dash it's positioned next to xAxisText += Number(xAxisIncrement); //have to use casting for some reason or it appends it like a string i++; } } //draw numbers along y axis private function drawYAxisNumbers():Void{ var movieName = this._name + "YAxisNumbers"; if(this[movieName]._x != undefined){ //recreates the movieclip the numbers are housed in and loads it over the old one, incase they need to be updated this.createEmptyMovieClip(movieName, this[movieName].getDepth()); } else{ this.createEmptyMovieClip(movieName, this.getNextHighestDepth()); } var axisTextFormat:TextFormat = new TextFormat(); axisTextFormat.font = "Arial"; axisTextFormat.size = 14; var yAxisText:Number = 0; var i = 0; for(var j = 0; i <= (yAxisMax / yAxisIncrement); j += yAxisIncrementPixels){ this[movieName].createTextField(("yAxisNum"+j), this[movieName].getNextHighestDepth(), (xMargin - xDashLength - 10), (graphHeight - yMargin - j), 10, 100); this[movieName]["yAxisNum" + j].autoSize = "right"; this[movieName]["yAxisNum" + j].setNewTextFormat(axisTextFormat); this[movieName]["yAxisNum" + j].text = yAxisText; this[movieName]["yAxisNum" + j]._y = this[movieName]["yAxisNum" + j]._y - (this[movieName]["yAxisNum" + j]._height / 2); //aligns text properly to the dash it's positioned next to yAxisText += Number(yAxisIncrement); //have to use casting for some reason or it appends it like a string i++; } } //draws gridlines private function drawGridLines():Void{ drawXGridLines(); drawYGridLines(); this[this._name + "XGridLines"]._visible = false; this[this._name + "YGridLines"]._visible = false; if(xGraphGridLines == true){ this[this._name + "XGridLines"]._visible = true; } if(yGraphGridLines == true){ this[this._name + "YGridLines"]._visible = true; } } //draw x axis grid lines (vertical) private function drawXGridLines():Void{ var movieName:String = this._name + "XGridLines"; if(this[movieName]._x == undefined){ this.createEmptyMovieClip(movieName, this.getNextHighestDepth()); } this[movieName].clear(); this[movieName].lineStyle(1, 0x000000, 100, false, "none", "none"); var i = 0; for(var j = 0; i <= (xAxisMax / xAxisIncrement); j += xAxisIncrementPixels){ this[movieName].moveTo((xMargin + j), (graphHeight - yMargin)); this[movieName].lineTo((xMargin + j), 0); i++; } } //draw y axis grid lines (horizontile) private function drawYGridLines():Void{ var movieName:String = this._name + "YGridLines"; if(this[movieName]._x == undefined){ this.createEmptyMovieClip(movieName, this.getNextHighestDepth()); } this[movieName].clear(); this[movieName].lineStyle(1, 0x000000, 100, false, "none", "none"); var j = 0; for(var i = 0; j <= (yAxisMax / yAxisIncrement); i += yAxisIncrementPixels){ this[movieName].moveTo(xMargin, (0 + i)); this[movieName].lineTo(graphWidth,(0 + i)); j++; } } //--------------------------------------Graphing Functions----------------------------------------- public function addLine(lineName:String, lineColor:Object){ //make sure the line gets a color of blue if a color isn't specified if(lineColor == undefined){lineColor = "0x66cc00";} //checks to see if the line already exists, and if so, just create it again over top of the old one if(this[lineName]._x != undefined){ this.attachMovie("DataPointSet", lineName, this[lineName].getDepth(), {x:0, y:0}).init(lineColor); } //otherwise, if it doesn't exist, create it at the next highest depth else{ this.attachMovie("DataPointSet", lineName, this.getNextHighestDepth(), {x:0, y:0}).init(lineColor); dataSets[dataSetCount] = lineName;//add the name of the line to the dataSets array dataSetCount++;//up the number of lines (sets of data) the graph has } } public function addDataPoint(lineName:String, _xCoordinate:Number, _yCoordinate:Number):Void{ var graphStats:Object = new Object(); graphStats.xMargin = xMargin; graphStats.yMargin = yMargin; graphStats.xUnit = xUnit; graphStats.yUnit = yUnit; graphStats.graphHeight = graphHeight; this[lineName].addDataPoint(_xCoordinate, _yCoordinate, graphStats); this[lineName].drawPoints(); if(this[lineName].pointCount > 1){ this[lineName].drawLines(); } } public function animateLines(lineToMove:String, goal:String){ //this calls draw lines every 20 milliseconds to keep up with the tweening of the points var intervalID:Number = setInterval(this[lineToMove], "drawLines", 20); this[lineToMove].swapDepths(this[goal].getDepth());//makes sure the moving line is placed on top this[lineToMove].animateDots(this[goal]); //this is simply a tween that does nothing but count to 4, and when finished it deletes the setInterval used above var deleteInterval:Tween = new Tween(this, _x, None.easeNone, _x, _x, 4, true); deleteInterval.onMotionFinished = function(){ clearInterval(intervalID); trace("interval deleted"); } } //Used only when the graph units / limits are updated. //uses the dataSets array to get the names of each dataSet, then redraws them to the new units public function redrawDataSets():Void { var movieName:String; var lineName:String; var color:Object; var coordinates:Array = new Array(); //will store the points from a line so they can be redrawn using the add data point functions //cycles through all data sets in this graph for(var i = 0; i < dataSets.length; i++){ lineName = dataSets[i]; color = this[lineName].color; //stores all the data points x and y coordinates in this dataSet so they can be re-added in a moment this[lineName].reset(); coordinates = new Array(); for(var j = 0; !this[lineName].atEnd(); j+=2){ coordinates[j] = this[lineName].getDataPoint().xcoord; coordinates[j+1] = this[lineName].getDataPoint().ycoord; this[lineName].advance(); } addLine(lineName, color); //now re-add all stored data points for(var j = 0; j < coordinates.length; j+=2){ addDataPoint(lineName, coordinates[j], coordinates[j+1]) } this[lineName].drawPoints(); if(this[lineName].pointCount > 1) this[lineName].drawLines(); } } //---------------------------------Getter and Setter functions------------------------------------- //----------Setter functions-------------- public function set xGridLines(value:Boolean):Void { xGraphGridLines = value; drawGridLines(); } public function set yGridLines(value:Boolean):Void { yGraphGridLines = value; drawGridLines(); } public function set xAxisTicks(value:Boolean):Void { this.xAxisDashes = value; drawXAxisDashes(); } public function set yAxisTicks(value:Boolean):Void { this.yAxisDashes = value; drawYAxisDashes(); } public function set dotStyle(value:String):Void { this.dotColor = value; } public function set lineStyle(value:String):Void { this.lineColor = value; } public function set backgroundType(value:String):Void { this.graphBackgroundType = value; } //updates and redraws the x axis with the new xAxisMax and increment values public function set XMax(value:Number):Void { this.xAxisMax = value; xUnit = int(((graphWidth - xMargin) / xAxisMax)*decimalRound)/decimalRound; xAxisIncrementPixels = int((xUnit * xAxisIncrement)*decimalRound)/decimalRound; drawXAxisNumbers(); drawGridLines(); drawXAxisDashes(); } //updates and redraws the y axis with the new xAxisMax and increment values public function set YMax(value:Number):Void { this.yAxisMax = value; yUnit = int(((graphHeight - yMargin) / yAxisMax)*decimalRound)/decimalRound; yAxisIncrementPixels = int((yUnit * yAxisIncrement)*decimalRound)/decimalRound; drawYAxisNumbers(); drawGridLines(); drawYAxisDashes(); } //updates and redraws the x axis at the specified increment public function set XIncrement(value:Number):Void { xAxisIncrement = value; xUnit = int(((graphWidth - xMargin) / xAxisMax)*decimalRound)/decimalRound; xAxisIncrementPixels = int((xUnit * xAxisIncrement)*decimalRound)/decimalRound; drawXAxisNumbers(); drawGridLines(); drawXAxisDashes(); } //updates and redraws the y axis at the specified increment public function set YIncrement(value:Number):Void { yAxisIncrement = value; yUnit = int(((graphHeight - yMargin) / yAxisMax)*decimalRound)/decimalRound; yAxisIncrementPixels = int((yUnit * yAxisIncrement)*decimalRound)/decimalRound; drawYAxisNumbers(); drawGridLines(); drawYAxisDashes(); } //----------Getter functions-------------- public function get xGridLines():Boolean { return xGraphGridLines; } public function get yGridLines():Boolean { return yGraphGridLines; } public function get xAxisTicks():Boolean { return xAxisDashes; } public function get yAxisTicks():Boolean { return yAxisDashes; } public function get backgroundType():String { return graphBackgroundType; } public function get dotStyle():String { return dotColor; } public function get lineStyle():String { return lineColor; } public function get XMax():Number { return xAxisMax; } public function get YMax():Number { return yAxisMax; } public function get XIncrement():Number { return xAxisIncrement; } public function get YIncrement():Number { return yAxisIncrement; } }