/*************************************************************************************************************** Vector2D Class For Actionscript 2.0 --------------------------------------------------------------------------------------------------------------- method listing: state-altering methods: Vector2D(x, y) - create vector and set the components to x and y Vector2D(v) - create vector and set the components to the components of vector v set(x, y) - set the components to x and y set(v) - set the components to the components of vector v plus(v1 ... vn) - add the components of the given vectors to the components of the vector minus(v1 ... vn) - subtract the components of given vectors from the components of the vector times(x, y) - multiply the components of vector by x and y times(scalar) - multiply the components of vector by number scalar times(v) - multiply the components of vector by the components of vector v invert() - invert (or reverse) the vector project(v) - set the vector to be the projection of the vector onto vector v reflect(v) - reflect the vector over vector v rotate(a) - rotate the vector by angle a in degrees swap(v) - swap the components of the vector and vector v (note: also alters vector v) state-safe methods: getLeftNormal() - return a new vector which is left hand normal to the vector getRightNormal() - return a new vector which is right hand normal to the vector cross(v) - return the magnitude of the cross product of the vector and vector v dot(v) - return the dot product of the vector and vector v angleBetween(v) - return the angle between the vector and vector v angleBetweenCos(v) - return the cosine of the angle between the vector and vector v angleBetweenSin(v) - return the sine of the angle between the vector and vector v comparison methods: isEqualTo(v) - test for equality between the vector and vector v isNormalTo(v) - test for normality between the vector and vector v utility methods: display(mc, color) - draw vector in given movieclip using a given color private methods: fixNumber() - convert all inputs to a number of fixed precision toString() - override of built in method to provide meaningful output property listing: x - virtual property representing the current x component of the vector (get/set) y - virtual property representing the current y component of the vector (get/set) angle - virtual property representing the current degree angle of the vector (get/set) magnitude - virtual property representing the current magnitude of the vector (get/set) Feel free to use this code as you wish, Have Fun! Please send bug reports && ( gripes || praise ) to nick[at]zambetti[dot]com ***************************************************************************************************************/ class Vector2D { // instance properties private var xValue:Number = 0; private var yValue:Number = 0; private var type:String = "Vector2D"; // // description: constructor for Vector2D // parameters: x:Number, y:Number || v:Vector2D // returns: reference to the new instance function Vector2D() { this.xValue = 0; this.yValue = 0; this.type = "Vector2D"; if (2 == arguments.length) { // x and y were passed this.xValue = this.fixNumber(arguments[0]); this.yValue = this.fixNumber(arguments[1]); } else if (1 == arguments.length) { if ("Vector2D" === arguments[0].type) { // an existing vector was passed this.xValue = arguments[0].x; this.yValue = arguments[0].y; } } } // // description: sets the properties of the vector // parameters: x:Number, y:Number || v:Vector2D // returns: reference to the vector public function set():Vector2D { this.xValue = 0; this.yValue = 0; this.type = "Vector2D"; if (2 == arguments.length) { // x and y were passed this.xValue = this.fixNumber(arguments[0]); this.yValue = this.fixNumber(arguments[1]); } else if (1 == arguments.length) { if ("Vector2D" === arguments[0].type) { // an existing vector was passed this.xValue = arguments[0].x; this.yValue = arguments[0].y; } } return this; } // // description: adds given vectors to the vector // parameters: v1:Vector2D ... vn;Vector2D // returns: reference to the vector public function plus():Vector2D { for (var i = 0; i < arguments.length; ++i) { if ("Vector2D" === arguments[i].type) { this.xValue += arguments[i].xValue; this.yValue += arguments[i].yValue; } } this.xValue = this.fixNumber(this.xValue); this.yValue = this.fixNumber(this.yValue); return this; } // // description: subtracts given vectors from the vector // parameters: v1:Vector2D ... vn:Vector2D // returns: reference to the vector public function minus():Vector2D { for (var i = 0; i < arguments.length; ++i) { if ("Vector2D" === arguments[i].type) { this.xValue -= arguments[i].xValue; this.yValue -= arguments[i].yValue; } } this.xValue = this.fixNumber(this.xValue); this.yValue = this.fixNumber(this.yValue); return this; } // // description: multiplies the components by the x and y args or by the given vector // parameters: x:number, y:number || scalar:number || v:Vector2D // returns: reference to the vector public function times():Vector2D { if (1 == arguments.length) { if ("Vector2D" === arguments[0].type) { this.xValue *= arguments[0].xValue; this.yValue *= arguments[0].yValue; } else { if (isNaN(Number(arguments[0]))) { this.xValue = this.yValue = 0; } else { this.xValue *= Number(arguments[0]); this.yValue *= Number(arguments[0]); } } } else if (2 == arguments.length) { isNaN(Number(arguments[0])) ? this.xValue = 0 : this.xValue *= Number(arguments[0]); isNaN(Number(arguments[1])) ? this.yValue = 0 : this.yValue *= Number(arguments[1]); } this.xValue = this.fixNumber(this.xValue); this.yValue = this.fixNumber(this.yValue); return this; } // // description: rotates the vector by the given angle (in degrees) // parameters: rotationAngle:Number // returns: reference to the vector public function rotate(rotationAngle:Number):Vector2D { if (isNaN(Number(rotationAngle))) { return this; } var currentMagnitude:Number = Math.sqrt(Math.pow(this.xValue, 2) + Math.pow(this.yValue, 2)); var newAngleRadians:Number = ((Math.atan2(this.yValue, this.xValue) * (180 / Math.PI)) + Number(rotationAngle)) * (Math.PI / 180); this.xValue = this.fixNumber(currentMagnitude * Math.cos(newAngleRadians)); this.yValue = this.fixNumber(currentMagnitude * Math.sin(newAngleRadians)); return this; } // // description: inverts the vector // parameters: none // returns: reference to the vector public function invert():Vector2D { this.xValue *= -1; this.yValue *= -1; return this; } // // description: projects the vector onto vector v // parameters: v:Vector2D // returns: reference to the vector public function project(v:Vector2D):Vector2D { if ("Vector2D" === v.type) { var scalar:Number = this.dot(v) / Math.pow(v.magnitude, 2); this.set(v); this.times(scalar); } return this; } // // description: relects the vector over vector v // parameters: v:Vector2D // returns: reference to the vector public function reflect(v:Vector2D):Vector2D { if ("Vector2D" === v.type) { var vAfterHorizReflect:Vector2D = new Vector2D(v.yValue, -v.xValue); var rotationAngle:Number = 2 * this.angleBetween(v); if (0 >= this.angleBetweenCos(vAfterHorizReflect)) { rotationAngle *= -1; } this.rotate(rotationAngle); } return this; } // // description: calculates the dot product of the vector and vector v // parameters: v:Vector2D // returns: number public function dot(v:Vector2D):Number { if ("Vector2D" === v.type) { return this.fixNumber((this.xValue * v.xValue) + (this.yValue * v.yValue)); } return 0; } // // description: calculates the cross product of the vector and vector v // parameters: v:Vector2D // returns: number (representing the magnitude of the theoretical vector result) public function cross(v:Vector2D):Number { if ("Vector2D" === v.type) { return Math.abs(this.fixNumber((this.xValue * v.yValue) - (this.yValue * v.xValue))); } return 0; } // // description: calculates the the angle between the vector and vector v in degrees // parameters: v:Vector2D // returns: number public function angleBetween(v:Vector2D):Number { if ("Vector2D" === v.type) { return this.fixNumber(Math.acos(this.dot(v) / (this.magnitude * v.magnitude)) * (180 / Math.PI)); } return 0; } // // description: calculates the sine of the angle between the vector and vector v // parameters: v:Vector2D // returns: number public function angleBetweenSin(v:Vector2D):Number { if ("Vector2D" === v.type) { return this.fixNumber(this.cross(v) / (this.magnitude * v.magnitude)); } return 0; } // // description: calculates the cosine of the angle between the vector and vector v // parameters: v:Vector2D // returns: number public function angleBetweenCos(v:Vector2D):Number { if ("Vector2D" === v.type) { return this.fixNumber(this.dot(v) / (this.magnitude * v.magnitude)); } return 0; } // // description: exchanges data of the vector and the given vector // parameters: v:Vector2D // returns: reference to the vector public function swap(v:Vector2D):Vector2D { if ("Vector2D" === v.type) { var tempX:Number = this.xValue; var tempY:Number = this.yValue; this.xValue = v.xValue; this.yValue = v.yValue; v.xValue = tempX; v.yValue = tempY; } return this; } // // description: creates a new vector which is normal (clockwise) to the vector // parameters: none // returns: reference to the newly created vector public function getRightNormal():Vector2D { return new Vector2D(this.yValue, -this.xValue); } // // description: creates a new vector which is normal (anti-clockwise) to the vector // parameters: none // returns: reference to the newly created vector public function getLeftNormal():Vector2D { return new Vector2D(-this.yValue, this.xValue); } // // description: tests if two vectors are normal to each other // parameters: v:Vector2D // returns: boolean indicating normality public function isNormalTo(v:Vector2D):Boolean { if ("Vector2D" === v.type) { return (this.dot(v) === 0); } else { return false; } } // // description: tests if two vectors are equal to each other // parameters: v:Vector2D // returns: boolean indicating equality public function isEqualTo(v:Vector2D):Boolean { if ("Vector2D" === v.type) { if ((this.xValue === v.xValue) && (this.yValue === v.yValue)) { return true; } } return false; } // // description: retrieves the current x value of the vector // parameters: none // returns: current value of x public function get x():Number { return this.xValue; } // // description: sets x value of the vector // parameters: newX:Number // returns: value of x BEFORE precision fix public function set x(newX:Number):Number { this.xValue = this.fixNumber(newX); } // // description: retrieves the current y value of the vector // parameters: none // returns: current value of y public function get y():Number { return this.yValue; } // // description: sets y value of the vector // parameters: newY:Number // returns: value of y BEFORE precision fix public function set y(newY:Number):Number { this.yValue = this.fixNumber(newY); } // // description: calculates the angle of the vector // parameters: none // returns: number representing angle in degrees public function get angle():Number { return this.fixNumber(Math.atan2(this.yValue, this.xValue) * (180 / Math.PI)); //return this.fixNumber(((Math.atan2(this.yValue, this.xValue)*(180/Math.PI))+360)%360); } // // description: sets the angle of the vector to the given angle (in degrees) // parameters: newAngle:Number // returns: number representing angle in degrees BEFORE precision fix public function set angle(newAngle:Number):Number { var angleRadians:Number = 0; if (!isNaN(Number(newAngle))) { angleRadians = Number(newAngle) * (Math.PI / 180); } var currentMagnitude:Number = Math.sqrt(Math.pow(this.xValue, 2) + Math.pow(this.yValue, 2)); this.xValue = this.fixNumber(currentMagnitude * Math.cos(angleRadians)); this.yValue = this.fixNumber(currentMagnitude * Math.sin(angleRadians)); } // // description: returns the magnitude of the vector (aka: length) // parameters: none // returns: 0 <= number public function get magnitude():Number { return this.fixNumber(Math.sqrt(Math.pow(this.xValue, 2) + Math.pow(this.yValue, 2))); } // // description: sets the magnitude (aka: length) to the given scalar // parameters: newMagnitude:Number // returns: magnitude after set operation public function set magnitude(newMagnitude:Number):Number { if (isNaN(Number(newMagnitude))) { this.xValue = this.yValue = 0; } var currentMagnitude:Number = Math.sqrt(Math.pow(this.xValue, 2) + Math.pow(this.yValue, 2)); if (0 < currentMagnitude) { this.times(Number(newMagnitude) / currentMagnitude); } else { this.yValue = 0; this.xValue = this.fixNumber(newMagnitude); } } // // description: draws a vector in a given mc using a given color // parameters: mc|movieClipReference color|hexNumber // returns: nothing public function display(mc:Object, color:Number) { if ("movieclip" === typeof mc) { mc.lineStyle(0, color, 100); mc.moveTo(0, 0); mc.lineTo(this.xValue, this.yValue); } return; } // // description: converts all numeric inputs to a number of fixed precision // parameters: numberValue:Number // returns: number rounded to a fixed precision private function fixNumber(numberValue:Number):Number { return isNaN(Number(numberValue)) ? 0 : Math.round(Number(numberValue) * 100000) / 100000; } // // description: override of toString() that produces meaningful output // parameters: none // returns: string private function toString():String { return "[" + this.xValue + "," + this.yValue + "]"; } }