1/* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.
2
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is
8furnished to do so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19THE SOFTWARE. */
20
21var glMatrix = require("./common.js");
22
23/**
24 * @class 4 Dimensional Vector
25 * @name vec4
26 */
27var vec4 = {};
28
29/**
30 * Creates a new, empty vec4
31 *
32 * @returns {vec4} a new 4D vector
33 */
34vec4.create = function() {
35    var out = new glMatrix.ARRAY_TYPE(4);
36    out[0] = 0;
37    out[1] = 0;
38    out[2] = 0;
39    out[3] = 0;
40    return out;
41};
42
43/**
44 * Creates a new vec4 initialized with values from an existing vector
45 *
46 * @param {vec4} a vector to clone
47 * @returns {vec4} a new 4D vector
48 */
49vec4.clone = function(a) {
50    var out = new glMatrix.ARRAY_TYPE(4);
51    out[0] = a[0];
52    out[1] = a[1];
53    out[2] = a[2];
54    out[3] = a[3];
55    return out;
56};
57
58/**
59 * Creates a new vec4 initialized with the given values
60 *
61 * @param {Number} x X component
62 * @param {Number} y Y component
63 * @param {Number} z Z component
64 * @param {Number} w W component
65 * @returns {vec4} a new 4D vector
66 */
67vec4.fromValues = function(x, y, z, w) {
68    var out = new glMatrix.ARRAY_TYPE(4);
69    out[0] = x;
70    out[1] = y;
71    out[2] = z;
72    out[3] = w;
73    return out;
74};
75
76/**
77 * Copy the values from one vec4 to another
78 *
79 * @param {vec4} out the receiving vector
80 * @param {vec4} a the source vector
81 * @returns {vec4} out
82 */
83vec4.copy = function(out, a) {
84    out[0] = a[0];
85    out[1] = a[1];
86    out[2] = a[2];
87    out[3] = a[3];
88    return out;
89};
90
91/**
92 * Set the components of a vec4 to the given values
93 *
94 * @param {vec4} out the receiving vector
95 * @param {Number} x X component
96 * @param {Number} y Y component
97 * @param {Number} z Z component
98 * @param {Number} w W component
99 * @returns {vec4} out
100 */
101vec4.set = function(out, x, y, z, w) {
102    out[0] = x;
103    out[1] = y;
104    out[2] = z;
105    out[3] = w;
106    return out;
107};
108
109/**
110 * Adds two vec4's
111 *
112 * @param {vec4} out the receiving vector
113 * @param {vec4} a the first operand
114 * @param {vec4} b the second operand
115 * @returns {vec4} out
116 */
117vec4.add = function(out, a, b) {
118    out[0] = a[0] + b[0];
119    out[1] = a[1] + b[1];
120    out[2] = a[2] + b[2];
121    out[3] = a[3] + b[3];
122    return out;
123};
124
125/**
126 * Subtracts vector b from vector a
127 *
128 * @param {vec4} out the receiving vector
129 * @param {vec4} a the first operand
130 * @param {vec4} b the second operand
131 * @returns {vec4} out
132 */
133vec4.subtract = function(out, a, b) {
134    out[0] = a[0] - b[0];
135    out[1] = a[1] - b[1];
136    out[2] = a[2] - b[2];
137    out[3] = a[3] - b[3];
138    return out;
139};
140
141/**
142 * Alias for {@link vec4.subtract}
143 * @function
144 */
145vec4.sub = vec4.subtract;
146
147/**
148 * Multiplies two vec4's
149 *
150 * @param {vec4} out the receiving vector
151 * @param {vec4} a the first operand
152 * @param {vec4} b the second operand
153 * @returns {vec4} out
154 */
155vec4.multiply = function(out, a, b) {
156    out[0] = a[0] * b[0];
157    out[1] = a[1] * b[1];
158    out[2] = a[2] * b[2];
159    out[3] = a[3] * b[3];
160    return out;
161};
162
163/**
164 * Alias for {@link vec4.multiply}
165 * @function
166 */
167vec4.mul = vec4.multiply;
168
169/**
170 * Divides two vec4's
171 *
172 * @param {vec4} out the receiving vector
173 * @param {vec4} a the first operand
174 * @param {vec4} b the second operand
175 * @returns {vec4} out
176 */
177vec4.divide = function(out, a, b) {
178    out[0] = a[0] / b[0];
179    out[1] = a[1] / b[1];
180    out[2] = a[2] / b[2];
181    out[3] = a[3] / b[3];
182    return out;
183};
184
185/**
186 * Alias for {@link vec4.divide}
187 * @function
188 */
189vec4.div = vec4.divide;
190
191/**
192 * Returns the minimum of two vec4's
193 *
194 * @param {vec4} out the receiving vector
195 * @param {vec4} a the first operand
196 * @param {vec4} b the second operand
197 * @returns {vec4} out
198 */
199vec4.min = function(out, a, b) {
200    out[0] = Math.min(a[0], b[0]);
201    out[1] = Math.min(a[1], b[1]);
202    out[2] = Math.min(a[2], b[2]);
203    out[3] = Math.min(a[3], b[3]);
204    return out;
205};
206
207/**
208 * Returns the maximum of two vec4's
209 *
210 * @param {vec4} out the receiving vector
211 * @param {vec4} a the first operand
212 * @param {vec4} b the second operand
213 * @returns {vec4} out
214 */
215vec4.max = function(out, a, b) {
216    out[0] = Math.max(a[0], b[0]);
217    out[1] = Math.max(a[1], b[1]);
218    out[2] = Math.max(a[2], b[2]);
219    out[3] = Math.max(a[3], b[3]);
220    return out;
221};
222
223/**
224 * Scales a vec4 by a scalar number
225 *
226 * @param {vec4} out the receiving vector
227 * @param {vec4} a the vector to scale
228 * @param {Number} b amount to scale the vector by
229 * @returns {vec4} out
230 */
231vec4.scale = function(out, a, b) {
232    out[0] = a[0] * b;
233    out[1] = a[1] * b;
234    out[2] = a[2] * b;
235    out[3] = a[3] * b;
236    return out;
237};
238
239/**
240 * Adds two vec4's after scaling the second operand by a scalar value
241 *
242 * @param {vec4} out the receiving vector
243 * @param {vec4} a the first operand
244 * @param {vec4} b the second operand
245 * @param {Number} scale the amount to scale b by before adding
246 * @returns {vec4} out
247 */
248vec4.scaleAndAdd = function(out, a, b, scale) {
249    out[0] = a[0] + (b[0] * scale);
250    out[1] = a[1] + (b[1] * scale);
251    out[2] = a[2] + (b[2] * scale);
252    out[3] = a[3] + (b[3] * scale);
253    return out;
254};
255
256/**
257 * Calculates the euclidian distance between two vec4's
258 *
259 * @param {vec4} a the first operand
260 * @param {vec4} b the second operand
261 * @returns {Number} distance between a and b
262 */
263vec4.distance = function(a, b) {
264    var x = b[0] - a[0],
265        y = b[1] - a[1],
266        z = b[2] - a[2],
267        w = b[3] - a[3];
268    return Math.sqrt(x*x + y*y + z*z + w*w);
269};
270
271/**
272 * Alias for {@link vec4.distance}
273 * @function
274 */
275vec4.dist = vec4.distance;
276
277/**
278 * Calculates the squared euclidian distance between two vec4's
279 *
280 * @param {vec4} a the first operand
281 * @param {vec4} b the second operand
282 * @returns {Number} squared distance between a and b
283 */
284vec4.squaredDistance = function(a, b) {
285    var x = b[0] - a[0],
286        y = b[1] - a[1],
287        z = b[2] - a[2],
288        w = b[3] - a[3];
289    return x*x + y*y + z*z + w*w;
290};
291
292/**
293 * Alias for {@link vec4.squaredDistance}
294 * @function
295 */
296vec4.sqrDist = vec4.squaredDistance;
297
298/**
299 * Calculates the length of a vec4
300 *
301 * @param {vec4} a vector to calculate length of
302 * @returns {Number} length of a
303 */
304vec4.length = function (a) {
305    var x = a[0],
306        y = a[1],
307        z = a[2],
308        w = a[3];
309    return Math.sqrt(x*x + y*y + z*z + w*w);
310};
311
312/**
313 * Alias for {@link vec4.length}
314 * @function
315 */
316vec4.len = vec4.length;
317
318/**
319 * Calculates the squared length of a vec4
320 *
321 * @param {vec4} a vector to calculate squared length of
322 * @returns {Number} squared length of a
323 */
324vec4.squaredLength = function (a) {
325    var x = a[0],
326        y = a[1],
327        z = a[2],
328        w = a[3];
329    return x*x + y*y + z*z + w*w;
330};
331
332/**
333 * Alias for {@link vec4.squaredLength}
334 * @function
335 */
336vec4.sqrLen = vec4.squaredLength;
337
338/**
339 * Negates the components of a vec4
340 *
341 * @param {vec4} out the receiving vector
342 * @param {vec4} a vector to negate
343 * @returns {vec4} out
344 */
345vec4.negate = function(out, a) {
346    out[0] = -a[0];
347    out[1] = -a[1];
348    out[2] = -a[2];
349    out[3] = -a[3];
350    return out;
351};
352
353/**
354 * Returns the inverse of the components of a vec4
355 *
356 * @param {vec4} out the receiving vector
357 * @param {vec4} a vector to invert
358 * @returns {vec4} out
359 */
360vec4.inverse = function(out, a) {
361  out[0] = 1.0 / a[0];
362  out[1] = 1.0 / a[1];
363  out[2] = 1.0 / a[2];
364  out[3] = 1.0 / a[3];
365  return out;
366};
367
368/**
369 * Normalize a vec4
370 *
371 * @param {vec4} out the receiving vector
372 * @param {vec4} a vector to normalize
373 * @returns {vec4} out
374 */
375vec4.normalize = function(out, a) {
376    var x = a[0],
377        y = a[1],
378        z = a[2],
379        w = a[3];
380    var len = x*x + y*y + z*z + w*w;
381    if (len > 0) {
382        len = 1 / Math.sqrt(len);
383        out[0] = x * len;
384        out[1] = y * len;
385        out[2] = z * len;
386        out[3] = w * len;
387    }
388    return out;
389};
390
391/**
392 * Calculates the dot product of two vec4's
393 *
394 * @param {vec4} a the first operand
395 * @param {vec4} b the second operand
396 * @returns {Number} dot product of a and b
397 */
398vec4.dot = function (a, b) {
399    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
400};
401
402/**
403 * Performs a linear interpolation between two vec4's
404 *
405 * @param {vec4} out the receiving vector
406 * @param {vec4} a the first operand
407 * @param {vec4} b the second operand
408 * @param {Number} t interpolation amount between the two inputs
409 * @returns {vec4} out
410 */
411vec4.lerp = function (out, a, b, t) {
412    var ax = a[0],
413        ay = a[1],
414        az = a[2],
415        aw = a[3];
416    out[0] = ax + t * (b[0] - ax);
417    out[1] = ay + t * (b[1] - ay);
418    out[2] = az + t * (b[2] - az);
419    out[3] = aw + t * (b[3] - aw);
420    return out;
421};
422
423/**
424 * Generates a random vector with the given scale
425 *
426 * @param {vec4} out the receiving vector
427 * @param {Number} [scale] Length of the resulting vector. If ommitted, a unit vector will be returned
428 * @returns {vec4} out
429 */
430vec4.random = function (out, scale) {
431    scale = scale || 1.0;
432
433    //TODO: This is a pretty awful way of doing this. Find something better.
434    out[0] = glMatrix.RANDOM();
435    out[1] = glMatrix.RANDOM();
436    out[2] = glMatrix.RANDOM();
437    out[3] = glMatrix.RANDOM();
438    vec4.normalize(out, out);
439    vec4.scale(out, out, scale);
440    return out;
441};
442
443/**
444 * Transforms the vec4 with a mat4.
445 *
446 * @param {vec4} out the receiving vector
447 * @param {vec4} a the vector to transform
448 * @param {mat4} m matrix to transform with
449 * @returns {vec4} out
450 */
451vec4.transformMat4 = function(out, a, m) {
452    var x = a[0], y = a[1], z = a[2], w = a[3];
453    out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
454    out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
455    out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
456    out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
457    return out;
458};
459
460/**
461 * Transforms the vec4 with a quat
462 *
463 * @param {vec4} out the receiving vector
464 * @param {vec4} a the vector to transform
465 * @param {quat} q quaternion to transform with
466 * @returns {vec4} out
467 */
468vec4.transformQuat = function(out, a, q) {
469    var x = a[0], y = a[1], z = a[2],
470        qx = q[0], qy = q[1], qz = q[2], qw = q[3],
471
472        // calculate quat * vec
473        ix = qw * x + qy * z - qz * y,
474        iy = qw * y + qz * x - qx * z,
475        iz = qw * z + qx * y - qy * x,
476        iw = -qx * x - qy * y - qz * z;
477
478    // calculate result * inverse quat
479    out[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy;
480    out[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz;
481    out[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx;
482    out[3] = a[3];
483    return out;
484};
485
486/**
487 * Perform some operation over an array of vec4s.
488 *
489 * @param {Array} a the array of vectors to iterate over
490 * @param {Number} stride Number of elements between the start of each vec4. If 0 assumes tightly packed
491 * @param {Number} offset Number of elements to skip at the beginning of the array
492 * @param {Number} count Number of vec4s to iterate over. If 0 iterates over entire array
493 * @param {Function} fn Function to call for each vector in the array
494 * @param {Object} [arg] additional argument to pass to fn
495 * @returns {Array} a
496 * @function
497 */
498vec4.forEach = (function() {
499    var vec = vec4.create();
500
501    return function(a, stride, offset, count, fn, arg) {
502        var i, l;
503        if(!stride) {
504            stride = 4;
505        }
506
507        if(!offset) {
508            offset = 0;
509        }
510
511        if(count) {
512            l = Math.min((count * stride) + offset, a.length);
513        } else {
514            l = a.length;
515        }
516
517        for(i = offset; i < l; i += stride) {
518            vec[0] = a[i]; vec[1] = a[i+1]; vec[2] = a[i+2]; vec[3] = a[i+3];
519            fn(vec, vec, arg);
520            a[i] = vec[0]; a[i+1] = vec[1]; a[i+2] = vec[2]; a[i+3] = vec[3];
521        }
522
523        return a;
524    };
525})();
526
527/**
528 * Returns a string representation of a vector
529 *
530 * @param {vec4} vec vector to represent as a string
531 * @returns {String} string representation of the vector
532 */
533vec4.str = function (a) {
534    return 'vec4(' + a[0] + ', ' + a[1] + ', ' + a[2] + ', ' + a[3] + ')';
535};
536
537module.exports = vec4;
538