Graph.as
DataPointSet.as
DataPoint.as


/*
author: Sammy Joe Osborne
date: 09/21/06
DataPointSet is a linked list implementation I ported from Java to Actionscript 2.0.
It is used to link together DataPoint objects.  The Node class is also used in this
implementation.  Each Node contains data (the actual DataPoint object), a nextNode,
                                          and a previousNode which simply contain (or point to) the Node before or after itself
in the chain.

FUNCTIONS:
DataPointSet() 			//The constructor
addDataPoint(o:Object) 	//Recieves a DataPoint object and adds a node to the set with this DataPoint as it's data
removeDataPoint() 		//Removes the current node
isEmpty()				//Returns a boolean if the list is empty or not
getDataPoint()			//Returns the DataPoint object contained in the current node
getNextDataPoint()		//Returns the DataPoint object contained in the node after current
atEnd()					//Returns a boolean true or false if the current node is at the end of the list
advance()				//Iterates the DataPointSet to move to the next node in the chain
reset()					//Sets currentPoint back to the beginning of the DataPointSet (the head node)
display()
drawPoints()			//draws all points in the dataSet to the stage
drawLines()

NOTES:
An onEnterFrame function in the Graph class continually calls the drawLine function in
this class to move them with the dots when they move.

Depth management to keep the dots on top of the lines is done within this class.  Depth
management to keep DataPointSets on top of each other will have to be done in the Graph
class.


*/
import mx.transitions.Tween;
import mx.transitions.easing.*;

class DataPointSet extends MovieClip{
        //current, previous, and next nodes
        var headPoint:Node;
        var currentPoint:Node;
        var previousPoint:Node;
        var nextPoint:Node;
        var pointCount:Number;
        var linesDrawn:Boolean;
        var varName:String;
        var myColor:Object;
        
        
        function DataPointSet(){
                //do nothing
        }
        
        //_color sent as an object just incase I emplement other forms of color later.......?  right now its a string.
        public function init(_color:Object){
                headPoint = null;
                currentPoint = null;
                previousPoint = null;
                nextPoint = null;
                pointCount = 0;
                linesDrawn = false;
                varName = this._name;
                myColor = _color;
                trace("The constructor just ran");
                return this;
        }
        
        
        //adds a new point to the list
        //it actually adds the attached movie clip into the linked list.  Attach movie actually returns a movieClip object, so I had the init() function return a DataPoint object, which is just an extended MovieClip object.
        public function addDataPoint(_xCoordinate:Number, _yCoordinate:Number, _graphStats:Object):Void{
                pointCount++;
                //create movieclips the points and lines will exist on
                if(this["Points"]._x == undefined){
                        this.createEmptyMovieClip(("Points"), 2);
                        this.createEmptyMovieClip(("Lines"), 1);
                }
                
                var n:Node = new Node(this["Points"].attachMovie("DataPoint", varName + pointCount, pointCount,{}).init(_xCoordinate, _yCoordinate, myColor, _graphStats), null);
                if(headPoint == null)
                headPoint = n;
                else{
                        reset();
                        gotoEndingPoint();//go to last point in the list to add new point after it (just incase list has been reset)
                        currentPoint.nextNode = n;
                        previousPoint = currentPoint;
                }
                currentPoint = n;
                
        }
        
        
        //remove a point from the list
        public function removeDataPoint():Void{
                if(headPoint != null){
                        if(previousPoint == null)
                        headPoint = headPoint.nextNode;
                        else
                        previousPoint.nextNode = currentPoint.nextNode;
                        currentPoint = currentPoint.nextNode;
                }
                pointCount--;
        }
        
        //determine if the DataPointSet is empty
        public function isEmpty():Boolean{
                if(headPoint == null)
                return true;
                return false;
        }
        
        //returns the DataPoint object in the current position; returns null if the current element is null
        public function getDataPoint():Object{
                if(currentPoint != null)
                return currentPoint.dataObject;
                return null;
        }
        
        //returns the next DataPoint object held in the node pointed to by the current node, but doesn't change current
        public function getNextDataPoint():Object{
                if(currentPoint != null)
                return currentPoint.nextNode.dataObject;
                return null;
        }
        
        //determines if we are at the end of the list of points (if we are on the last point)
        public function atEnd():Boolean{
                return currentPoint == null;
        }
        
        //sets currentPoint to the ending point in the list
        public function gotoEndingPoint():Void{
                while(currentPoint.nextNode != null){
                        advance();
                }
        }
        
        //Advances currentPoint to be the next DataPoint in the chain
        public function advance():Void{
                if(!atEnd()){
                        previousPoint = currentPoint;
                        currentPoint = currentPoint.nextNode;
                }
        }
        
        //resets to the beginning of the list
        public function reset():Void{
                previousPoint = null;
                currentPoint = headPoint;
        }
        
        //From the first point, outputs all x,y graph coords of points
        public function display():Void{
                reset();
                if(headPoint != null){
                        do{
                                trace("x:" + getDataPoint().xcoord + " y:" + getDataPoint().ycoord);
                                advance();
                        }while(!atEnd());
                }
        }
        
        //returns an array of all the points in this set in the form of a DataProvider so it can populate a DataGrid component
        public function getDataProvider():Array{
                reset();
                var DP:Array = new Array();
                var i = 0;
                if(headPoint != null){
                        do{
                                DP[i] = ({X:(""+getDataPoint().xcoord), Y:(""+getDataPoint().ycoord)});
                                advance();
                                i++;
                        }while(!atEnd());
                }
                return DP;
        }
        
        //makes all points (dots) visible
        public function drawPoints():Void{
                reset();
                var i = 1;
                do{
                        this["Points"][varName+i]._visible = true;
                        i++;
                        advance();
                }while(i<=pointCount);
        }
        
        
        //Simply draws a fat black line, then draws another thinner line of color over it to give appareance if black outlines with a fill
        public function drawLines():Void{
                reset();
                this["Lines"].clear();
                
                if(headPoint != null){
                        while(getNextDataPoint() != null){
                                this["Lines"].lineStyle(3.8, 0x000000, 100);
                                this["Lines"].moveTo(getDataPoint()._x, getDataPoint()._y);
                                this["Lines"].lineTo(getNextDataPoint()._x, getNextDataPoint()._y);
                                this["Lines"].lineStyle(2, Number(myColor), 100);
                                this["Lines"].moveTo(getDataPoint()._x, getDataPoint()._y);
                                this["Lines"].lineTo(getNextDataPoint()._x, getNextDataPoint()._y);
                                advance();
                                updateAfterEvent();
                        }
                }
        }
        
        /*public function drawLines_Old(customLine:String):Void{
        reset();
        var i = 1;
        var lineName;
        if(_parent[varName + "Lines"]._x == undefined){
        _parent.createEmptyMovieClip((varName + "Lines"), 1);
}
        while(getNextDataPoint() != undefined){
        lineName = varName + customLine + i;
        if(_parent[varName + "Lines"][lineName]._x == undefined){
        _parent[varName + "Lines"].attachMovie(customLine, lineName, _parent[varName + "Lines"].getNextHighestDepth());
}
        _parent[varName + "Lines"][lineName]._rotation = 0;
        _parent[varName + "Lines"][lineName]._width = getDistance(_parent[varName + "Points"][varName + i], _parent[varName + "Points"][varName + (i + 1)]);
        _parent[varName + "Lines"][lineName]._rotation = getAngle(_parent[varName + "Points"][varName + i], _parent[varName + "Points"][varName + (i + 1)]);
        _parent[varName + "Lines"][lineName]._x = _parent[varName + "Points"][varName + i]._x;
        _parent[varName + "Lines"][lineName]._y = _parent[varName + "Points"][varName + i]._y;
        advance();
        i++;
}
}*/
        
        
        
        //Returns the distance between two points.  Used for drawing a line between the two points.
        public function getDistance(point1, point2):Number{
                var a, b, c;
                a = Math.abs(point2._x - point1._x);
                b = Math.abs(point2._y - point1._y);
                c = Math.sqrt(a*a + b*b);
                return c;
        }
        
        //Returns an angle in degrees
        public function getAngle(point1, point2):Number{
                var a, b, c, angle;
                a = point2._x - point1._x;
                b = point2._y - point1._y;
                //c = Math.sqrt(a*a + b*b);
                angle = Math.atan(b/a); //this is in radians
                angle = angle*180/Math.PI; //converts radians to degrees
                return angle;
        }
        
        public function animateDots(goalsPassed:DataPointSet):Void{
                reset();
                var goals:DataPointSet = goalsPassed;
                goals.reset();
                var newX, newY;
                var i = 1;
                var instanceName:String;
                if(headPoint != null){
                        do{
                                instanceName = varName + i;
                                newX = goals.getDataPoint().xcoord;
                                newY = goals.getDataPoint().ycoord;
                                this["Points"][instanceName].animateTo(newX, newY);
                                
                                advance();
                                goals.advance();
                                i++;
                        }while(!goals.atEnd());
                }
        }
        
        public function changeColor(newColor:Object):Void{
                myColor = newColor;
                reset();
                while(getDataPoint() != undefined){
                        getDataPoint().color = newColor;
                        advance();
                }
                drawLines();
        }
        
        
        
        
        
        
        public function set color(value:Object):Void{
                myColor = value;
        }
        
        
        public function get color():Object{
                return myColor;
        }
        
        
        public function toString():String{
                return (pointCount + " points in chain");
        }
}