1 /**
  2  * Copyright (C) 2009-2011 Klaus Reimer <k@ailis.de>
  3  * See LICENSE.txt for licensing information.
  4  * 
  5  * @require twodee.js
  6  */
  7 
  8 /**
  9  * Constructs a new empty vector.
 10  * 
 11  * @param {number=} x
 12  *            The X coordinate (Optional)
 13  * @param {number=} y
 14  *            The Y coordinate (Optional)
 15  * 
 16  * @constructor
 17  * @class
 18  * A vector with two elements.
 19  */
 20 twodee.Vector = function(x, y)
 21 {
 22     if (x) this.x = x;
 23     if (y) this.y = y;
 24     twodee.Vector.counter++;
 25 };
 26 
 27 /**
 28  * Instance counter.
 29  * 
 30  * @type {number}
 31  */
 32 twodee.Vector.counter = 0;
 33 
 34 /**
 35  * The X coordinate.
 36  * 
 37  * @type {number}
 38  */
 39 twodee.Vector.prototype.x = 0;
 40 
 41 /**
 42  * The Y coordinate.
 43  * 
 44  * @type {number}
 45  */
 46 twodee.Vector.prototype.y = 0;
 47 
 48 /**
 49  * Returns a copy of this vector.
 50  * 
 51  * @param {twodee.Vector=} v
 52  *            Optional target vector
 53  * @return {twodee.Vector} A copy of this vector or the target vector
 54  */
 55 twodee.Vector.prototype.copy = function(v)
 56 {
 57     return (v ? v : new twodee.Vector()).set(this.x, this.y);
 58 };
 59 
 60 /**
 61  * Checks if this vector is a zero vector.
 62  * 
 63  * @return {boolean} True if vector is a zero vector, false if not
 64  */
 65 twodee.Vector.prototype.isZero = function()
 66 {
 67     return !this.x && !this.y; 
 68 };
 69 
 70 /**
 71  * Sets the vector coordinates.
 72  * 
 73  * @param {number} x
 74  *            The X coordinate
 75  * @param {number} y
 76  *            The Y coordinate
 77  * @return {twodee.Vector}
 78  *            This vector
 79  */            
 80 twodee.Vector.prototype.set = function(x, y)
 81 {
 82     this.x = x;
 83     this.y = y;
 84     return this;
 85 };
 86 
 87 /**
 88  * Adds the coordinates of the specified vector to this one.
 89  * 
 90  * @param {twodee.Vector} v
 91  *            The vector to add
 92  * @return {twodee.Vector} This vector
 93  */
 94 twodee.Vector.prototype.add = function(v)
 95 {
 96     this.x += v.x;
 97     this.y += v.y;
 98     return this;
 99 };
100 
101 /**
102  * Subtracts the coordinates of the specified vector from this one.
103  * 
104  * @param {twodee.Vector} v
105  *            The vector to subtract
106  * @return {twodee.Vector} This vector
107  */
108 twodee.Vector.prototype.sub = function(v)
109 {
110     this.x -= v.x;
111     this.y -= v.y;
112     return this;
113 };
114 
115 /**
116  * Scales the vector with the specified factors.
117  * 
118  * @param {number} fx
119  *            The X factor
120  * @param {number=} fy
121  *            The Y factor (Optional. Defaults to fx)
122  * @return {twodee.Vector} This vector
123  */
124 twodee.Vector.prototype.scale = function(fx, fy)
125 {
126     this.x *= fx;
127     this.y *= fy === undefined ? fx : fy;
128     return this;
129 };
130 
131 /**
132  * Rotates the vector by the specified angle.
133  * 
134  * @param {number} angle
135  *            The rotation angle in anti-clockwise RAD.
136  * @return {twodee.Vector} This vector
137  */
138 twodee.Vector.prototype.rotate = function(angle)
139 {
140     var x, y, s, c;
141     
142     s = Math.sin(angle);
143     c = Math.cos(angle);
144     x = this.x;
145     y = this.y;
146     this.x = x * c - y * s;
147     this.y = x * s + y * c;
148     return this;
149 };
150 
151 /**
152  * Creates and returns the dot product of this vector and the specified one.
153  * 
154  * @param {twodee.Vector} v
155  *            The other vector
156  * @return {number}
157  *            The dot product
158  */
159 twodee.Vector.prototype.dot = function(v)
160 {
161     return this.x * v.x + this.y * v.y;
162 };
163 
164 /**
165  * Creates the cross product of this vector and the specified one and stores
166  * the result back into this vector.
167  * 
168  * @param {twodee.Vector} v
169  *            The other vector
170  * @return {twodee.Vector} This vector
171  */
172 twodee.Vector.prototype.cross = function(v)
173 {
174     var x, y;
175     
176     x = this.y * v.x - this.x * v.y;
177     y = this.x * v.y - this.y * v.x;
178     this.x = x;
179     this.y = y;
180     return this;
181 };
182 
183 /**
184  * Returns the length of the vector.
185  * 
186  * @return {number} The vector length
187  */
188 twodee.Vector.prototype.getLength = function()
189 {
190     return Math.sqrt(this.x * this.x + this.y * this.y);
191 };
192 
193 /**
194  * Converts this vector into a unit vector with length 1.
195  * 
196  * @return {twodee.Vector} This vector
197  */
198 twodee.Vector.prototype.toUnit = function()
199 {
200     var len;
201     
202     len = this.getLength();
203     this.x /= len;
204     this.y /= len;
205     return this;
206 };
207 
208 /**
209  * Calculate the angle between this vector and the specified one in radians.
210  * It returns always the shortest angle. If the angle is clock-wise then the
211  * returned number is positive. If it's counter-clock-wise then the angle is
212  * negative. So the return value of this method is always between -PI and +PI.
213  * 
214  * @param {twodee.Vector=} v
215  *            The second vector. Optional. If not specified then the angle
216  *            to the vector 0;1 is returned.
217  * @return {number}
218  *            The angle between the two vectors, in radians
219  */
220 twodee.Vector.prototype.getAngle = function(v)
221 {
222     var angle = Math.atan2(this.x, this.y);
223     if (v) angle -= Math.atan2(v.x, v.y);
224     if (angle > Math.PI) angle -= Math.PI * 2;
225     return angle;
226 };
227 
228 /**
229  * Transforms this vector with the specified matrix.
230  * 
231  * @param {twodee.Matrix} m
232  *            The matrix
233  * @return {twodee.Vector}
234  *            This vector
235  */
236 twodee.Vector.prototype.transform = function(m)
237 {
238     return this.set(
239         m.m00 * this.x + m.m01 * this.y + m.m02,
240         m.m10 * this.x + m.m11 * this.y + m.m12);
241 };
242 
243 /**
244  * Converts the vector into a JSON object with keys 'x' and 'y'.
245  * 
246  * @return {Object} The vector as a JSON object
247  */
248 twodee.Vector.prototype.toJSON = function()
249 {
250     return { "x": this.x, "y": this.y };
251 };
252 
253 /**
254  * Creates a new vector instance with the data read from the
255  * specified JSON object (with keys 'x', and 'y'). Returns null if data
256  * was empty.
257  * 
258  * @param {{x:number,y:number}} data
259  *            The vector as JSON object
260  * @return {twodee.Vector} The vector object or null if data was empty
261  */
262 twodee.Vector.fromJSON = function(data)
263 {
264     if (!data) return null;
265     return new twodee.Vector(data.x, data.y);
266 };
267 
268 /**
269  * Converts this vector into its orthogonal vector and returns a reference to
270  * it.
271  * 
272  * @return {twodee.Vector}
273  *             The reference to this vector
274  */
275 twodee.Vector.prototype.orthogonal = function()
276 {
277     var tmp = this.x;
278     this.x = -this.y;
279     this.y = tmp;
280     return this;
281 };
282