1 /**
  2  * The Render Engine
  3  * Vector2D
  4  *
  5  * @fileoverview A Vector2D class
  6  *
  7  * @author: Brett Fattori (brettf@renderengine.com)
  8  * @author: $Author: bfattori $
  9  * @version: $Revision: 1555 $
 10  *
 11  * Copyright (c) 2011 Brett Fattori (brettf@renderengine.com)
 12  *
 13  * Permission is hereby granted, free of charge, to any person obtaining a copy
 14  * of this software and associated documentation files (the "Software"), to deal
 15  * in the Software without restriction, including without limitation the rights
 16  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 17  * copies of the Software, and to permit persons to whom the Software is
 18  * furnished to do so, subject to the following conditions:
 19  *
 20  * The above copyright notice and this permission notice shall be included in
 21  * all copies or substantial portions of the Software.
 22  *
 23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 26  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 28  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 29  * THE SOFTWARE.
 30  *
 31  */
 32 
 33 // The class this file defines and its required classes
 34 R.Engine.define({
 35     "class":"R.math.Vector2D",
 36     "requires":[
 37         "R.math.Math2D",
 38         "R.math.Point2D"
 39     ]
 40 });
 41 
 42 /**
 43  * @class A 2D vector class with helpful manipulation methods.
 44  *
 45  * @param x {R.math.Vector2D|Number} If this arg is a Vector2D, its values will be
 46  *                           copied into the new vector.  If a number,
 47  *                           the X length of the vector.
 48  * @param y {Number} The Y length of the vector.  Only required if X
 49  *                   was a number.
 50  * @constructor
 51  * @description Create a new 2D Vector
 52  * @extends R.math.Point2D
 53  */
 54 R.math.Vector2D = function () {
 55     "use strict";
 56     return R.math.Point2D.extend(/** @scope R.math.Vector2D.prototype */{
 57 
 58         /**
 59          * @private
 60          */
 61         constructor:function (x, y) {
 62             return this.base(x, y);
 63         },
 64 
 65         /**
 66          * A mutator method that normalizes this vector, returning a unit length vector.
 67          * @return {R.math.Vector2D} This vector, normalized
 68          * @see #len
 69          */
 70         normalize:function () {
 71             var ln = this.len();
 72             if (ln != 0) {
 73                 this.x /= ln;
 74                 this.y /= ln;
 75             }
 76             return this;
 77         },
 78 
 79         /**
 80          * Get the magnitude/length of this vector.
 81          *
 82          * @return {Number} A value representing the length (magnitude) of the vector.
 83          */
 84         len:function () {
 85             return Math.sqrt((this.x * this.x) + (this.y * this.y));
 86         },
 87 
 88         /**
 89          * Set the magnitude/length of this vector without altering the angle
 90          * @param len {Number} The length
 91          */
 92         setLen:function (len) {
 93             var angle = R.math.Math2D.degToRad(this.getAngle());
 94             this.x = Math.cos(angle) * len;
 95             this.y = Math.sin(angle) * len;
 96             if (Math.abs(this.x) < 0.00000001) this.x = 0;
 97             if (Math.abs(this.y) < 0.00000001) this.y = 0;
 98         },
 99 
100         /**
101          * Get the angle, in degrees, of this vector.
102          * @return {Number}
103          */
104         getAngle:function () {
105             return R.math.Math2D.radToDeg(Math.atan2(this.y, this.x));
106         },
107 
108         /**
109          * Set the angle of this vector without altering the length
110          * @param angle {Number} The angle
111          */
112         setAngle:function (angle) {
113             var len = this.len();
114             this.x = Math.cos(R.math.Math2D.degToRad(angle)) * len;
115             this.y = Math.sin(R.math.Math2D.degToRad(angle)) * len;
116         },
117 
118         /**
119          * Is the vector to the right or left of this one?
120          * @param vector {R.math.Vector2D} The vectore to compare against
121          * @return {Number} -1 (left) 1 (right)
122          */
123         getSign:function (vector) {
124             return this.rightNormal().dot(vector) < 0 ? -1 : 1;
125         },
126 
127         /**
128          * Get the dot product of this vector and another.
129          * @param vector {R.math.Vector2D} The Point to perform the operation against.
130          * @return {Number} The dot product
131          */
132         dot:function (vector) {
133             Assert(vector != null, "Dot product to undefined vector");
134             return (this.x * vector.x) + (this.y * vector.y);
135         },
136 
137         /**
138          * A mutator method that gets the cross product of this vector and another.
139          * @param vector {R.math.Vector2D} The vector to perform the operation against.
140          * @return {R.math.Vector2D} This vector
141          */
142         cross:function (vector) {
143             Assert(vector != null, "Cross multiply with undefined vector");
144             this.x = this.y - vector.y;
145             this.y = vector.x - this.x;
146             // this.z = (this.x * vector.y) - (this.y * vector.x);
147             return this;
148         },
149 
150         truncate:function (max) {
151             if (this.len() > max) {
152                 this.setLen(max);
153             }
154             return this;
155         },
156 
157         /**
158          * Returns the angle (in degrees) between two vectors.  This assumes that the
159          * point is being used to represent a vector, and that the supplied point
160          * is also a vector.
161          *
162          * @param vector {R.math.Vector2D} The vector to perform the angular determination against
163          * @return {Number} The angle between two vectors, in degrees
164          */
165         angleBetween:function (vector) {
166             Assert(vector != null, "Angle between undefined vector");
167             var t = R.clone(this).normalize(), v = R.clone(vector).normalize(),
168                 a = Math.acos(t.dot(v));
169 
170             t.destroy();
171             v.destroy();
172             return R.math.Math2D.radToDeg(a);
173 
174             //var v1 = $V([this.x,this.y,1]), v2 = $V([vector.x,vector.y,1]);
175             //return R.math.Math2D.radToDeg(v1.angleFrom(v2));
176         },
177 
178         /**
179          * Returns the signed angle (in degrees) between two vectors.  This assumes that the
180          * point is being used to represent a vector, and that the supplied point
181          * is also a vector.
182          *
183          * @param vector {R.math.Vector2D} The vector to perform the angular determination against
184          * @return {Number} The angle between two vectors, in degrees
185          */
186         signedAngleBetween:function (vector) {
187             Assert(vector != null, "Angle between undefined vector");
188             var t = R.clone(this).normalize(), v = R.clone(vector).normalize(),
189                 a = Math.atan2(v.y, v.x) - Math.atan2(t.y, t.x);
190 
191             t.destroy();
192             v.destroy();
193             return R.math.Math2D.radToDeg(a);
194         },
195 
196         /**
197          * Returns <tt>true</tt> if this vector is parallel to <tt>vector</tt>.
198          * @param vector {R.math.Vector2D} The vector to compare against
199          * @return {Boolean}
200          */
201         isParallelTo:function (vector) {
202             Assert(vector != null, "Parallel to undefined vector");
203             var v1 = $V([this.x, this.y, 1]), v2 = $V([vector.x, vector.y, 1]);
204             return v1.isParallelTo(v2);
205         },
206 
207         /**
208          * Returns <tt>true</tt> if this vector is anti-parallel to <tt>vector</tt>.
209          * @param vector {R.math.Vector2D} The vector to compare against
210          * @return {Boolean}
211          */
212         isAntiparallelTo:function (vector) {
213             Assert(vector != null, "Anti-parallel to undefined vector");
214             var v1 = $V([this.x, this.y, 1]), v2 = $V([vector.x, vector.y, 1]);
215             return v1.isAntiparallelTo(v2);
216         },
217 
218         /**
219          * Returns <tt>true</tt> if this vector is perpendicular to <tt>vector</tt>.
220          * @param vector {R.math.Vector2D} The vector to compare against
221          * @return {Boolean}
222          */
223         isPerpendicularTo:function (vector) {
224             Assert(vector != null, "Perpendicular to undefined vector");
225             var v1 = $V([this.x, this.y, 1]), v2 = $V([vector.x, vector.y, 1]);
226             return v1.isPependicularTo(v2);
227         },
228 
229         /**
230          * Mutator method that modifies the vector rotated <tt>angle</tt> degrees about
231          * the vector defined by <tt>axis</tt>.
232          *
233          * @param angle {Number} The rotation angle in degrees
234          * @param axis {R.math.Vector2D} The axis to rotate about
235          * @return {R.math.Vector2D} This vector
236          */
237         rotate:function (angle, axis) {
238             var v1 = $V([this.x, this.y, 1]);
239             var v3 = v1.rotate(R.math.Math2D.degToRad(angle), axis);
240             this.x = v3.elements[0];
241             this.y = v3.elements[1];
242             return this;
243         },
244 
245         /**
246          * Project this vector onto <tt>vector</tt>.
247          *
248          * @param vector {R.math.Vector2D} The vector to project onto
249          * @return {R.math.Vector2D}
250          */
251         projectOnto:function (vector) {
252             Assert(vector != null, "Project onto undefined vector");
253             var proj = R.math.Vector2D.create(0, 0), v = vector, dp = this.dot(vector);
254             proj.set((dp / (v.x * v.x + v.y * v.y)) * v.x, (dp / (v.x * v.x + v.y * v.y)) * v.y);
255             return proj;
256         },
257 
258         /**
259          * Get the right-hand normal of this vector.  The left-hand
260          * normal would simply be <tt>this.rightNormal().neg()</tt>.
261          * @return {R.math.Vector2D}
262          */
263         rightNormal:function () {
264             return R.math.Vector2D.create(-this.y, this.x).normalize();
265         },
266 
267         /**
268          * Get the perproduct (sign) of this vector and <tt>vector</tt>.  Returns
269          * -1 if <tt>vector</tt> is to the left, or 1 if it is to the right
270          * of this vector.
271          * @param vector {R.math.Vector2D} The other vector
272          * @return {Number}
273          */
274         perProduct:function (vector) {
275             Assert(vector != null, "Per-product with undefined vector");
276             return this.dot(vector.rightNormal());
277         }
278 
279     }, /** @scope R.math.Vector2D.prototype */{
280         /**
281          * Return the classname of the this object
282          * @return {String} "R.math.Vector2D"
283          */
284         getClassName:function () {
285             return "R.math.Vector2D";
286         },
287 
288         /** @private */
289         resolved:function () {
290             R.math.Vector2D.ZERO = R.math.Vector2D.create(0, 0);
291             R.math.Vector2D.UP = R.math.Vector2D.create(0, -1);
292             R.math.Vector2D.LEFT = R.math.Vector2D.create(-1, 0);
293             R.math.Vector2D.DOWN = R.math.Vector2D.create(0, 1);
294             R.math.Vector2D.RIGHT = R.math.Vector2D.create(1, 0);
295             if (Object.freeze) {
296                 Object.freeze(R.math.Vector2D.ZERO);
297                 Object.freeze(R.math.Vector2D.UP);
298                 Object.freeze(R.math.Vector2D.LEFT);
299                 Object.freeze(R.math.Vector2D.DOWN);
300                 Object.freeze(R.math.Vector2D.RIGHT);
301             }
302 
303         },
304 
305         /**
306          * The "zero" vector. This vector should not be modified.
307          * @type {R.math.Vector2D}
308          * @memberOf R.math.Vector2D
309          */
310         ZERO:null,
311 
312         /**
313          * The normalized "up" vector. This vector should not be modified.
314          * @type {R.math.Vector2D}
315          * @memberOf R.math.Vector2D
316          */
317         UP:null,
318 
319         /**
320          * The normalized "left" vector. This vector should not be modified.
321          * @type {R.math.Vector2D}
322          * @memberOf R.math.Vector2D
323          */
324         LEFT:null,
325 
326         /**
327          * The normalized "down" vector. This vector should not be modified.
328          * @type {R.math.Vector2D}
329          * @memberOf R.math.Vector2D
330          */
331         DOWN:null,
332 
333         /**
334          * The normalized "right" vector. This vector should not be modified.
335          * @type {R.math.Vector2D}
336          * @memberOf R.math.Vector2D
337          */
338         RIGHT:null
339     });
340 
341 };