HTML Preparation code:
x
 
1
<script src="https://cdn.rawgit.com/google/closure-library/master/closure/goog/base.js"></script>
2
<script src="http://numericjs.com/lib/numeric-1.2.6.min.js"></script>
3
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.1/gl-matrix-min.js"></script>
4
<script>
5
6
let index = 0;
7
const defaultCount = 100000000;
8
const floatArray = new Float64Array(defaultCount);
9
10
var total = 0;
11
// === Sylvester ===
12
// Vector and Matrix mathematics modules for JavaScript
13
// Copyright (c) 2007 James Coglan
14
// 
15
// Permission is hereby granted, free of charge, to any person obtaining
16
// a copy of this software and associated documentation files (the "Software"),
17
// to deal in the Software without restriction, including without limitation
18
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
19
// and/or sell copies of the Software, and to permit persons to whom the
20
// Software is furnished to do so, subject to the following conditions:
21
// 
22
// The above copyright notice and this permission notice shall be included
23
// in all copies or substantial portions of the Software.
24
// 
25
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
26
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
28
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31
// DEALINGS IN THE SOFTWARE.
32
33
var Sylvester = {
34
  version: '0.1.3',
35
  precision: 1e-6
36
};
37
38
function Vector() {}
39
Vector.prototype = {
40
41
  // Returns element i of the vector
42
  e: function(i) {
43
    return (i < 1 || i > this.elements.length) ? null : this.elements[i-1];
44
  },
45
46
  // Returns the number of elements the vector has
47
  dimensions: function() {
48
    return this.elements.length;
49
  },
50
51
  // Returns the modulus ('length') of the vector
52
  modulus: function() {
53
    return Math.sqrt(this.dot(this));
54
  },
55
56
  // Returns true iff the vector is equal to the argument
57
  eql: function(vector) {
58
    var n = this.elements.length;
59
    var V = vector.elements || vector;
60
    if (n != V.length) { return false; }
61
    do {
62
      if (Math.abs(this.elements[n-1] - V[n-1]) > Sylvester.precision) { return false; }
63
    } while (--n);
64
    return true;
65
  },
66
67
  // Returns a copy of the vector
68
  dup: function() {
69
    return Vector.create(this.elements);
70
  },
71
72
  // Maps the vector to another vector according to the given function
73
  map: function(fn) {
74
    var elements = [];
75
    this.each(function(x, i) {
76
      elements.push(fn(x, i));
77
    });
78
    return Vector.create(elements);
79
  },
80
  
81
  // Calls the iterator for each element of the vector in turn
82
  each: function(fn) {
83
    var n = this.elements.length, k = n, i;
84
    do { i = k - n;
85
      fn(this.elements[i], i+1);
86
    } while (--n);
87
  },
88
89
  // Returns a new vector created by normalizing the receiver
90
  toUnitVector: function() {
91
    var r = this.modulus();
92
    if (r === 0) { return this.dup(); }
93
    return this.map(function(x) { return x/r; });
94
  },
95
96
  // Returns the angle between the vector and the argument (also a vector)
97
  angleFrom: function(vector) {
98
    var V = vector.elements || vector;
99
    var n = this.elements.length, k = n, i;
100
    if (n != V.length) { return null; }
101
    var dot = 0, mod1 = 0, mod2 = 0;
102
    // Work things out in parallel to save time
103
    this.each(function(x, i) {
104
      dot += x * V[i-1];
105
      mod1 += x * x;
106
      mod2 += V[i-1] * V[i-1];
107
    });
108
    mod1 = Math.sqrt(mod1); mod2 = Math.sqrt(mod2);
109
    if (mod1*mod2 === 0) { return null; }
110
    var theta = dot / (mod1*mod2);
111
    if (theta < -1) { theta = -1; }
112
    if (theta > 1) { theta = 1; }
113
    return Math.acos(theta);
114
  },
115
116
  // Returns true iff the vector is parallel to the argument
117
  isParallelTo: function(vector) {
118
    var angle = this.angleFrom(vector);
119
    return (angle === null) ? null : (angle <= Sylvester.precision);
120
  },
121
122
  // Returns true iff the vector is antiparallel to the argument
123
  isAntiparallelTo: function(vector) {
124
    var angle = this.angleFrom(vector);
125
    return (angle === null) ? null : (Math.abs(angle - Math.PI) <= Sylvester.precision);
126
  },
127
128
  // Returns true iff the vector is perpendicular to the argument
129
  isPerpendicularTo: function(vector) {
130
    var dot = this.dot(vector);
131
    return (dot === null) ? null : (Math.abs(dot) <= Sylvester.precision);
132
  },
133
134
  // Returns the result of adding the argument to the vector
135
  add: function(vector) {
136
    var V = vector.elements || vector;
137
    if (this.elements.length != V.length) { return null; }
138
    return this.map(function(x, i) { return x + V[i-1]; });
139
  },
140
141
  // Returns the result of subtracting the argument from the vector
142
  subtract: function(vector) {
143
    var V = vector.elements || vector;
144
    if (this.elements.length != V.length) { return null; }
145
    return this.map(function(x, i) { return x - V[i-1]; });
146
  },
147
148
  // Returns the result of multiplying the elements of the vector by the argument
149
  multiply: function(k) {
150
    return this.map(function(x) { return x*k; });
151
  },
152
153
  x: function(k) { return this.multiply(k); },
154
155
  // Returns the scalar product of the vector with the argument
156
  // Both vectors must have equal dimensionality
157
  dot: function(vector) {
158
    var V = vector.elements || vector;
159
    var i, product = 0, n = this.elements.length;
160
    if (n != V.length) { return null; }
161
    do { product += this.elements[n-1] * V[n-1]; } while (--n);
162
    return product;
163
  },
164
165
  // Returns the vector product of the vector with the argument
166
  // Both vectors must have dimensionality 3
167
  cross: function(vector) {
168
    var B = vector.elements || vector;
169
    if (this.elements.length != 3 || B.length != 3) { return null; }
170
    var A = this.elements;
171
    return Vector.create([
172
      (A[1] * B[2]) - (A[2] * B[1]),
173
      (A[2] * B[0]) - (A[0] * B[2]),
174
      (A[0] * B[1]) - (A[1] * B[0])
175
    ]);
176
  },
177
178
  // Returns the (absolute) largest element of the vector
179
  max: function() {
180
    var m = 0, n = this.elements.length, k = n, i;
181
    do { i = k - n;
182
      if (Math.abs(this.elements[i]) > Math.abs(m)) { m = this.elements[i]; }
183
    } while (--n);
184
    return m;
185
  },
186
187
  // Returns the index of the first match found
188
  indexOf: function(x) {
189
    var index = null, n = this.elements.length, k = n, i;
190
    do { i = k - n;
191
      if (index === null && this.elements[i] == x) {
192
        index = i + 1;
193
      }
194
    } while (--n);
195
    return index;
196
  },
197
198
  // Returns a diagonal matrix with the vector's elements as its diagonal elements
199
  toDiagonalMatrix: function() {
200
    return Matrix.Diagonal(this.elements);
201
  },
202
203
  // Returns the result of rounding the elements of the vector
204
  round: function() {
205
    return this.map(function(x) { return Math.round(x); });
206
  },
207
208
  // Returns a copy of the vector with elements set to the given value if they
209
  // differ from it by less than Sylvester.precision
210
  snapTo: function(x) {
211
    return this.map(function(y) {
212
      return (Math.abs(y - x) <= Sylvester.precision) ? x : y;
213
    });
214
  },
215
216
  // Returns the vector's distance from the argument, when considered as a point in space
217
  distanceFrom: function(obj) {
218
    if (obj.anchor) { return obj.distanceFrom(this); }
219
    var V = obj.elements || obj;
220
    if (V.length != this.elements.length) { return null; }
221
    var sum = 0, part;
222
    this.each(function(x, i) {
223
      part = x - V[i-1];
224
      sum += part * part;
225
    });
226
    return Math.sqrt(sum);
227
  },
228
229
  // Returns true if the vector is point on the given line
230
  liesOn: function(line) {
231
    return line.contains(this);
232
  },
233
234
  // Return true iff the vector is a point in the given plane
235
  liesIn: function(plane) {
236
    return plane.contains(this);
237
  },
238
239
  // Rotates the vector about the given object. The object should be a 
240
  // point if the vector is 2D, and a line if it is 3D. Be careful with line directions!
241
  rotate: function(t, obj) {
242
    var V, R, x, y, z;
243
    switch (this.elements.length) {
244
      case 2:
245
        V = obj.elements || obj;
246
        if (V.length != 2) { return null; }
247
        R = Matrix.Rotation(t).elements;
248
        x = this.elements[0] - V[0];
249
        y = this.elements[1] - V[1];
250
        return Vector.create([
251
          V[0] + R[0][0] * x + R[0][1] * y,
252
          V[1] + R[1][0] * x + R[1][1] * y
253
        ]);
254
        break;
255
      case 3:
256
        if (!obj.direction) { return null; }
257
        var C = obj.pointClosestTo(this).elements;
258
        R = Matrix.Rotation(t, obj.direction).elements;
259
        x = this.elements[0] - C[0];
260
        y = this.elements[1] - C[1];
261
        z = this.elements[2] - C[2];
262
        return Vector.create([
263
          C[0] + R[0][0] * x + R[0][1] * y + R[0][2] * z,
264
          C[1] + R[1][0] * x + R[1][1] * y + R[1][2] * z,
265
          C[2] + R[2][0] * x + R[2][1] * y + R[2][2] * z
266
        ]);
267
        break;
268
      default:
269
        return null;
270
    }
271
  },
272
273
  // Returns the result of reflecting the point in the given point, line or plane
274
  reflectionIn: function(obj) {
275
    if (obj.anchor) {
276
      // obj is a plane or line
277
      var P = this.elements.slice();
278
      var C = obj.pointClosestTo(P).elements;
279
      return Vector.create([C[0] + (C[0] - P[0]), C[1] + (C[1] - P[1]), C[2] + (C[2] - (P[2] || 0))]);
280
    } else {
281
      // obj is a point
282
      var Q = obj.elements || obj;
283
      if (this.elements.length != Q.length) { return null; }
284
      return this.map(function(x, i) { return Q[i-1] + (Q[i-1] - x); });
285
    }
286
  },
287
288
  // Utility to make sure vectors are 3D. If they are 2D, a zero z-component is added
289
  to3D: function() {
290
    var V = this.dup();
291
    switch (V.elements.length) {
292
      case 3: break;
293
      case 2: V.elements.push(0); break;
294
      default: return null;
295
    }
296
    return V;
297
  },
298
299
  // Returns a string representation of the vector
300
  inspect: function() {
301
    return '[' + this.elements.join(', ') + ']';
302
  },
303
304
  // Set vector's elements from an array
305
  setElements: function(els) {
306
    this.elements = (els.elements || els).slice();
307
    return this;
308
  }
309
};
310
  
311
// Constructor function
312
Vector.create = function(elements) {
313
  var V = new Vector();
314
  return V.setElements(elements);
315
};
316
317
// i, j, k unit vectors
318
Vector.i = Vector.create([1,0,0]);
319
Vector.j = Vector.create([0,1,0]);
320
Vector.k = Vector.create([0,0,1]);
321
322
// Random vector of size n
323
Vector.Random = function(n) {
324
  var elements = [];
325
  do { elements.push(Math.random());
326
  } while (--n);
327
  return Vector.create(elements);
328
};
329
330
// Vector filled with zeros
331
Vector.Zero = function(n) {
332
  var elements = [];
333
  do { elements.push(0);
334
  } while (--n);
335
  return Vector.create(elements);
336
};
337
338
339
340
function Matrix() {}
341
Matrix.prototype = {
342
343
  // Returns element (i,j) of the matrix
344
  e: function(i,j) {
345
    if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) { return null; }
346
    return this.elements[i-1][j-1];
347
  },
348
349
  // Returns row k of the matrix as a vector
350
  row: function(i) {
351
    if (i > this.elements.length) { return null; }
352
    return Vector.create(this.elements[i-1]);
353
  },
354
355
  // Returns column k of the matrix as a vector
356
  col: function(j) {
357
    if (j > this.elements[0].length) { return null; }
358
    var col = [], n = this.elements.length, k = n, i;
359
    do { i = k - n;
360
      col.push(this.elements[i][j-1]);
361
    } while (--n);
362
    return Vector.create(col);
363
  },
364
365
  // Returns the number of rows/columns the matrix has
366
  dimensions: function() {
367
    return {rows: this.elements.length, cols: this.elements[0].length};
368
  },
369
370
  // Returns the number of rows in the matrix
371
  rows: function() {
372
    return this.elements.length;
373
  },
374
375
  // Returns the number of columns in the matrix
376
  cols: function() {
377
    return this.elements[0].length;
378
  },
379
380
  // Returns true iff the matrix is equal to the argument. You can supply
381
  // a vector as the argument, in which case the receiver must be a
382
  // one-column matrix equal to the vector.
383
  eql: function(matrix) {
384
    var M = matrix.elements || matrix;
385
    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
386
    if (this.elements.length != M.length ||
387
        this.elements[0].length != M[0].length) { return false; }
388
    var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
389
    do { i = ki - ni;
390
      nj = kj;
391
      do { j = kj - nj;
392
        if (Math.abs(this.elements[i][j] - M[i][j]) > Sylvester.precision) { return false; }
393
      } while (--nj);
394
    } while (--ni);
395
    return true;
396
  },
397
398
  // Returns a copy of the matrix
399
  dup: function() {
400
    return Matrix.create(this.elements);
401
  },
402
403
  // Maps the matrix to another matrix (of the same dimensions) according to the given function
404
  map: function(fn) {
405
    var els = [], ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
406
    do { i = ki - ni;
407
      nj = kj;
408
      els[i] = [];
409
      do { j = kj - nj;
410
        els[i][j] = fn(this.elements[i][j], i + 1, j + 1);
411
      } while (--nj);
412
    } while (--ni);
413
    return Matrix.create(els);
414
  },
415
416
  // Returns true iff the argument has the same dimensions as the matrix
417
  isSameSizeAs: function(matrix) {
418
    var M = matrix.elements || matrix;
419
    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
420
    return (this.elements.length == M.length &&
421
        this.elements[0].length == M[0].length);
422
  },
423
424
  // Returns the result of adding the argument to the matrix
425
  add: function(matrix) {
426
    var M = matrix.elements || matrix;
427
    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
428
    if (!this.isSameSizeAs(M)) { return null; }
429
    return this.map(function(x, i, j) { return x + M[i-1][j-1]; });
430
  },
431
432
  // Returns the result of subtracting the argument from the matrix
433
  subtract: function(matrix) {
434
    var M = matrix.elements || matrix;
435
    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
436
    if (!this.isSameSizeAs(M)) { return null; }
437
    return this.map(function(x, i, j) { return x - M[i-1][j-1]; });
438
  },
439
440
  // Returns true iff the matrix can multiply the argument from the left
441
  canMultiplyFromLeft: function(matrix) {
442
    var M = matrix.elements || matrix;
443
    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
444
    // this.columns should equal matrix.rows
445
    return (this.elements[0].length == M.length);
446
  },
447
448
  // Returns the result of multiplying the matrix from the right by the argument.
449
  // If the argument is a scalar then just multiply all the elements. If the argument is
450
  // a vector, a vector is returned, which saves you having to remember calling
451
  // col(1) on the result.
452
  multiply: function(matrix) {
453
    if (!matrix.elements) {
454
      return this.map(function(x) { return x * matrix; });
455
    }
456
    var returnVector = matrix.modulus ? true : false;
457
    var M = matrix.elements || matrix;
458
    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
459
    if (!this.canMultiplyFromLeft(M)) { return null; }
460
    var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j;
461
    var cols = this.elements[0].length, elements = [], sum, nc, c;
462
    do { i = ki - ni;
463
      elements[i] = [];
464
      nj = kj;
465
      do { j = kj - nj;
466
        sum = 0;
467
        nc = cols;
468
        do { c = cols - nc;
469
          sum += this.elements[i][c] * M[c][j];
470
        } while (--nc);
471
        elements[i][j] = sum;
472
      } while (--nj);
473
    } while (--ni);
474
    var M = Matrix.create(elements);
475
    return returnVector ? M.col(1) : M;
476
  },
477
478
  x: function(matrix) { return this.multiply(matrix); },
479
480
  // Returns a submatrix taken from the matrix
481
  // Argument order is: start row, start col, nrows, ncols
482
  // Element selection wraps if the required index is outside the matrix's bounds, so you could
483
  // use this to perform row/column cycling or copy-augmenting.
484
  minor: function(a, b, c, d) {
485
    var elements = [], ni = c, i, nj, j;
486
    var rows = this.elements.length, cols = this.elements[0].length;
487
    do { i = c - ni;
488
      elements[i] = [];
489
      nj = d;
490
      do { j = d - nj;
491
        elements[i][j] = this.elements[(a+i-1)%rows][(b+j-1)%cols];
492
      } while (--nj);
493
    } while (--ni);
494
    return Matrix.create(elements);
495
  },
496
497
  // Returns the transpose of the matrix
498
  transpose: function() {
499
    var rows = this.elements.length, cols = this.elements[0].length;
500
    var elements = [], ni = cols, i, nj, j;
501
    do { i = cols - ni;
502
      elements[i] = [];
503
      nj = rows;
504
      do { j = rows - nj;
505
        elements[i][j] = this.elements[j][i];
506
      } while (--nj);
507
    } while (--ni);
508
    return Matrix.create(elements);
509
  },
510
511
  // Returns true iff the matrix is square
512
  isSquare: function() {
513
    return (this.elements.length == this.elements[0].length);
514
  },
515
516
  // Returns the (absolute) largest element of the matrix
517
  max: function() {
518
    var m = 0, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
519
    do { i = ki - ni;
520
      nj = kj;
521
      do { j = kj - nj;
522
        if (Math.abs(this.elements[i][j]) > Math.abs(m)) { m = this.elements[i][j]; }
523
      } while (--nj);
524
    } while (--ni);
525
    return m;
526
  },
527
528
  // Returns the indeces of the first match found by reading row-by-row from left to right
529
  indexOf: function(x) {
530
    var index = null, ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
531
    do { i = ki - ni;
532
      nj = kj;
533
      do { j = kj - nj;
534
        if (this.elements[i][j] == x) { return {i: i+1, j: j+1}; }
535
      } while (--nj);
536
    } while (--ni);
537
    return null;
538
  },
539
540
  // If the matrix is square, returns the diagonal elements as a vector.
541
  // Otherwise, returns null.
542
  diagonal: function() {
543
    if (!this.isSquare) { return null; }
544
    var els = [], n = this.elements.length, k = n, i;
545
    do { i = k - n;
546
      els.push(this.elements[i][i]);
547
    } while (--n);
548
    return Vector.create(els);
549
  },
550
551
  // Make the matrix upper (right) triangular by Gaussian elimination.
552
  // This method only adds multiples of rows to other rows. No rows are
553
  // scaled up or switched, and the determinant is preserved.
554
  toRightTriangular: function() {
555
    var M = this.dup(), els;
556
    var n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p;
557
    do { i = k - n;
558
      if (M.elements[i][i] == 0) {
559
        for (j = i + 1; j < k; j++) {
560
          if (M.elements[j][i] != 0) {
561
            els = []; np = kp;
562
            do { p = kp - np;
563
              els.push(M.elements[i][p] + M.elements[j][p]);
564
            } while (--np);
565
            M.elements[i] = els;
566
            break;
567
          }
568
        }
569
      }
570
      if (M.elements[i][i] != 0) {
571
        for (j = i + 1; j < k; j++) {
572
          var multiplier = M.elements[j][i] / M.elements[i][i];
573
          els = []; np = kp;
574
          do { p = kp - np;
575
            // Elements with column numbers up to an including the number
576
            // of the row that we're subtracting can safely be set straight to
577
            // zero, since that's the point of this routine and it avoids having
578
            // to loop over and correct rounding errors later
579
            els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier);
580
          } while (--np);
581
          M.elements[j] = els;
582
        }
583
      }
584
    } while (--n);
585
    return M;
586
  },
587
588
  toUpperTriangular: function() { return this.toRightTriangular(); },
589
590
  // Returns the determinant for square matrices
591
  determinant: function() {
592
    if (!this.isSquare()) { return null; }
593
    var M = this.toRightTriangular();
594
    var det = M.elements[0][0], n = M.elements.length - 1, k = n, i;
595
    do { i = k - n + 1;
596
      det = det * M.elements[i][i];
597
    } while (--n);
598
    return det;
599
  },
600
601
  det: function() { return this.determinant(); },
602
603
  // Returns true iff the matrix is singular
604
  isSingular: function() {
605
    return (this.isSquare() && this.determinant() === 0);
606
  },
607
608
  // Returns the trace for square matrices
609
  trace: function() {
610
    if (!this.isSquare()) { return null; }
611
    var tr = this.elements[0][0], n = this.elements.length - 1, k = n, i;
612
    do { i = k - n + 1;
613
      tr += this.elements[i][i];
614
    } while (--n);
615
    return tr;
616
  },
617
618
  tr: function() { return this.trace(); },
619
620
  // Returns the rank of the matrix
621
  rank: function() {
622
    var M = this.toRightTriangular(), rank = 0;
623
    var ni = this.elements.length, ki = ni, i, nj, kj = this.elements[0].length, j;
624
    do { i = ki - ni;
625
      nj = kj;
626
      do { j = kj - nj;
627
        if (Math.abs(M.elements[i][j]) > Sylvester.precision) { rank++; break; }
628
      } while (--nj);
629
    } while (--ni);
630
    return rank;
631
  },
632
  
633
  rk: function() { return this.rank(); },
634
635
  // Returns the result of attaching the given argument to the right-hand side of the matrix
636
  augment: function(matrix) {
637
    var M = matrix.elements || matrix;
638
    if (typeof(M[0][0]) == 'undefined') { M = Matrix.create(M).elements; }
639
    var T = this.dup(), cols = T.elements[0].length;
640
    var ni = T.elements.length, ki = ni, i, nj, kj = M[0].length, j;
641
    if (ni != M.length) { return null; }
642
    do { i = ki - ni;
643
      nj = kj;
644
      do { j = kj - nj;
645
        T.elements[i][cols + j] = M[i][j];
646
      } while (--nj);
647
    } while (--ni);
648
    return T;
649
  },
650
651
  // Returns the inverse (if one exists) using Gauss-Jordan
652
  inverse: function() {
653
    if (!this.isSquare() || this.isSingular()) { return null; }
654
    var ni = this.elements.length, ki = ni, i, j;
655
    var M = this.augment(Matrix.I(ni)).toRightTriangular();
656
    var np, kp = M.elements[0].length, p, els, divisor;
657
    var inverse_elements = [], new_element;
658
    // Matrix is non-singular so there will be no zeros on the diagonal
659
    // Cycle through rows from last to first
660
    do { i = ni - 1;
661
      // First, normalise diagonal elements to 1
662
      els = []; np = kp;
663
      inverse_elements[i] = [];
664
      divisor = M.elements[i][i];
665
      do { p = kp - np;
666
        new_element = M.elements[i][p] / divisor;
667
        els.push(new_element);
668
        // Shuffle of the current row of the right hand side into the results
669
        // array as it will not be modified by later runs through this loop
670
        if (p >= ki) { inverse_elements[i].push(new_element); }
671
      } while (--np);
672
      M.elements[i] = els;
673
      // Then, subtract this row from those above it to
674
      // give the identity matrix on the left hand side
675
      for (j = 0; j < i; j++) {
676
        els = []; np = kp;
677
        do { p = kp - np;
678
          els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]);
679
        } while (--np);
680
        M.elements[j] = els;
681
      }
682
    } while (--ni);
683
    return Matrix.create(inverse_elements);
684
  },
685
686
  inv: function() { return this.inverse(); },
687
688
  // Returns the result of rounding all the elements
689
  round: function() {
690
    return this.map(function(x) { return Math.round(x); });
691
  },
692
693
  // Returns a copy of the matrix with elements set to the given value if they
694
  // differ from it by less than Sylvester.precision
695
  snapTo: function(x) {
696
    return this.map(function(p) {
697
      return (Math.abs(p - x) <= Sylvester.precision) ? x : p;
698
    });
699
  },
700
701
  // Returns a string representation of the matrix
702
  inspect: function() {
703
    var matrix_rows = [];
704
    var n = this.elements.length, k = n, i;
705
    do { i = k - n;
706
      matrix_rows.push(Vector.create(this.elements[i]).inspect());
707
    } while (--n);
708
    return matrix_rows.join('\n');
709
  },
710
711
  // Set the matrix's elements from an array. If the argument passed
712
  // is a vector, the resulting matrix will be a single column.
713
  setElements: function(els) {
714
    var i, elements = els.elements || els;
715
    if (typeof(elements[0][0]) != 'undefined') {
716
      var ni = elements.length, ki = ni, nj, kj, j;
717
      this.elements = [];
718
      do { i = ki - ni;
719
        nj = elements[i].length; kj = nj;
720
        this.elements[i] = [];
721
        do { j = kj - nj;
722
          this.elements[i][j] = elements[i][j];
723
        } while (--nj);
724
      } while(--ni);
725
      return this;
726
    }
727
    var n = elements.length, k = n;
728
    this.elements = [];
729
    do { i = k - n;
730
      this.elements.push([elements[i]]);
731
    } while (--n);
732
    return this;
733
  }
734
};
735
736
// Constructor function
737
Matrix.create = function(elements) {
738
  var M = new Matrix();
739
  return M.setElements(elements);
740
};
741
742
// Identity matrix of size n
743
Matrix.I = function(n) {
744
  var els = [], k = n, i, nj, j;
745
  do { i = k - n;
746
    els[i] = []; nj = k;
747
    do { j = k - nj;
748
      els[i][j] = (i == j) ? 1 : 0;
749
    } while (--nj);
750
  } while (--n);
751
  return Matrix.create(els);
752
};
753
754
// Diagonal matrix - all off-diagonal elements are zero
755
Matrix.Diagonal = function(elements) {
756
  var n = elements.length, k = n, i;
757
  var M = Matrix.I(n);
758
  do { i = k - n;
759
    M.elements[i][i] = elements[i];
760
  } while (--n);
761
  return M;
762
};
763
764
// Rotation matrix about some axis. If no axis is
765
// supplied, assume we're after a 2D transform
766
Matrix.Rotation = function(theta, a) {
767
  if (!a) {
768
    return Matrix.create([
769
      [Math.cos(theta),  -Math.sin(theta)],
770
      [Math.sin(theta),   Math.cos(theta)]
771
    ]);
772
  }
773
  var axis = a.dup();
774
  if (axis.elements.length != 3) { return null; }
775
  var mod = axis.modulus();
776
  var x = axis.elements[0]/mod, y = axis.elements[1]/mod, z = axis.elements[2]/mod;
777
  var s = Math.sin(theta), c = Math.cos(theta), t = 1 - c;
778
  // Formula derived here: http://www.gamedev.net/reference/articles/article1199.asp
779
  // That proof rotates the co-ordinate system so theta
780
  // becomes -theta and sin becomes -sin here.
781
  return Matrix.create([
782
    [ t*x*x + c, t*x*y - s*z, t*x*z + s*y ],
783
    [ t*x*y + s*z, t*y*y + c, t*y*z - s*x ],
784
    [ t*x*z - s*y, t*y*z + s*x, t*z*z + c ]
785
  ]);
786
};
787
788
// Special case rotations
789
Matrix.RotationX = function(t) {
790
  var c = Math.cos(t), s = Math.sin(t);
791
  return Matrix.create([
792
    [  1,  0,  0 ],
793
    [  0,  c, -s ],
794
    [  0,  s,  c ]
795
  ]);
796
};
797
Matrix.RotationY = function(t) {
798
  var c = Math.cos(t), s = Math.sin(t);
799
  return Matrix.create([
800
    [  c,  0,  s ],
801
    [  0,  1,  0 ],
802
    [ -s,  0,  c ]
803
  ]);
804
};
805
Matrix.RotationZ = function(t) {
806
  var c = Math.cos(t), s = Math.sin(t);
807
  return Matrix.create([
808
    [  c, -s,  0 ],
809
    [  s,  c,  0 ],
810
    [  0,  0,  1 ]
811
  ]);
812
};
813
814
// Random matrix of n rows, m columns
815
Matrix.Random = function(n, m) {
816
  return Matrix.Zero(n, m).map(
817
    function() { return Math.random(); }
818
  );
819
};
820
821
// Matrix filled with zeros
822
Matrix.Zero = function(n, m) {
823
  var els = [], ni = n, i, nj, j;
824
  do { i = n - ni;
825
    els[i] = [];
826
    nj = m;
827
    do { j = m - nj;
828
      els[i][j] = 0;
829
    } while (--nj);
830
  } while (--ni);
831
  return Matrix.create(els);
832
};
833
834
835
836
function Line() {}
837
Line.prototype = {
838
839
  // Returns true if the argument occupies the same space as the line
840
  eql: function(line) {
841
    return (this.isParallelTo(line) && this.contains(line.anchor));
842
  },
843
844
  // Returns a copy of the line
845
  dup: function() {
846
    return Line.create(this.anchor, this.direction);
847
  },
848
849
  // Returns the result of translating the line by the given vector/array
850
  translate: function(vector) {
851
    var V = vector.elements || vector;
852
    return Line.create([
853
      this.anchor.elements[0] + V[0],
854
      this.anchor.elements[1] + V[1],
855
      this.anchor.elements[2] + (V[2] || 0)
856
    ], this.direction);
857
  },
858
859
  // Returns true if the line is parallel to the argument. Here, 'parallel to'
860
  // means that the argument's direction is either parallel or antiparallel to
861
  // the line's own direction. A line is parallel to a plane if the two do not
862
  // have a unique intersection.
863
  isParallelTo: function(obj) {
864
    if (obj.normal) { return obj.isParallelTo(this); }
865
    var theta = this.direction.angleFrom(obj.direction);
866
    return (Math.abs(theta) <= Sylvester.precision || Math.abs(theta - Math.PI) <= Sylvester.precision);
867
  },
868
869
  // Returns the line's perpendicular distance from the argument,
870
  // which can be a point, a line or a plane
871
  distanceFrom: function(obj) {
872
    if (obj.normal) { return obj.distanceFrom(this); }
873
    if (obj.direction) {
874
      // obj is a line
875
      if (this.isParallelTo(obj)) { return this.distanceFrom(obj.anchor); }
876
      var N = this.direction.cross(obj.direction).toUnitVector().elements;
877
      var A = this.anchor.elements, B = obj.anchor.elements;
878
      return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
879
    } else {
880
      // obj is a point
881
      var P = obj.elements || obj;
882
      var A = this.anchor.elements, D = this.direction.elements;
883
      var PA1 = P[0] - A[0], PA2 = P[1] - A[1], PA3 = (P[2] || 0) - A[2];
884
      var modPA = Math.sqrt(PA1*PA1 + PA2*PA2 + PA3*PA3);
885
      if (modPA === 0) return 0;
886
      // Assumes direction vector is normalized
887
      var cosTheta = (PA1 * D[0] + PA2 * D[1] + PA3 * D[2]) / modPA;
888
      var sin2 = 1 - cosTheta*cosTheta;
889
      return Math.abs(modPA * Math.sqrt(sin2 < 0 ? 0 : sin2));
890
    }
891
  },
892
893
  // Returns true iff the argument is a point on the line
894
  contains: function(point) {
895
    var dist = this.distanceFrom(point);
896
    return (dist !== null && dist <= Sylvester.precision);
897
  },
898
899
  // Returns true iff the line lies in the given plane
900
  liesIn: function(plane) {
901
    return plane.contains(this);
902
  },
903
904
  // Returns true iff the line has a unique point of intersection with the argument
905
  intersects: function(obj) {
906
    if (obj.normal) { return obj.intersects(this); }
907
    return (!this.isParallelTo(obj) && this.distanceFrom(obj) <= Sylvester.precision);
908
  },
909
910
  // Returns the unique intersection point with the argument, if one exists
911
  intersectionWith: function(obj) {
912
    if (obj.normal) { return obj.intersectionWith(this); }
913
    if (!this.intersects(obj)) { return null; }
914
    var P = this.anchor.elements, X = this.direction.elements,
915
        Q = obj.anchor.elements, Y = obj.direction.elements;
916
    var X1 = X[0], X2 = X[1], X3 = X[2], Y1 = Y[0], Y2 = Y[1], Y3 = Y[2];
917
    var PsubQ1 = P[0] - Q[0], PsubQ2 = P[1] - Q[1], PsubQ3 = P[2] - Q[2];
918
    var XdotQsubP = - X1*PsubQ1 - X2*PsubQ2 - X3*PsubQ3;
919
    var YdotPsubQ = Y1*PsubQ1 + Y2*PsubQ2 + Y3*PsubQ3;
920
    var XdotX = X1*X1 + X2*X2 + X3*X3;
921
    var YdotY = Y1*Y1 + Y2*Y2 + Y3*Y3;
922
    var XdotY = X1*Y1 + X2*Y2 + X3*Y3;
923
    var k = (XdotQsubP * YdotY / XdotX + XdotY * YdotPsubQ) / (YdotY - XdotY * XdotY);
924
    return Vector.create([P[0] + k*X1, P[1] + k*X2, P[2] + k*X3]);
925
  },
926
927
  // Returns the point on the line that is closest to the given point or line
928
  pointClosestTo: function(obj) {
929
    if (obj.direction) {
930
      // obj is a line
931
      if (this.intersects(obj)) { return this.intersectionWith(obj); }
932
      if (this.isParallelTo(obj)) { return null; }
933
      var D = this.direction.elements, E = obj.direction.elements;
934
      var D1 = D[0], D2 = D[1], D3 = D[2], E1 = E[0], E2 = E[1], E3 = E[2];
935
      // Create plane containing obj and the shared normal and intersect this with it
936
      // Thank you: http://www.cgafaq.info/wiki/Line-line_distance
937
      var x = (D3 * E1 - D1 * E3), y = (D1 * E2 - D2 * E1), z = (D2 * E3 - D3 * E2);
938
      var N = Vector.create([x * E3 - y * E2, y * E1 - z * E3, z * E2 - x * E1]);
939
      var P = Plane.create(obj.anchor, N);
940
      return P.intersectionWith(this);
941
    } else {
942
      // obj is a point
943
      var P = obj.elements || obj;
944
      if (this.contains(P)) { return Vector.create(P); }
945
      var A = this.anchor.elements, D = this.direction.elements;
946
      var D1 = D[0], D2 = D[1], D3 = D[2], A1 = A[0], A2 = A[1], A3 = A[2];
947
      var x = D1 * (P[1]-A2) - D2 * (P[0]-A1), y = D2 * ((P[2] || 0) - A3) - D3 * (P[1]-A2),
948
          z = D3 * (P[0]-A1) - D1 * ((P[2] || 0) - A3);
949
      var V = Vector.create([D2 * x - D3 * z, D3 * y - D1 * x, D1 * z - D2 * y]);
950
      var k = this.distanceFrom(P) / V.modulus();
951
      return Vector.create([
952
        P[0] + V.elements[0] * k,
953
        P[1] + V.elements[1] * k,
954
        (P[2] || 0) + V.elements[2] * k
955
      ]);
956
    }
957
  },
958
959
  // Returns a copy of the line rotated by t radians about the given line. Works by
960
  // finding the argument's closest point to this line's anchor point (call this C) and
961
  // rotating the anchor about C. Also rotates the line's direction about the argument's.
962
  // Be careful with this - the rotation axis' direction affects the outcome!
963
  rotate: function(t, line) {
964
    // If we're working in 2D
965
    if (typeof(line.direction) == 'undefined') { line = Line.create(line.to3D(), Vector.k); }
966
    var R = Matrix.Rotation(t, line.direction).elements;
967
    var C = line.pointClosestTo(this.anchor).elements;
968
    var A = this.anchor.elements, D = this.direction.elements;
969
    var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
970
    var x = A1 - C1, y = A2 - C2, z = A3 - C3;
971
    return Line.create([
972
      C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
973
      C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
974
      C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
975
    ], [
976
      R[0][0] * D[0] + R[0][1] * D[1] + R[0][2] * D[2],
977
      R[1][0] * D[0] + R[1][1] * D[1] + R[1][2] * D[2],
978
      R[2][0] * D[0] + R[2][1] * D[1] + R[2][2] * D[2]
979
    ]);
980
  },
981
982
  // Returns the line's reflection in the given point or line
983
  reflectionIn: function(obj) {
984
    if (obj.normal) {
985
      // obj is a plane
986
      var A = this.anchor.elements, D = this.direction.elements;
987
      var A1 = A[0], A2 = A[1], A3 = A[2], D1 = D[0], D2 = D[1], D3 = D[2];
988
      var newA = this.anchor.reflectionIn(obj).elements;
989
      // Add the line's direction vector to its anchor, then mirror that in the plane
990
      var AD1 = A1 + D1, AD2 = A2 + D2, AD3 = A3 + D3;
991
      var Q = obj.pointClosestTo([AD1, AD2, AD3]).elements;
992
      var newD = [Q[0] + (Q[0] - AD1) - newA[0], Q[1] + (Q[1] - AD2) - newA[1], Q[2] + (Q[2] - AD3) - newA[2]];
993
      return Line.create(newA, newD);
994
    } else if (obj.direction) {
995
      // obj is a line - reflection obtained by rotating PI radians about obj
996
      return this.rotate(Math.PI, obj);
997
    } else {
998
      // obj is a point - just reflect the line's anchor in it
999
      var P = obj.elements || obj;
1000
      return Line.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.direction);
1001
    }
1002
  },
1003
1004
  // Set the line's anchor point and direction.
1005
  setVectors: function(anchor, direction) {
1006
    // Need to do this so that line's properties are not
1007
    // references to the arguments passed in
1008
    anchor = Vector.create(anchor);
1009
    direction = Vector.create(direction);
1010
    if (anchor.elements.length == 2) {anchor.elements.push(0); }
1011
    if (direction.elements.length == 2) { direction.elements.push(0); }
1012
    if (anchor.elements.length > 3 || direction.elements.length > 3) { return null; }
1013
    var mod = direction.modulus();
1014
    if (mod === 0) { return null; }
1015
    this.anchor = anchor;
1016
    this.direction = Vector.create([
1017
      direction.elements[0] / mod,
1018
      direction.elements[1] / mod,
1019
      direction.elements[2] / mod
1020
    ]);
1021
    return this;
1022
  }
1023
};
1024
1025
  
1026
// Constructor function
1027
Line.create = function(anchor, direction) {
1028
  var L = new Line();
1029
  return L.setVectors(anchor, direction);
1030
};
1031
1032
// Axes
1033
Line.X = Line.create(Vector.Zero(3), Vector.i);
1034
Line.Y = Line.create(Vector.Zero(3), Vector.j);
1035
Line.Z = Line.create(Vector.Zero(3), Vector.k);
1036
1037
1038
1039
function Plane() {}
1040
Plane.prototype = {
1041
1042
  // Returns true iff the plane occupies the same space as the argument
1043
  eql: function(plane) {
1044
    return (this.contains(plane.anchor) && this.isParallelTo(plane));
1045
  },
1046
1047
  // Returns a copy of the plane
1048
  dup: function() {
1049
    return Plane.create(this.anchor, this.normal);
1050
  },
1051
1052
  // Returns the result of translating the plane by the given vector
1053
  translate: function(vector) {
1054
    var V = vector.elements || vector;
1055
    return Plane.create([
1056
      this.anchor.elements[0] + V[0],
1057
      this.anchor.elements[1] + V[1],
1058
      this.anchor.elements[2] + (V[2] || 0)
1059
    ], this.normal);
1060
  },
1061
1062
  // Returns true iff the plane is parallel to the argument. Will return true
1063
  // if the planes are equal, or if you give a line and it lies in the plane.
1064
  isParallelTo: function(obj) {
1065
    var theta;
1066
    if (obj.normal) {
1067
      // obj is a plane
1068
      theta = this.normal.angleFrom(obj.normal);
1069
      return (Math.abs(theta) <= Sylvester.precision || Math.abs(Math.PI - theta) <= Sylvester.precision);
1070
    } else if (obj.direction) {
1071
      // obj is a line
1072
      return this.normal.isPerpendicularTo(obj.direction);
1073
    }
1074
    return null;
1075
  },
1076
  
1077
  // Returns true iff the receiver is perpendicular to the argument
1078
  isPerpendicularTo: function(plane) {
1079
    var theta = this.normal.angleFrom(plane.normal);
1080
    return (Math.abs(Math.PI/2 - theta) <= Sylvester.precision);
1081
  },
1082
1083
  // Returns the plane's distance from the given object (point, line or plane)
1084
  distanceFrom: function(obj) {
1085
    if (this.intersects(obj) || this.contains(obj)) { return 0; }
1086
    if (obj.anchor) {
1087
      // obj is a plane or line
1088
      var A = this.anchor.elements, B = obj.anchor.elements, N = this.normal.elements;
1089
      return Math.abs((A[0] - B[0]) * N[0] + (A[1] - B[1]) * N[1] + (A[2] - B[2]) * N[2]);
1090
    } else {
1091
      // obj is a point
1092
      var P = obj.elements || obj;
1093
      var A = this.anchor.elements, N = this.normal.elements;
1094
      return Math.abs((A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2]);
1095
    }
1096
  },
1097
1098
  // Returns true iff the plane contains the given point or line
1099
  contains: function(obj) {
1100
    if (obj.normal) { return null; }
1101
    if (obj.direction) {
1102
      return (this.contains(obj.anchor) && this.contains(obj.anchor.add(obj.direction)));
1103
    } else {
1104
      var P = obj.elements || obj;
1105
      var A = this.anchor.elements, N = this.normal.elements;
1106
      var diff = Math.abs(N[0]*(A[0] - P[0]) + N[1]*(A[1] - P[1]) + N[2]*(A[2] - (P[2] || 0)));
1107
      return (diff <= Sylvester.precision);
1108
    }
1109
  },
1110
1111
  // Returns true iff the plane has a unique point/line of intersection with the argument
1112
  intersects: function(obj) {
1113
    if (typeof(obj.direction) == 'undefined' && typeof(obj.normal) == 'undefined') { return null; }
1114
    return !this.isParallelTo(obj);
1115
  },
1116
1117
  // Returns the unique intersection with the argument, if one exists. The result
1118
  // will be a vector if a line is supplied, and a line if a plane is supplied.
1119
  intersectionWith: function(obj) {
1120
    if (!this.intersects(obj)) { return null; }
1121
    if (obj.direction) {
1122
      // obj is a line
1123
      var A = obj.anchor.elements, D = obj.direction.elements,
1124
          P = this.anchor.elements, N = this.normal.elements;
1125
      var multiplier = (N[0]*(P[0]-A[0]) + N[1]*(P[1]-A[1]) + N[2]*(P[2]-A[2])) / (N[0]*D[0] + N[1]*D[1] + N[2]*D[2]);
1126
      return Vector.create([A[0] + D[0]*multiplier, A[1] + D[1]*multiplier, A[2] + D[2]*multiplier]);
1127
    } else if (obj.normal) {
1128
      // obj is a plane
1129
      var direction = this.normal.cross(obj.normal).toUnitVector();
1130
      // To find an anchor point, we find one co-ordinate that has a value
1131
      // of zero somewhere on the intersection, and remember which one we picked
1132
      var N = this.normal.elements, A = this.anchor.elements,
1133
          O = obj.normal.elements, B = obj.anchor.elements;
1134
      var solver = Matrix.Zero(2,2), i = 0;
1135
      while (solver.isSingular()) {
1136
        i++;
1137
        solver = Matrix.create([
1138
          [ N[i%3], N[(i+1)%3] ],
1139
          [ O[i%3], O[(i+1)%3]  ]
1140
        ]);
1141
      }
1142
      // Then we solve the simultaneous equations in the remaining dimensions
1143
      var inverse = solver.inverse().elements;
1144
      var x = N[0]*A[0] + N[1]*A[1] + N[2]*A[2];
1145
      var y = O[0]*B[0] + O[1]*B[1] + O[2]*B[2];
1146
      var intersection = [
1147
        inverse[0][0] * x + inverse[0][1] * y,
1148
        inverse[1][0] * x + inverse[1][1] * y
1149
      ];
1150
      var anchor = [];
1151
      for (var j = 1; j <= 3; j++) {
1152
        // This formula picks the right element from intersection by
1153
        // cycling depending on which element we set to zero above
1154
        anchor.push((i == j) ? 0 : intersection[(j + (5 - i)%3)%3]);
1155
      }
1156
      return Line.create(anchor, direction);
1157
    }
1158
  },
1159
1160
  // Returns the point in the plane closest to the given point
1161
  pointClosestTo: function(point) {
1162
    var P = point.elements || point;
1163
    var A = this.anchor.elements, N = this.normal.elements;
1164
    var dot = (A[0] - P[0]) * N[0] + (A[1] - P[1]) * N[1] + (A[2] - (P[2] || 0)) * N[2];
1165
    return Vector.create([P[0] + N[0] * dot, P[1] + N[1] * dot, (P[2] || 0) + N[2] * dot]);
1166
  },
1167
1168
  // Returns a copy of the plane, rotated by t radians about the given line
1169
  // See notes on Line#rotate.
1170
  rotate: function(t, line) {
1171
    var R = Matrix.Rotation(t, line.direction).elements;
1172
    var C = line.pointClosestTo(this.anchor).elements;
1173
    var A = this.anchor.elements, N = this.normal.elements;
1174
    var C1 = C[0], C2 = C[1], C3 = C[2], A1 = A[0], A2 = A[1], A3 = A[2];
1175
    var x = A1 - C1, y = A2 - C2, z = A3 - C3;
1176
    return Plane.create([
1177
      C1 + R[0][0] * x + R[0][1] * y + R[0][2] * z,
1178
      C2 + R[1][0] * x + R[1][1] * y + R[1][2] * z,
1179
      C3 + R[2][0] * x + R[2][1] * y + R[2][2] * z
1180
    ], [
1181
      R[0][0] * N[0] + R[0][1] * N[1] + R[0][2] * N[2],
1182
      R[1][0] * N[0] + R[1][1] * N[1] + R[1][2] * N[2],
1183
      R[2][0] * N[0] + R[2][1] * N[1] + R[2][2] * N[2]
1184
    ]);
1185
  },
1186
1187
  // Returns the reflection of the plane in the given point, line or plane.
1188
  reflectionIn: function(obj) {
1189
    if (obj.normal) {
1190
      // obj is a plane
1191
      var A = this.anchor.elements, N = this.normal.elements;
1192
      var A1 = A[0], A2 = A[1], A3 = A[2], N1 = N[0], N2 = N[1], N3 = N[2];
1193
      var newA = this.anchor.reflectionIn(obj).elements;
1194
      // Add the plane's normal to its anchor, then mirror that in the other plane
1195
      var AN1 = A1 + N1, AN2 = A2 + N2, AN3 = A3 + N3;
1196
      var Q = obj.pointClosestTo([AN1, AN2, AN3]).elements;
1197
      var newN = [Q[0] + (Q[0] - AN1) - newA[0], Q[1] + (Q[1] - AN2) - newA[1], Q[2] + (Q[2] - AN3) - newA[2]];
1198
      return Plane.create(newA, newN);
1199
    } else if (obj.direction) {
1200
      // obj is a line
1201
      return this.rotate(Math.PI, obj);
1202
    } else {
1203
      // obj is a point
1204
      var P = obj.elements || obj;
1205
      return Plane.create(this.anchor.reflectionIn([P[0], P[1], (P[2] || 0)]), this.normal);
1206
    }
1207
  },
1208
1209
  // Sets the anchor point and normal to the plane. If three arguments are specified,
1210
  // the normal is calculated by assuming the three points should lie in the same plane.
1211
  // If only two are sepcified, the second is taken to be the normal. Normal vector is
1212
  // normalised before storage.
1213
  setVectors: function(anchor, v1, v2) {
1214
    anchor = Vector.create(anchor);
1215
    anchor = anchor.to3D(); if (anchor === null) { return null; }
1216
    v1 = Vector.create(v1);
1217
    v1 = v1.to3D(); if (v1 === null) { return null; }
1218
    if (typeof(v2) == 'undefined') {
1219
      v2 = null;
1220
    } else {
1221
      v2 = Vector.create(v2);
1222
      v2 = v2.to3D(); if (v2 === null) { return null; }
1223
    }
1224
    var A1 = anchor.elements[0], A2 = anchor.elements[1], A3 = anchor.elements[2];
1225
    var v11 = v1.elements[0], v12 = v1.elements[1], v13 = v1.elements[2];
1226
    var normal, mod;
1227
    if (v2 !== null) {
1228
      var v21 = v2.elements[0], v22 = v2.elements[1], v23 = v2.elements[2];
1229
      normal = Vector.create([
1230
        (v12 - A2) * (v23 - A3) - (v13 - A3) * (v22 - A2),
1231
        (v13 - A3) * (v21 - A1) - (v11 - A1) * (v23 - A3),
1232
        (v11 - A1) * (v22 - A2) - (v12 - A2) * (v21 - A1)
1233
      ]);
1234
      mod = normal.modulus();
1235
      if (mod === 0) { return null; }
1236
      normal = Vector.create([normal.elements[0] / mod, normal.elements[1] / mod, normal.elements[2] / mod]);
1237
    } else {
1238
      mod = Math.sqrt(v11*v11 + v12*v12 + v13*v13);
1239
      if (mod === 0) { return null; }
1240
      normal = Vector.create([v1.elements[0] / mod, v1.elements[1] / mod, v1.elements[2] / mod]);
1241
    }
1242
    this.anchor = anchor;
1243
    this.normal = normal;
1244
    return this;
1245
  }
1246
};
1247
1248
// Constructor function
1249
Plane.create = function(anchor, v1, v2) {
1250
  var P = new Plane();
1251
  return P.setVectors(anchor, v1, v2);
1252
};
1253
1254
// X-Y-Z planes
1255
Plane.XY = Plane.create(Vector.Zero(3), Vector.k);
1256
Plane.YZ = Plane.create(Vector.Zero(3), Vector.i);
1257
Plane.ZX = Plane.create(Vector.Zero(3), Vector.j);
1258
Plane.YX = Plane.XY; Plane.ZY = Plane.YZ; Plane.XZ = Plane.ZX;
1259
1260
// Utility functions
1261
var $V = Vector.create;
1262
var $M = Matrix.create;
1263
var $L = Line.create;
1264
var $P = Plane.create;
1265
</script>
1266
<script>
1267
var N3D = {
1268
  Models:{},
1269
  GetPageSize:function(){
1270
    var d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0];  
1271
    return {
1272
      width:window.innerWidth || e.clientWidth || g.clientWidth,    
1273
      height:window.innerHeight|| e.clientHeight|| g.clientHeight
1274
    };
1275
  }
1276
};
1277
N3D.Error = function(name,message) {
1278
    this.name = name;
1279
    this.level = "Show Stopper";
1280
    this.message =      message; 
1281
    this.htmlMessage =  message;
1282
};  
1283
N3D.Error.prototype.toString =  function(){return this.name + ": " + this.message};
1284
1285
function extend(child,parent){
1286
  var F = function(){};
1287
  F.prototype = parent.prototype;
1288
  child.prototype = new F(); 
1289
  child.prototype.constructor = child; 
1290
  return child.prototype;                                   
1291
};
1292
1293
1294
(function(){
1295
  var head = document.getElementsByTagName("head")[0];
1296
  var master_script = document.getElementsByTagName('script');
1297
  master_script = master_script[master_script.length-1];
1298
  var abs_path = master_script.src.match(/.*\//);
1299
  abs_path = abs_path ? abs_path[0] : '/';
1300
1301
  /* >>>> Detect support render >>>> */           
1302
  var supp = N3D.Support = {
1303
    Canvas:false,
1304
    WebGL:false,
1305
    SVG:false,
1306
    VML:false,
1307
    toString:function(){
1308
      return 'Context 2D: '+this.Canvas+'\n' + 
1309
             'WebGL: '+this.WebGL+'\n' + 
1310
             'SVG: '+this.SVG+'\n' +
1311
             'VML: '+this.VML;
1312
    } 
1313
  };
1314
  
1315
  var canvas = document.createElement('canvas');
1316
  
1317
  /* Support Canvas */
1318
  try{
1319
    canvas.getContext('2d');    
1320
    supp.Canvas = true; 
1321
  }catch(e){} 
1322
  
1323
  
1324
  /* Support WebGL */                            
1325
  if(typeof WebGLRenderingContext !== 'undefined'){
1326
    supp.WebGL = true;
1327
  }else{
1328
    var types = ['webgl','experimental-webgl'];
1329
    for(var i=0;i<length;i++){
1330
      try{
1331
        var ctx = canvas.getContext(types[i]);
1332
      
1333
        supp.WebGL = true;
1334
        break;
1335
      }catch(e){}  
1336
    }
1337
  }
1338
  
1339
  /* Support SVG */
1340
  supp.SVG = document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Shape", "1.1")
1341
  
1342
  /* Support VML */
1343
  var a = document.createElement('div');
1344
  a.innerHTML = '<v:shape adj="1" />';
1345
  var b = a.firstChild;
1346
  b.style.behavior = "url(#default#VML)";
1347
  supp.VML = b ? typeof b.adj == "object": true;
1348
  /* <<<< Detect support render <<<< */  
1349
  
1350
  
1351
function load(urls,callbacks){
1352
  if(urls.length <= 0){
1353
    if(callbacks.unloaded.length == 0){
1354
      callbacks.success();
1355
    }else{
1356
      callbacks.error();
1357
    }
1358
    callbacks.complete();
1359
    return;
1360
  }
1361
  var url = urls[0];
1362
  urls.shift();
1363
 
1364
  if(document.getElementById(url) != null){ loader(urls,callbacks); return false;}
1365
1366
  var script = document.createElement('script');
1367
  script.type = 'text/javascript';
1368
  script.id = url;
1369
  script.src = abs_path+url+'.js';
1370
  head.appendChild(script);
1371
1372
  script.onreadystatechange = function(){
1373
    if(script.readyState == 'loaded' || script.readyState == 'complete'){
1374
      this.onreadystatechange = null; 
1375
        
1376
      load(urls,callbacks);        
1377
    }
1378
  };
1379
  
1380
  script.onerror = function(){
1381
    callbacks.unloaded.push(this.id+' missing library file,'+this.src+' not a file'); 
1382
    load(urls,callbacks);
1383
    head.removeChild(this);
1384
    return false;
1385
  };
1386
    
1387
  script.onload = function(){
1388
    load(urls,callbacks);
1389
  };
1390
};
1391
1392
1393
N3D.require = function(){
1394
  var urls = Array.prototype.slice.call(arguments);
1395
  var callbacks = {
1396
    complete: function(f){ this.complete = f || this.complete; },
1397
    success: function(f){ this.success = f || this.success; },
1398
    error: function(f){ this.error = f || this.error; },
1399
    unloaded:[]
1400
  };
1401
1402
  load(urls,callbacks);
1403
  return callbacks;
1404
};
1405
1406
1407
1408
function getAttr(el, attr) {
1409
  var result;
1410
  if(result = el[attr]){ return result; }
1411
  if(el.getAttribute && (result = el.getAttribute(attr))){ return result;}
1412
  var attrs = el.attributes;
1413
  var length = attrs.length;
1414
1415
  for(var i = 0; i < length; i++){
1416
    if(attrs[i].nodeName === attr){
1417
      return attrs[i].nodeValue;
1418
    }
1419
  }
1420
  return null;
1421
};
1422
  
1423
  var require = getAttr(master_script,'require');
1424
  
1425
  if(require !== null){
1426
    
1427
    var req_load = N3D.require.apply(null,require.split(','));
1428
    req_load.success(function(){
1429
      var script = document.createElement('script');
1430
      script.type = 'text/javascript';
1431
     
1432
      script.text = master_script.innerHTML;
1433
      head.appendChild(script);
1434
      
1435
      master_script.parentNode.removeChild(master_script);
1436
      
1437
    });
1438
    req_load.error(function(){
1439
      console.log('error');
1440
    })
1441
  
1442
  }
1443
  
1444
  N3D.SaveModel = function(name){
1445
    var model = N3D.Models[name];
1446
    var text = '{"vp":[1,2,3,4],"f":[0,1,2,0,2,3]}';
1447
     
1448
    if(model instanceof N3D.Geometry.Shapes){
1449
      var a = document.createElement("a");
1450
          a.download = name+'.json';
1451
          a.href = 'data:text/javascript;charset=utf-8,'+text;
1452
          a.style.display = "none";
1453
          a.onclick = function(event){
1454
            var event = event || window.event;
1455
            var target = event.target || event.srcElement;
1456
            document.body.removeChild(event.target);
1457
          };
1458
        
1459
          document.body.appendChild(a);
1460
          a.click();
1461
      throw new N3D.Error('Model Exporter','export was successful');  
1462
    }else{
1463
      throw new N3D.Error('Model Exporter','Model "'+name+'" not found');
1464
    }
1465
  };
1466
1467
})();
1468
</script>
1469
<script>
1470
/* >>>> Math.Main >>>> */
1471
N3D.Math = (function(){
1472
  var obj = {};
1473
  var PI = Math.PI, floor = Math.floor, random = Math.random;
1474
  var PI180 = PI/180, PI180_rev = 180/PI, sqrt5 = Math.sqrt(5);
1475
  
1476
  obj.Log10E = Math.LOG10E || 0.4342945;
1477
  obj.Log2E = Math.LOG2E || 1.442695;
1478
  obj.PiOver2 = PI*0.5;
1479
  obj.PiOver4 = PI*0.25;
1480
  obj.TwoPi = PI*2;
1481
  obj.PiOver360 = PI/360;
1482
  obj.PiOver180 = PI180;
1483
  obj.Pi = PI;
1484
  obj.AbsFloat = Math.abs;
1485
  obj.Floor = floor;
1486
  obj.Max = Math.max;
1487
  obj.Min = Math.min;
1488
  obj.Sqrt = Math.sqrt;
1489
  obj.Pow = Math.pow;
1490
  obj.Ceil = Math.ceil;
1491
  obj.Round = Math.round;
1492
  obj.Pow2 = function(n){ return n*n; };
1493
  obj.Pow3 = function(n){ return n*n*n; };
1494
  obj.Ceil2 = function(n){ return (~~n)+1; };
1495
  obj.Floor2 = function(n){ return ~~n; };
1496
  obj.RandomInt = function(min, max) {
1497
    return min + floor(random() * (max - min + 1));
1498
  };
1499
  obj.RandomFloat = function(min, max) {
1500
    return min + (random() * (max - min));
1501
  };
1502
  obj.AbsInt = function(n){
1503
    var b = n >> 31; 
1504
    return (n ^ b) - b;
1505
  };
1506
  
1507
  obj.ToDegrees = function(d){
1508
    return d * PI180_rev;
1509
  };
1510
  obj.ToRadians = function(d){
1511
    return d * PI180;
1512
  };
1513
  
1514
  obj.FromFibonacci = function(T){
1515
    var phi = (1 + root5) / 2;
1516
1517
    var idx  = floor( Math.log(T*sqrt5) / Math.log(phi) + 0.5 );
1518
    var u = floor( Math.pow(phi, idx)/sqrt5 + 0.5);
1519
1520
    return (u == T) ? idx : false;
1521
  };
1522
  
1523
  obj.ToFibonacci = function(d){
1524
    for(var a=0,b=1,c=0,f=1;f<d;f++){
1525
      a = c + b;
1526
      c = b;
1527
      b = a;  
1528
    }
1529
    return a;
1530
  };
1531
  
1532
  obj.Barycentric = function(v1,v2,v3,a1,a2){
1533
    return v1 + (v2-v1) * a1 + (v3-v1) * a2;
1534
  };
1535
  
1536
  obj.CatmullRom = function(v1, v2, v3, v4, a){
1537
    var aS = a * a, aC = aS * a;
1538
    return (0 * (2 * ve2 + (v3 - v1) * a + (2 * v1 - 5 * v2 + 4 * v3 - v4) * aS + (3 * v2 - v1 - 3 * v3 + v4) * aC));
1539
  };
1540
  
1541
  obj.Clamp = function(v,min,max){
1542
    return v > max ? max : (v < min ? min : v);
1543
  };
1544
  obj.Lerp = function(v1,v2,a){
1545
    return v1 + (v2-v1) * a;
1546
  };
1547
  
1548
  return obj;
1549
})();
1550
/* <<<< Math.Main <<<< */
1551
1552
1553
/* >>>> Math.Matrix3 >>>> */
1554
N3D.Math.Matrix3 = function(n0,n1,n2,n3,n4,n5,n6,n7,n8){
1555
  this.m = [n0,n1,n2,n3,n4,n5,n6,n7,n8];
1556
1557
  return this;
1558
};
1559
N3D.Math.Matrix3.prototype = {
1560
  constructor:N3D.Math.Matrix3,
1561
  identity:function(){
1562
    this.m = [
1563
      1,0,0,
1564
      0,1,0,
1565
      0,0,1
1566
    ]; 
1567
    return this; 
1568
  },
1569
  determinant:function(){
1570
  },
1571
  inverse:function(){
1572
    var m = this.m,
1573
        m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3],
1574
        m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7],
1575
        m8 = m[8],
1576
        a0 = (m8*m4-m7*m5),
1577
        a1 = (m8*m1-m7*m2),
1578
        a2 = (m5*m1-m4*m2);
1579
    
1580
    var det = 1/(m0*a0-m3*a1+m6*a2);
1581
    
1582
    m[0] =  a0*det;
1583
    m[1] = -a1*det;
1584
    m[2] =  a2*det;
1585
    
1586
    m[3] = -(m8*m3-m6*m5)*det;
1587
    m[4] =  (m8*m0-m6*m2)*det;
1588
    m[5] = -(m5*m0-m3*m2)*det;
1589
    
1590
    m[6] =  (m7*m3-m6*m4)*det;
1591
    m[7] = -(m7*m0-m6*m1)*det;
1592
    m[8] =  (m4*m0-m3*m1)*det; 
1593
1594
    return this;
1595
  },
1596
  multiply:function(n){
1597
    var m = this.m,
1598
        nm = n.m, 
1599
        m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3],
1600
        m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7],
1601
        m8 = m[8],
1602
1603
        n0 = nm[0], n1 = nm[1], n2 = nm[2], n3 = nm[3],
1604
        n4 = nm[4], n5 = nm[5], n6 = nm[6], n7 = nm[7],
1605
        n8 = nm[8];
1606
        
1607
        
1608
    m[0] = m0*n0 + m1*n3 + m2*n6;
1609
    m[1] = m0*n1 + m1*n4 + m2*n7;
1610
    m[2] = m0*n2 + m1*n5 + m2*n8;
1611
      
1612
    m[3] = m3*n0 + m4*n3 + m5*n6;
1613
    m[4] = m3*n1 + m4*n4 + m5*n7;
1614
    m[5] = m3*n2 + m4*n5 + m5*n8;  
1615
    
1616
    m[6] = m6*n0 + m7*n3 + m8*n6;
1617
    m[7] = m6*n1 + m7*n4 + m8*n7;
1618
    m[8] = m6*n2 + m7*n5 + m8*n8; 
1619
      
1620
   return this;
1621
  },
1622
  multiplyVector3:function(v){
1623
    var m = this.m;
1624
    var x = v.x, y = v.y, z = v.z;
1625
1626
    return new $V3(
1627
      m[0] * x + m[3] * y + m[6] * z,
1628
      m[1] * x + m[4] * y + m[7] * z,
1629
      m[2] * x + m[5] * y + m[8] * z
1630
    );
1631
  },
1632
  transpose:function(){
1633
    var m = this.m;
1634
    var a1 = m[1], a2 = m[2], a5 = m[3];
1635
                
1636
    m[1] = m[3];
1637
    m[2] = m[6];
1638
    m[5] = m[7];
1639
    
1640
    m[3] = a1;
1641
    m[6] = a2;
1642
    m[7] = a5;
1643
    
1644
    return this;    
1645
  },
1646
  scale:function(x,y,z){
1647
    var m = this.m;
1648
    m[0] *= x; m[1] *= y; m[2] *= z;
1649
        m[3] *= x; m[4] *= y; m[5] *= z;
1650
        m[6] *= x; m[7] *= y; m[8] *= z;
1651
    
1652
    return this;
1653
  },
1654
  rotateX: function(angle){
1655
    var m = this.m;
1656
    
1657
    var c = Math.cos(angle);
1658
    var s = Math.sin(angle);
1659
    
1660
    var m1 = m[1], m4 = m[4], m7 = m[7],
1661
        m2 = m[2], m5 = m[5], m8= m[8];
1662
1663
    m[1] = m1 * c + m2 *s;
1664
    m[4] = m4 * c + m5 *s;
1665
    m[7] = m7 * c + m8*s;
1666
1667
    m[2] = m1 * -s + m2 * c;
1668
    m[6] = m4 * -s + m5 * c;
1669
    m[10]= m7 * -s + m8* c;
1670
1671
    return this;
1672
  },
1673
  rotateY: function(angle){
1674
    var m = this.m;
1675
    
1676
    var c = Math.cos(angle);
1677
    var s = Math.sin(angle);
1678
    var m0 = m[0], m3 = m[3], m6 = m[6],
1679
        m2 = m[2], m5 = m[5], m8= m[8];
1680
    
1681
    m[0] = m0 * c + m2 * -s;
1682
    m[3] = m3 * c + m5 * -s;
1683
    m[6] = m6 * c + m8* -s;
1684
1685
    m[2] = m0 *s + m2 * c;
1686
    m[5] = m3 *s + m5 * c;
1687
    m[8] = m6 *s + m8* c;
1688
1689
    return this;
1690
  },
1691
  rotateZ:function(angle){
1692
    var m = this.m;
1693
    
1694
    var c = Math.cos(angle);
1695
    var s = Math.sin(angle);
1696
    var m0 = m[0], m3 = m[3], m6 = m[6],
1697
        m1 = m[1], m4 = m[4], m7 = m[7];
1698
1699
    m[0] = m0 * c + m1 *s;
1700
    m[3] = m3 * c + m4 *s;
1701
    m[6] = m6 * c + m7 *s;
1702
1703
    m[1] = m0 * -s + m1 * c;
1704
    m[4] = m3 * -s + m4 * c;
1705
    m[7] = m6 * -s + m7 * c; 
1706
1707
    return this;
1708
  },
1709
  toString:function(){
1710
    var m = this.m;
1711
    return m[0].toFixed(4)+", "+m[1].toFixed(4)+", "+m[2].toFixed(4) + "\n" +
1712
           m[3].toFixed(4)+", "+m[4].toFixed(4)+", "+m[5].toFixed(4) + "\n" + 
1713
           m[6].toFixed(4)+", "+m[7].toFixed(4)+", "+m[8].toFixed(4) + "\n"; 
1714
  }
1715
};
1716
N3D.Math.Matrix3.Identity = function(){
1717
  return new this(1,0,0,0,1,0,0,0,1);
1718
};
1719
1720
N3D.Math.Matrix3.FromMatrix4 = function(m){
1721
  var m = m.m;
1722
  
1723
  return new this(
1724
    m[0], m[1], m[2],
1725
    m[4], m[5], m[6],
1726
    m[8], m[9], m[10]
1727
  );
1728
};
1729
1730
N3D.Math.Matrix3.Inverse = function(m){
1731
  var m = this.m,
1732
        m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3],
1733
        m4 = m[4], m5 = m[5], m6 = m[6], m7 = m[7],
1734
        m8 = m[8],
1735
        a0 = (m8*m4-m7*m5),
1736
        a1 = (m8*m1-m7*m2),
1737
        a2 = (m5*m1-m4*m2);
1738
    
1739
    var det = 1/(m0*a0-m3*a1+m6*a2);
1740
    
1741
    return new this(
1742
        a0*det,           -a1*det,              a2*det,
1743
      -(m8*m3-m6*m5)*det,  (m8*m0-m6*m2)*det, -(m5*m0-m3*m2)*det,
1744
       (m7*m3-m6*m4)*det, -(m7*m0-m6*m1)*det,  (m4*m0-m3*m1)*det 
1745
    );
1746
1747
    return this;
1748
};
1749
/* <<<< Math.Matrix3 <<<< */
1750
1751
1752
/* >>>> Math.Matrix4 >>>> */
1753
N3D.Math.Matrix4 = function(a00,a04,a08,a12,
1754
                            a01,a05,a09,a13,
1755
                            a02,a06,a10,a14,
1756
                            a03,a07,a11,a15){
1757
  this.elements =  [
1758
    a00,a01,a02,a03,
1759
    a04,a05,a06,a07,
1760
    a08,a09,a10,a11,
1761
    a12,a13,a14,a15
1762
  ];
1763
  return this;
1764
1765
};
1766
N3D.Math.Matrix4.prototype = {
1767
  constructor:N3D.Math.Matrix4,
1768
  getTranslate:function(){
1769
    var m = this.elements;
1770
    return new $V4(m[12],m[13],m[14],m[15]);
1771
  },
1772
  clone:function(){  
1773
    var m = this.elements;
1774
    return new this.constructor(
1775
      m[0],m[4],m[8],m[12],
1776
      m[1],m[5],m[9],m[13],
1777
      m[2],m[6],m[10],m[14],
1778
      m[3],m[7],m[11],m[15]
1779
    );
1780
  },
1781
  inverse:function(){ //zkontrolovat
1782
    var m = this.elements, 
1783
        m00 = m[0],  m04 = m[4],  m08 = m[8],  m12 = m[12],
1784
        m01 = m[1],  m05 = m[5],  m09 = m[9],  m13 = m[13],
1785
        m02 = m[2],  m06 = m[6],  m10 = m[10], m14 = m[14],
1786
        m03 = m[3],  m07 = m[7],  m11 = m[11], m15 = m[15];
1787
        
1788
    
1789
    var n0 =  m05 * (m10*m15 - m11*m14) - m06 * (m09*m15 - m11*m13) + m07 * (m09*m14 - m10*m13),
1790
        n1 =  m04 * (m10*m15 - m11*m14) - m06 * (m08*m15 - m11*m12) + m07 * (m08*m14 - m10*m12),
1791
        n2 =  m04 * (m09*m15 - m11*m13) - m05 * (m08*m15 - m11*m12) + m07 * (m08*m13 - m09*m12),
1792
        n3 =  m04 * (m09*m14 - m10*m13) - m05 * (m08*m14 - m10*m12) + m06 * (m08*m13 - m09*m12),  
1793
              
1794
        invDet = 1/(m00*n0 - m01*n1 + m02*n2 - m03*n3);
1795
    
1796
    m[0]  =  n0*invDet;
1797
    m[1]  = -n1*invDet;
1798
    m[2]  =  n2*invDet;
1799
    m[3]  = -n3*invDet;     
1800
    
1801
    m[4]  = -(m01 * (m10*m15 - m11*m14) - m02 * (m09*m15 - m11*m13) + m03 * (m09*m14 - m10*m13))*invDet;  
1802
    m[5]  =  (m00 * (m10*m15 - m11*m14) - m02 * (m08*m15 - m11*m12) + m03 * (m08*m14 - m10*m12))*invDet;
1803
    m[6]  = -(m00 * (m09*m15 - m11*m13) - m01 * (m08*m15 - m11*m12) + m03 * (m08*m13 - m09*m12))*invDet;
1804
    m[7]  =  (m00 * (m09*m14 - m10*m13) - m01 * (m08*m14 - m10*m12) + m02 * (m08*m13 - m09*m12))*invDet;
1805
    
1806
    m[8]  =  (m01 * (m06*m15 - m07*m14) - m02 * (m05*m15 - m07*m13) + m03 * (m05*m14 - m06*m13))*invDet;
1807
    m[9]  = -(m00 * (m06*m15 - m07*m14) - m02 * (m04*m15 - m07*m12) + m03 * (m04*m14 - m06*m12))*invDet;
1808
    m[10] =  (m00 * (m05*m15 - m07*m13) - m01 * (m04*m15 - m07*m12) + m03 * (m04*m13 - m05*m12))*invDet;
1809
    m[11] = -(m00 * (m05*m14 - m06*m13) - m01 * (m04*m14 - m06*m12) + m02 * (m04*m13 - m05*m12))*invDet;
1810
    
1811
    m[12] = -(m01 * (m06*m11 - m07*m10) - m02 * (m05*m11 - m07*m09) + m03 * (m05*m10 - m06*m09))*invDet;
1812
    m[13] =  (m00 * (m06*m11 - m07*m10) - m02 * (m04*m11 - m07*m08) + m03 * (m04*m10 - m06*m08))*invDet;
1813
    m[14] = -(m00 * (m05*m11 - m07*m09) - m01 * (m04*m11 - m07*m08) + m03 * (m04*m09 - m05*m08))*invDet;
1814
    m[15] =  (m00 * (m05*m10 - m06*m09) - m01 * (m04*m10 - m06*m08) + m02 * (m04*m09 - m05*m08))*invDet; 
1815
1816
    return this;
1817
  },
1818
  inverseFast:function(){
1819
    var m = this.elements, 
1820
        m00 = m[0],  m04 = m[4],  m08 = m[8],  m12 = m[12],
1821
        m01 = m[1],  m05 = m[5],  m09 = m[9],  m13 = m[13],
1822
        m02 = m[2],  m06 = m[6],  m10 = m[10], m14 = m[14],
1823
        m03 = m[3],  m07 = m[7],  m11 = m[11], m15 = m[15];
1824
    
1825
    
1826
    var a0813 = m08 * m13, a0814 = m08 * m14, a0815 = m08 * m15,
1827
        a0912 = m09 * m12, a0914 = m09 * m14, a0915 = m09 * m15,
1828
        a1012 = m10 * m12, a1013 = m10 * m13, a1015 = m10 * m15,
1829
        a1112 = m11 * m12, a1113 = m11 * m13, a1114 = m11 * m14;
1830
        
1831
    var n0 =  m05 * (a1015 - a1114) - m06 * (a0915 - a1113) + m07 * (a0914 - a1013),
1832
        n1 =  m04 * (a1015 - a1114) - m06 * (a0815 - a1112) + m07 * (a0814 - a1012),
1833
        n2 =  m04 * (a0915 - a1113) - m05 * (a0815 - a1112) + m07 * (a0813 - a0912),
1834
        n3 =  m04 * (a0914 - a1013) - m05 * (a0814 - a1012) + m06 * (a0813 - a0912),  
1835
              
1836
        invDet = 1/(m00*n0 - m01*n1 + m02*n2 - m03*n3);
1837
    
1838
    m[0]  =  n0*invDet;
1839
    m[1]  = -n1*invDet;
1840
    m[2]  =  n2*invDet;
1841
    m[3]  = -n3*invDet;     
1842
    
1843
    m[4]  = -(m01 * (a1015 - a1114) - m02 * (a0915 - a1113) + m03 * (a0914 - a1013))*invDet;  
1844
    m[5]  =  (m00 * (a1015 - a1114) - m02 * (a0815 - a1112) + m03 * (a0814 - a1012))*invDet;
1845
    m[6]  = -(m00 * (a0915 - a1113) - m01 * (a0815 - a1112) + m03 * (a0813 - a0912))*invDet;
1846
    m[7]  =  (m00 * (a0914 - a1013) - m01 * (a0814 - a1012) + m02 * (a0813 - a0912))*invDet;
1847
    
1848
    m[8]  =  (m01 * (m06*m15 - m07*m14) - m02 * (m05*m15 - m07*m13) + m03 * (m05*m14 - m06*m13))*invDet;
1849
    m[9]  = -(m00 * (m06*m15 - m07*m14) - m02 * (m04*m15 - m07*m12) + m03 * (m04*m14 - m06*m12))*invDet;
1850
    m[10] =  (m00 * (m05*m15 - m07*m13) - m01 * (m04*m15 - m07*m12) + m03 * (m04*m13 - m05*m12))*invDet;
1851
    m[11] = -(m00 * (m05*m14 - m06*m13) - m01 * (m04*m14 - m06*m12) + m02 * (m04*m13 - m05*m12))*invDet;
1852
    
1853
    m[12] = -(m01 * (m06*m11 - m07*m10) - m02 * (m05*m11 - m07*m09) + m03 * (m05*m10 - m06*m09))*invDet;
1854
    m[13] =  (m00 * (m06*m11 - m07*m10) - m02 * (m04*m11 - m07*m08) + m03 * (m04*m10 - m06*m08))*invDet;
1855
    m[14] = -(m00 * (m05*m11 - m07*m09) - m01 * (m04*m11 - m07*m08) + m03 * (m04*m09 - m05*m08))*invDet;
1856
    m[15] =  (m00 * (m05*m10 - m06*m09) - m01 * (m04*m10 - m06*m08) + m02 * (m04*m09 - m05*m08))*invDet; 
1857
1858
    return this;
1859
  },
1860
  multiply:function(m2){
1861
    var m1 = this.elements,
1862
        m2 = m2.elements, 
1863
      
1864
        m1_00 = m1[0],  m1_04 = m1[4],  m1_08 = m1[8],  m1_12 = m1[12],
1865
        m1_01 = m1[1],  m1_05 = m1[5],  m1_09 = m1[9],  m1_13 = m1[13],
1866
        m1_02 = m1[2],  m1_06 = m1[6],  m1_10 = m1[10], m1_14 = m1[14],
1867
        m1_03 = m1[3],  m1_07 = m1[7],  m1_11 = m1[11], m1_15 = m1[15], 
1868
                                                   
1869
        m2_00 = m2[0],  m2_04 = m2[4],  m2_08 = m2[8],  m2_12 = m2[12],
1870
        m2_01 = m2[1],  m2_05 = m2[5],  m2_09 = m2[9],  m2_13 = m2[13],
1871
        m2_02 = m2[2],  m2_06 = m2[6],  m2_10 = m2[10], m2_14 = m2[14],
1872
        m2_03 = m2[3],  m2_07 = m2[7],  m2_11 = m2[11], m2_15 = m2[15];
1873
      
1874
    m1[0] = m1_00*m2_00 + m1_01*m2_04 + m1_02*m2_08 + m1_03*m2_12;
1875
    m1[4] = m1_04*m2_00 + m1_05*m2_04 + m1_06*m2_08 + m1_07*m2_12;
1876
    m1[8] = m1_08*m2_00 + m1_09*m2_04 + m1_10*m2_08 + m1_11*m2_12;
1877
    m1[12] = m1_12*m2_00 + m1_13*m2_04 + m1_14*m2_08 + m1_15*m2_12;
1878
    
1879
    m1[1] = m1_00*m2_01 + m1_01*m2_05 + m1_02*m2_09 + m1_03*m2_13;
1880
    m1[5] = m1_04*m2_01 + m1_05*m2_05 + m1_06*m2_09 + m1_07*m2_13;
1881
    m1[9] = m1_08*m2_01 + m1_09*m2_05 + m1_10*m2_09 + m1_11*m2_13;
1882
    m1[13] = m1_12*m2_01 + m1_13*m2_05 + m1_14*m2_09 + m1_15*m2_13;
1883
    
1884
    m1[2] = m1_00*m2_02 + m1_01*m2_06 + m1_02*m2_10 + m1_03*m2_14;
1885
    m1[6] = m1_04*m2_02 + m1_05*m2_06 + m1_06*m2_10 + m1_07*m2_14;
1886
    m1[10] = m1_08*m2_02 + m1_09*m2_06 + m1_10*m2_10 + m1_11*m2_14;
1887
    m1[14] = m1_12*m2_02 + m1_13*m2_06 + m1_14*m2_10 + m1_15*m2_14;
1888
    
1889
    m1[3] = m1_00*m2_03 + m1_01*m2_07 + m1_02*m2_11 + m1_03*m2_15;
1890
    m1[7] = m1_04*m2_03 + m1_05*m2_07 + m1_06*m2_11 + m1_07*m2_15;
1891
    m1[11] = m1_08*m2_03 + m1_09*m2_07 + m1_10*m2_11 + m1_11*m2_15;
1892
    m1[15] = m1_12*m2_03 + m1_13*m2_07 + m1_14*m2_11 + m1_15*m2_15;
1893
    
1894
    return this;
1895
  },
1896
  multiplyTranspose:function(m2){
1897
    var m1 = this.elements,
1898
        m2 = m2.elements, 
1899
        m1_00 = m1[0],  m1_04 = m1[4],  m1_08 = m1[8],  m1_12 = m1[12],
1900
        m1_01 = m1[1],  m1_05 = m1[5],  m1_09 = m1[9],  m1_13 = m1[13],
1901
        m1_02 = m1[2],  m1_06 = m1[6],  m1_10 = m1[10], m1_14 = m1[14],
1902
        m1_03 = m1[3],  m1_07 = m1[7],  m1_11 = m1[11], m1_15 = m1[15], 
1903
                                                   
1904
        m2_00 = m2[0],  m2_04 = m2[4],  m2_08 = m2[8],  m2_12 = m2[12],
1905
        m2_01 = m2[1],  m2_05 = m2[5],  m2_09 = m2[9],  m2_13 = m2[13],
1906
        m2_02 = m2[2],  m2_06 = m2[6],  m2_10 = m2[10], m2_14 = m2[14],
1907
        m2_03 = m2[3],  m2_07 = m2[7],  m2_11 = m2[11], m2_15 = m2[15];
1908
1909
    m1[0] =   m1_00*m2_00 + m1_04*m2_01 + m1_08*m2_02 + m1_12*m2_03;
1910
    m1[1] =   m1_01*m2_00 + m1_05*m2_01 + m1_09*m2_02 + m1_13*m2_03;
1911
    m1[2] =   m1_02*m2_00 + m1_06*m2_01 + m1_10*m2_02 + m1_14*m2_03;
1912
    m1[3] =  m1_03*m2_00 + m1_07*m2_01 + m1_11*m2_02 + m1_15*m2_03;
1913
    
1914
    m1[4] =   m1_00*m2_04 + m1_04*m2_05 + m1_08*m2_06 + m1_12*m2_07;
1915
    m1[5] =   m1_01*m2_04 + m1_05*m2_05 + m1_09*m2_06 + m1_13*m2_07;
1916
    m1[6] =   m1_02*m2_04 + m1_06*m2_05 + m1_10*m2_06 + m1_14*m2_07;
1917
    m1[7] =  m1_03*m2_04 + m1_07*m2_05 + m1_11*m2_06 + m1_15*m2_07;    
1918
    
1919
    m1[8] =   m1_00*m2_08 + m1_04*m2_09 + m1_08*m2_10 + m1_12*m2_11;
1920
    m1[9] =   m1_01*m2_08 + m1_05*m2_09 + m1_09*m2_10 + m1_13*m2_11;
1921
    m1[10] =  m1_02*m2_08 + m1_06*m2_09 + m1_10*m2_10 + m1_14*m2_11;
1922
    m1[11] =  m1_03*m2_08 + m1_07*m2_09 + m1_11*m2_10 + m1_15*m2_11;
1923
    
1924
    m1[12] =   m1_00*m2_12 + m1_04*m2_13 + m1_08*m2_14 + m1_12*m2_15;
1925
    m1[13] =   m1_01*m2_12 + m1_05*m2_13 + m1_09*m2_14 + m1_13*m2_15;
1926
    m1[14] =  m1_02*m2_12 + m1_06*m2_13 + m1_10*m2_14 + m1_14*m2_15;
1927
    m1[15] =  m1_03*m2_12 + m1_07*m2_13 + m1_11*m2_14 + m1_15*m2_15;
1928
1929
    return this;
1930
  },
1931
  multiplyVector4:function(v){
1932
    var x = v.x, y = v.y, z = v.z, w = v.w;
1933
    var m = this.elements;
1934
1935
    return new v.constructor(
1936
      m[0]*x + m[4]*y + m[8]*z + m[12]*w,
1937
      m[1]*x + m[5]*y + m[9]*z + m[13]*w,
1938
      m[2]*x + m[6]*y + m[10]*z + m[14]*w,
1939
      m[3]*x + m[7]*y + m[11]*z + m[15]*w
1940
    );
1941
  },
1942
  rotateX: function(radians){
1943
    var m = this.elements;
1944
    
1945
    var c = Math.cos(radians);
1946
    var s = Math.sin(radians);
1947
    var m04 = m[4], m05 = m[5], m06 = m[6],  m07 = m[7],
1948
        m08 = m[8], m09 = m[9], m10 = m[10], m11= m[11];
1949
    
1950
    
1951
    m[4] = m04 * c + m08 * s; m[5] = m05 * c + m09 * s; m[6] = m06 * c + m10 * s; m[7] = m07 * c + m11 * s;
1952
    m[8] = m04 * -s + m08 * c; m[9] = m05 * -s + m09 * c; m[10] = m06 * -s + m10 * c; m[11] = m07 * -s + m11 * c;
1953
    
1954
    return this;
1955
  },
1956
  rotateY: function(radians){
1957
    var m = this.elements;
1958
    
1959
    var c = Math.cos(radians);
1960
    var s = Math.sin(radians);
1961
    var m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3],
1962
        m08 = m[8], m09 = m[9], m10= m[10], m11= m[11];
1963
    
1964
    m[0] = m00 * c + m02 * -s; m[1] = m01 * c + m09 * -s; m[2] = m02 * c + m10* -s; m[3] = m03 * c + m11* -s;
1965
    
1966
    m[8] = m00 * s + m02 * c; m[9] = m01 * s + m09 * c; m[10]= m02 * s + m10* c; m[11] = m03 * s + m11* c;    
1967
1968
    return this;
1969
  },
1970
  rotateZ: function(radians){
1971
    var m = this.elements;
1972
    
1973
    var c = Math.cos(radians);
1974
    var s = Math.sin(radians);
1975
    var m00 = m[0], m01 = m[1], m02 = m[2],  m03 = m[3],
1976
        m04 = m[4], m05 = m[5], m06 = m[6],  m07 = m[7];
1977
    
1978
    m[0] = m00 * c + m04 * s; m[1] = m01 * c + m05 * s; m[2] = m02 * c + m06* s; m[3] = m03 * c + m07* s;
1979
    m[4] = m00 * -s + m04 * c; m[5] = m01 * -s + m05 * c; m[6] = m02 * -s + m06* c; m[7] = m03 * -s + m07* c;
1980
1981
    return this;
1982
  },  
1983
  rotateAroundAxis:function(r,v){
1984
    var c = Math.cos(r), s = Math.sin(r);
1985
    
1986
    var x = v.x,y = v.y, z = v.z, t = 1-c,
1987
        xyt = x*y*t, xzt = x*z*t, yzt = y*z*t,
1988
        xs = x*s, ys = y*s, zs = z*s;
1989
        
1990
    var m = this.elements, 
1991
        m00 = m[0],  m04 = m[4],  m08 = m[8],  m12 = m[12],
1992
        m01 = m[1],  m05 = m[5],  m09 = m[9],  m13 = m[13],
1993
        m02 = m[2],  m06 = m[6],  m10 = m[10], m14 = m[14],
1994
        m03 = m[3],  m07 = m[7],  m11 = m[11], m15 = m[15];
1995
    
1996
    var a00 = c+x*x*t, a04 = xyt-zs, a08 = xzt+ys,
1997
        a01 = xyt+zs,  a05 = c+y*y*t,a09 = yzt-xs,
1998
        a02 = xzt-ys,  a06 = yzt+xs, a10 = c+z*z*t;
1999
2000
    m[0] = m00*a00 + m01*a04 + m02*a08;
2001
    m[4] = m04*a00 + m05*a04 + m06*a08;
2002
    m[8] = m08*a00 + m09*a04 + m10*a08;
2003
    m[12] = m12*a00 + m13*a04 + m14*a08;   
2004
    
2005
    m[1] = m00*a01 + m01*a05 + m02*a09;
2006
    m[5] = m04*a01 + m05*a05 + m06*a09;
2007
    m[9] = m08*a01 + m09*a05 + m10*a09;
2008
    m[13] = m12*a01 + m13*a05 + m14*a09;
2009
    
2010
    m[2] = m00*a02 + m01*a06 + m02*a10;
2011
    m[6] = m04*a02 + m05*a06 + m06*a10;
2012
    m[10] = m08*a02 + m09*a06 + m10*a10;
2013
    m[14] = m12*a02 + m13*a06 + m14*a10;
2014
2015
    return this;
2016
  },
2017
  scale:function(x,y,z){
2018
    var m = this.elements;
2019
    
2020
    m[0] = m[0]*x;  m[4] = m[4]*y;  m[8] = m[8]*z;         
2021
    m[1] = m[1]*x;  m[5] = m[5]*y;  m[9] = m[9]*z;
2022
    m[2] = m[2]*x;  m[6] = m[6]*y;  m[10]= m[10]*z;
2023
    m[3] = m[3]*x;  m[7] = m[7]*y;  m[11]= m[11]*z;   
2024
    
2025
    return this;
2026
  },
2027
  translate:function(x,y,z){
2028
    var m = this.elements;
2029
    
2030
    m[12] = m[0]*x + m[4]*y + m[8]*z + m[12];
2031
        m[13] = m[1]*x + m[5]*y + m[9]*z + m[13];
2032
        m[14] = m[2]*x + m[6]*y + m[10]*z + m[14];
2033
        m[15] = m[3]*x + m[7]*y + m[11]*z + m[15];
2034
    
2035
    return this;
2036
  },
2037
  transpose:function(){
2038
    var m = this.elements;
2039
    var a01 = m[1], a02 = m[2], a03 = m[3],
2040
        a12 = m[6], a13 = m[7],
2041
        a23 = m[11];
2042
                
2043
    m[1] = m[4]; m[2] = m[8]; m[3] = m[12];
2044
    m[4] = a01;  m[6] = m[9]; m[7] = m[13];
2045
    m[8] = a02;  m[9] = a12;  m[11] = m[14];
2046
    m[12] = a03; m[13] = a13; m[14] = a23;
2047
    
2048
    return this;  
2049
  },
2050
  toQuaternion:function(){
2051
    var m = this.elements, 
2052
        m00 = m[0],  m04 = m[4],  m08 = m[8],
2053
        m01 = m[1],  m05 = m[5],  m09 = m[9],
2054
        m02 = m[2],  m06 = m[6],  m10 = m[10];
2055
    var max = Math.max, sqrt = Math.sqrt;
2056
    
2057
    return new N3D_M_Quaternion(
2058
      sqrt(max(0,1+m00-m05-m10))*0.5, //sqrt( max( 0, 1 + m00 - m11 - m22 ) ) / 2;
2059
      sqrt(max(0,1-m00+m05-m10))*0.5, //sqrt( max( 0, 1 - m00 + m11 - m22 ) ) / 2;
2060
      sqrt(max(0,1-m00-m05+m10))*0.5, //sqrt( max( 0, 1 - m00 - m11 + m22 ) ) / 2;
2061
      sqrt(max(0,1+m00+m05+m10))*0.5  //sqrt( max( 0, 1 + m00 + m11 + m22 ) ) / 2;
2062
    ); 
2063
    
2064
    /*    
2065
    var trace = m[0] + m[5] + m[10];
2066
    var s;
2067
    
2068
    if(trace>0){
2069
      s = 0.5/Math.sqrt(trace+1);
2070
2071
      return new N3D_M_Vector4(
2072
        (m[9] - m[6]) * s,
2073
        (m[2] - m[8]) * s,
2074
        (m[4] - m[1]) * s,
2075
        0.25 / s
2076
      );
2077
    }else if((m00>m05) && (m00>m10)){
2078
      s = 0.5/Math.sqrt(1 + m00 - m05 - m10)
2079
2080
      return new N3D_M_Vector4(
2081
        0.25 / s,
2082
        (m01 + m04) * s,
2083
        (m02 + m08) * s,
2084
        (m09 - m06) * s        
2085
      );
2086
      
2087
    }else if(m05 > m10){
2088
      s = 0.5/Math.sqrt(1 + m05 - m00 - m10);
2089
      
2090
      return new N3D_M_Vector4(
2091
        (m01 + m04) * s,
2092
        0.25 / s,
2093
        (m06 + m09) * s,
2094
        (m02 - m08) * s
2095
      );
2096
       
2097
    }
2098
    
2099
    
2100
    s = 0.5/Math.sqrt(1+m10 - m00 - m05);
2101
    return new N3D_M_Vector4(
2102
      (m02 + m08) * s,
2103
      (m06 + m09) * s,
2104
      0.25 / s,
2105
      (m04 - m01) * s
2106
    );
2107
    */    
2108
  },
2109
  toString:function(){
2110
    var e = this.elements;
2111
    return '01: '+e[0].toFixed(3)+', 04: '+e[4].toFixed(3)+', 08: '+e[8].toFixed(3)+', 12: '+e[12].toFixed(3) + '\n' + 
2112
           '02: '+e[1].toFixed(3)+', 05: '+e[5].toFixed(3)+', 09: '+e[9].toFixed(3)+', 13: '+e[13].toFixed(3) + '\n' + 
2113
           '03: '+e[2].toFixed(3)+', 06: '+e[6].toFixed(3)+', 10: '+e[10].toFixed(3)+', 14: '+e[14].toFixed(3) + '\n' + 
2114
           '04: '+e[3].toFixed(3)+', 07: '+e[7].toFixed(3)+', 11: '+e[11].toFixed(3)+', 15: '+e[15].toFixed(3);
2115
  }
2116
};
2117
2118
N3D.Math.Matrix4.Identity = function(){
2119
  return new N3D_M_Matrix4(
2120
    1,0,0,0,
2121
    0,1,0,0,
2122
    0,0,1,0,
2123
    0,0,0,1
2124
  );
2125
};
2126
2127
2128
N3D.Math.Matrix4.Multiply = function(m1,m2){
2129
  var m1 = m1.elements,
2130
      m2 = m2.elements, 
2131
      
2132
      m1_00 = m1[0],  m1_04 = m1[4],  m1_08 = m1[8],  m1_12 = m1[12],
2133
      m1_01 = m1[1],  m1_05 = m1[5],  m1_09 = m1[9],  m1_13 = m1[13],
2134
      m1_02 = m1[2],  m1_06 = m1[6],  m1_10 = m1[10], m1_14 = m1[14],
2135
      m1_03 = m1[3],  m1_07 = m1[7],  m1_11 = m1[11], m1_15 = m1[15], 
2136
                                                   
2137
      m2_00 = m2[0],  m2_04 = m2[4],  m2_08 = m2[8],  m2_12 = m2[12],
2138
      m2_01 = m2[1],  m2_05 = m2[5],  m2_09 = m2[9],  m2_13 = m2[13],
2139
      m2_02 = m2[2],  m2_06 = m2[6],  m2_10 = m2[10], m2_14 = m2[14],
2140
      m2_03 = m2[3],  m2_07 = m2[7],  m2_11 = m2[11], m2_15 = m2[15];
2141
      
2142
  return new N3D_M_Matrix4(
2143
    m1_00*m2_00 + m1_01*m2_04 + m1_02*m2_08 + m1_03*m2_12,
2144
    m1_04*m2_00 + m1_05*m2_04 + m1_06*m2_08 + m1_07*m2_12,
2145
    m1_08*m2_00 + m1_09*m2_04 + m1_10*m2_08 + m1_11*m2_12,
2146
    m1_12*m2_00 + m1_13*m2_04 + m1_14*m2_08 + m1_15*m2_12,
2147
    
2148
    m1_00*m2_01 + m1_01*m2_05 + m1_02*m2_09 + m1_03*m2_13,
2149
    m1_04*m2_01 + m1_05*m2_05 + m1_06*m2_09 + m1_07*m2_13,
2150
    m1_08*m2_01 + m1_09*m2_05 + m1_10*m2_09 + m1_11*m2_13,
2151
    m1_12*m2_01 + m1_13*m2_05 + m1_14*m2_09 + m1_15*m2_13,
2152
    
2153
    m1_00*m2_02 + m1_01*m2_06 + m1_02*m2_10 + m1_03*m2_14,
2154
    m1_04*m2_02 + m1_05*m2_06 + m1_06*m2_10 + m1_07*m2_14,
2155
    m1_08*m2_02 + m1_09*m2_06 + m1_10*m2_10 + m1_11*m2_14,
2156
    m1_12*m2_02 + m1_13*m2_06 + m1_14*m2_10 + m1_15*m2_14,
2157
    
2158
    m1_00*m2_03 + m1_01*m2_07 + m1_02*m2_11 + m1_03*m2_15,
2159
    m1_04*m2_03 + m1_05*m2_07 + m1_06*m2_11 + m1_07*m2_15,
2160
    m1_08*m2_03 + m1_09*m2_07 + m1_10*m2_11 + m1_11*m2_15,
2161
    m1_12*m2_03 + m1_13*m2_07 + m1_14*m2_11 + m1_15*m2_15
2162
  );   
2163
};
2164
2165
2166
2167
N3D.Math.Matrix4.MultiplyTranspose = function(m1,m2){
2168
  var m1 = m1.elements,
2169
      m2 = m2.elements, 
2170
      
2171
      m1_00 = m1[0],  m1_04 = m1[4],  m1_08 = m1[8],  m1_12 = m1[12],
2172
      m1_01 = m1[1],  m1_05 = m1[5],  m1_09 = m1[9],  m1_13 = m1[13],
2173
      m1_02 = m1[2],  m1_06 = m1[6],  m1_10 = m1[10], m1_14 = m1[14],
2174
      m1_03 = m1[3],  m1_07 = m1[7],  m1_11 = m1[11], m1_15 = m1[15], 
2175
                                                   
2176
      m2_00 = m2[0],  m2_04 = m2[4],  m2_08 = m2[8],  m2_12 = m2[12],
2177
      m2_01 = m2[1],  m2_05 = m2[5],  m2_09 = m2[9],  m2_13 = m2[13],
2178
      m2_02 = m2[2],  m2_06 = m2[6],  m2_10 = m2[10], m2_14 = m2[14],
2179
      m2_03 = m2[3],  m2_07 = m2[7],  m2_11 = m2[11], m2_15 = m2[15];
2180
    
2181
  return new N3D_M_Matrix4(        
2182
    m1_00*m2_00 + m1_04*m2_01 + m1_08*m2_02 + m1_12*m2_03,
2183
    m1_00*m2_04 + m1_04*m2_05 + m1_08*m2_06 + m1_12*m2_07,
2184
    m1_00*m2_08 + m1_04*m2_09 + m1_08*m2_10 + m1_12*m2_11,
2185
    m1_00*m2_12 + m1_04*m2_13 + m1_08*m2_14 + m1_12*m2_15,
2186
    
2187
    m1_01*m2_00 + m1_05*m2_01 + m1_09*m2_02 + m1_13*m2_03,
2188
    m1_01*m2_04 + m1_05*m2_05 + m1_09*m2_06 + m1_13*m2_07,
2189
    m1_01*m2_08 + m1_05*m2_09 + m1_09*m2_10 + m1_13*m2_11,
2190
    m1_01*m2_12 + m1_05*m2_13 + m1_09*m2_14 + m1_13*m2_15,
2191
    
2192
    m1_02*m2_00 + m1_06*m2_01 + m1_10*m2_02 + m1_14*m2_03,
2193
    m1_02*m2_04 + m1_06*m2_05 + m1_10*m2_06 + m1_14*m2_07,
2194
    m1_02*m2_08 + m1_06*m2_09 + m1_10*m2_10 + m1_14*m2_11,
2195
    m1_02*m2_12 + m1_06*m2_13 + m1_10*m2_14 + m1_14*m2_15,
2196
    
2197
    m1_03*m2_00 + m1_07*m2_01 + m1_11*m2_02 + m1_15*m2_03,
2198
    m1_03*m2_04 + m1_07*m2_05 + m1_11*m2_06 + m1_15*m2_07,
2199
    m1_03*m2_08 + m1_07*m2_09 + m1_11*m2_10 + m1_15*m2_11,
2200
    m1_03*m2_12 + m1_07*m2_13 + m1_11*m2_14 + m1_15*m2_15 
2201
  );  
2202
};
2203
2204
N3D.Math.Matrix4.CreateLookAt = function(eye,target,up){
2205
  var f = N3D_M_Vector3.Sub(eye,target).normalize(),
2206
      s = N3D_M_Vector3.Cross(up,f).normalize(),
2207
      u = N3D_M_Vector3.Cross(f,s); 
2208
      
2209
  return new N3D_M_Matrix4(
2210
    s.x,  u.x,  f.x,  -eye.x,
2211
    s.y,  u.y,  f.y,  -eye.y,
2212
    s.z,  u.z,  f.z,  -eye.z,
2213
    0,    0,    0,    1 
2214
  );
2215
};
2216
2217
N3D.Math.Matrix4.CreateRotationX = function(r){
2218
  var c = Math.cos(r), s = Math.sin(r);
2219
  
2220
  return new N3D_M_Matrix4(
2221
    1,0,0,0,
2222
    0,c,-s,0,
2223
    0,s,c,0,
2224
    0,0,0,1
2225
  );
2226
};
2227
2228
2229
N3D.Math.Matrix4.CreateRotationY = function(r){
2230
  var c = Math.cos(r), s = Math.sin(r);
2231
  
2232
  return new N3D_M_Matrix4(
2233
    c,0,s,0,
2234
    0,1,0,0,
2235
    -s,0,c,0,
2236
    0,0,0,1
2237
  );
2238
};
2239
N3D.Math.Matrix4.CreateRotationZ = function(r){
2240
  var c = Math.cos(r), s = Math.sin(r);
2241
  
2242
  return new N3D_M_Matrix4(
2243
    c,-s,0,0,
2244
    s,c,0,0,
2245
    0,0,1,0,
2246
    0,0,0,1
2247
  );
2248
};
2249
2250
N3D.Math.Matrix4.CreateRotationAroundAxis = function(r,v){
2251
  var c = Math.cos(r), s = Math.sin(r);
2252
  var x = v.x,y = v.y, z = v.z,
2253
      t = 1-c,
2254
      xyt = x*y*t, xzt = x*z*t, yzt = y*z*t,
2255
      xs = x*s, ys = y*s, zs = z*s;
2256
  
2257
  return new N3D_M_Matrix4(
2258
    c+x*x*t, xyt-zs,  xzt+ys,  0,
2259
    xyt+zs,  c+y*y*t,  yzt-xs,  0,
2260
    xzt-ys,  yzt+xs,  c+z*z*t,  0,
2261
    0,        0,        0,        1
2262
  );
2263
};
2264
2265
N3D.Math.Matrix4.CreateScale = function(x,y,z){
2266
  return new N3D_M_Matrix4(
2267
    x,0,0,0,
2268
    0,y,0,0,
2269
    0,0,z,0,
2270
    0,0,0,1
2271
  );
2272
};
2273
2274
N3D.Math.Matrix4.CreateTranslation = function(x,y,z){
2275
  return new N3D_M_Matrix4(
2276
    1,0,0,x,
2277
    0,1,0,y,
2278
    0,0,1,z,
2279
    0,0,0,1
2280
  );
2281
};
2282
2283
N3D.Math.Matrix4.CreateFrustum = function(left,right,bottom,top,near,far){
2284
  var rl = right - left,
2285
      tb = top - bottom,
2286
      fn = far - near,
2287
      n2 = 2 * near;
2288
  return new N3D_M_Matrix4(
2289
    n2/rl,            0,                0,                0,
2290
    0,                n2/tb,            0,                0,
2291
    (right+left)/rl,  (top+bottom)/tb,  -(far+near)/fn,   -1,
2292
    0,                0,                -n2*far/fn,         0
2293
  );
2294
};
2295
2296
N3D.Math.Matrix4.CreatePerspective = function(angle,aspectRatio,near,far){
2297
  var scale = Math.tan(angle * N3D_M.PiOver360) * near,
2298
      right = aspectRatio * scale,
2299
      fn = far - near,
2300
      tb = scale + scale,
2301
      rl = right + right,
2302
      n2 = 2 * near;
2303
      
2304
  return new N3D_M_Matrix4(
2305
    n2/rl,  0,      (right-right)/rl,       0,
2306
    0,      n2/tb,  (scale-scale)/tb,       0,
2307
    0,      0,      -(far+near)/fn,         -1,
2308
    0,      0,      -n2*far/fn,             0
2309
  );
2310
};
2311
2312
N3D.Math.Matrix4.CreatePerspective2 = function(angle,aspectRatio,near,far){
2313
  var scale = Math.tan(angle * N3D_M.PiOver360) * near,
2314
      right = aspectRatio * scale;
2315
2316
  return N3D_M_Matrix4.CreateFrustum(-right,right,-scale,scale,near,far);
2317
};
2318
2319
N3D.Math.Matrix4.CreateOrthographic = function(l,r,b,t,n,f){
2320
  var rl = r - l,
2321
        tb = t - b,
2322
        fn = f - n;
2323
  
2324
  return new N3D_M_Matrix4(
2325
    2/rl,       0,          0,          0,
2326
    0,          2/tb,       0,          0,
2327
    0,          0,          -2/fn,      0,
2328
    -(l+r)/rl,  -(t+b)/tb,  -(f+n)/fn,  1
2329
  );
2330
};
2331
2332
N3D.Math.Matrix4.CreateFromQuaternion = function(q){
2333
  q.normalize();
2334
  
2335
  var x = q.x, y = q.y, z = q.z, w = 0;
2336
  
2337
  var xx = x*x, xy = x*y, xz = x*z, xw = x*w, 
2338
      yy = y*y, yz = y*z, yw = y*w,
2339
      zz = z*z, zw = z*w;
2340
  
2341
  return new N3D_M_Matrix4(
2342
    1 - 2*(yy+zz),  2*(xy-zw),    2*(xz+yw),    0,
2343
    2*(xy+zw),      1-2*(xx+zz),  2*(yz-xw),    0,
2344
    2*(xz-yw),      2*(yz+xw),    1-2*(xx+yy),  0,
2345
    0,              0,            0,            1 
2346
     
2347
  );
2348
  /*var xx2 = 2*x*x, yy2 = 2*y*y, zz2 = 2*z*z;
2349
  
2350
  return new N3D_M_Matrix4(
2351
    1-yy2 - zz2, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
2352
    2*x*y + 2*z*w, 1-xx2 - zz2, 2*y*z - 2*x*w, 0,
2353
    2*x*z - 2*y*w, 2*y*z + 2*x*w, 1-xx2 - yy2, 0,
2354
    0,0,0,1 
2355
  
2356
  ); */
2357
};
2358
/* <<<< Math.Matrix4 <<<< */
2359
2360
2361
/* >>>> Math.Vector2 >>>> */
2362
N3D.Math.Vector2 = function(x,y){
2363
  this.x = x;
2364
  this.y = y;
2365
2366
  return this;
2367
};
2368
N3D.Math.Vector2.prototype = {
2369
  constructor:N3D.Math.Vector2,
2370
  xy:function(){
2371
    return [this.x,this.y];
2372
  },
2373
  clone:function(){
2374
    return new N3D_M_Vector2(this.x,this.y);
2375
  },
2376
  add:function(v){
2377
    this.x += v.x;
2378
    this.y += v.y;
2379
    
2380
    return this;
2381
  },
2382
  sub:function(v){
2383
    this.x -= v.x;
2384
    this.y -= v.y;
2385
    
2386
    return this;  
2387
  },
2388
  multiply:function(v){
2389
    this.x *= v.x;
2390
    this.y *= v.y;
2391
    
2392
    return this;  
2393
  },
2394
  scale:function(n){
2395
    this.x *= n;
2396
    this.y *= n;
2397
    
2398
    return this;
2399
  },
2400
  normalize:function(){
2401
    var x = this.x,y = this.y;
2402
    var length = Math.sqrt(x*x + y*y);
2403
    
2404
    this.x /= length;
2405
    this.y /= length;
2406
    
2407
    return this; 
2408
  },
2409
  perpendicular:function(){
2410
   var x = this.x,y = this.y;
2411
   var scale_factor = 1 / Math.sqrt(x*x + y*y);
2412
   
2413
   this.x = -1 * y;
2414
   this.y = x;
2415
   
2416
   return this;
2417
  },
2418
  divide:function(v){
2419
    this.x /= v.x;
2420
    this.y /= v.y;
2421
    
2422
    return this;  
2423
  },
2424
  rotate:function(angle){
2425
    var x = this.x, y = this.y;
2426
    
2427
    var cos = Math.cos(angle);
2428
    var sin = Math.sin(angle);
2429
    
2430
    this.x = x*cos - y*sin;
2431
    this.y = x*sin + y*cos;
2432
    
2433
    return this;
2434
  },
2435
  dot:function(v){
2436
    return (this.x * v.x + this.y * v.y);
2437
  },
2438
  toString:function(){
2439
    return "N3D.Math.Vector2("+this.x+","+this.y+")";
2440
  },
2441
  negative:function(){
2442
    return this.scale(-1);
2443
  },
2444
  equals:function(v){
2445
    return (this.x == v.x && this.y == v.y);
2446
  },
2447
  distance:function(v){
2448
    var x = this.x-v.x;
2449
    var y = this.y-v.y;
2450
    return Math.sqrt(x*x + y*y);
2451
  }  
2452
};
2453
N3D.Math.Vector2.Equals = function(v1,v2){
2454
  return (v1.x == v2.x && v1.y == v2.y);
2455
};
2456
N3D.Math.Vector2.Identity = function(){
2457
  return new N3D_M_Vector2(0,0);
2458
};
2459
N3D.Math.Vector2.Add = function(v1,v2){
2460
  return new N3D_M_Vector2(v2.x+v1.x,v2.y+v1.y);
2461
};
2462
2463
N3D.Math.Vector2.MultiplyScalar = function(v,n){
2464
  return new N3D_M_Vector2(v.x*n,v.y*n);
2465
};
2466
N3D.Math.Vector2.Dot = function(v1, v2){
2467
  return (v1.x*v2.x + v1.y*v2.y);
2468
};
2469
N3D.Math.Vector2.Sub = function(v1,v2){
2470
  return new N3D_M_Vector2(v1.x-v2.x,v1.y-v2.y);
2471
};
2472
N3D.Math.Vector2.Distance = function(v1,v2){
2473
  var x = v1.x-v2.x, y = v1.y-v2.y;
2474
  return Math.sqrt(x*x+y*y);
2475
};
2476
N3D.Math.Vector2.Cross = function(v1,v2){
2477
  return new N3D_M_Vector2(
2478
    v1.x*v2.y - v1.y*v2.x,
2479
    v2.x*v1.y - v2.y*v1.x
2480
  );
2481
};
2482
2483
N3D.Math.Vector2.Lerp = function(v1,v2,a){
2484
  return new N3D_M_Vector2(
2485
    v1.x + (v2.x-v1.x) * a,
2486
    v1.y + (v2.y-v1.y) * a,
2487
    v1.z + (v2.z-v1.z) * a
2488
  );
2489
};
2490
2491
/* <<<< Math.Vector2 <<<< */
2492
2493
/* >>>> Math.Vector3 >>>> */
2494
N3D.Math.Vector3 = function(x,y,z){
2495
  this.x = x;
2496
  this.y = y;
2497
  this.z = z;
2498
  
2499
  return this;
2500
};
2501
N3D.Math.Vector3.prototype = {
2502
  constructor:N3D.Math.Vector3,
2503
  clone:function(){
2504
    return new N3D_M_Vector3(this.x,this.y,this.z);
2505
  },
2506
  xyz:function(){
2507
    return [this.x,this.y,this.z];
2508
  },
2509
  add:function(v){
2510
    this.x += v.x;
2511
    this.y += v.y;
2512
    this.z += v.z;
2513
    
2514
    return this;
2515
  },
2516
  sub:function(v){
2517
    this.x -= v.x;
2518
    this.y -= v.y;
2519
    this.z -= v.z;
2520
    
2521
    return this;
2522
  },
2523
  multiply:function(v){
2524
    this.x *= v.x;
2525
    this.y *= v.y;
2526
    this.z *= v.z;
2527
    
2528
    return this;
2529
  },
2530
  scale:function(n){
2531
    this.x *= n;
2532
    this.y *= n;
2533
    this.z *= n;
2534
    
2535
    return this;
2536
  },
2537
  cross:function(v){
2538
    var x = this.x,y = this.y,z = this.z;
2539
    
2540
    this.x = y*v.z - z*v.y;
2541
    this.y = z*v.x - x*v.z;
2542
    this.z = x*v.y - y*v.x;
2543
    
2544
    return this;
2545
  },
2546
  dot:function(){
2547
    var x = this.x,y = this.y,z = this.z;
2548
    return (x*x + y*y + z*z);
2549
  },
2550
  length:function(){
2551
    var x = this.x,y = this.y,z = this.z;
2552
    return Math.sqrt(x*x + y*y + z*z);
2553
  },
2554
  normalize:function(){
2555
    var x = this.x,y = this.y,z = this.z;
2556
    var length = 1/Math.sqrt(x*x + y*y + z*z);
2557
    
2558
    this.x *= length;
2559
    this.y *= length;
2560
    this.z *= length;
2561
    
2562
    return this; 
2563
  },
2564
  negative:function(){
2565
    this.x *= -1;
2566
    this.y *= -1;
2567
    this.z *= -1;
2568
    return this;
2569
  },
2570
  rotateY:function(angle){
2571
    var x = this.x, z = this.z;
2572
    
2573
    var c = Math.cos(angle),
2574
        s = Math.sin(angle);
2575
    
2576
    this.x = z*s + x*c;
2577
    this.z = z*c - x*s;    
2578
    
2579
    return this;
2580
  },
2581
  rounded:function(){
2582
    this.x = ~~this.x;
2583
    this.y = ~~this.y;
2584
    this.z = ~~this.z;
2585
    
2586
    return this;
2587
  },
2588
  toRotationMatrix:function(r){
2589
    var c = Math.cos(r), s = Math.sin(r);
2590
    
2591
    var x = this.x, y = this.y, z = this.z, t = 1-c,
2592
        xyt = x*y*t, xzt = x*z*t, yzt = y*z*t,
2593
        xs = x*s, ys = y*s, zs = z*s;
2594
2595
    return new N3D_M_Matrix4(
2596
      c+x*x*t, xyt-zs,  xzt+ys,  0,
2597
      xyt+zs,  c+y*y*t, yzt-xs,  0,
2598
      xzt-ys,  yzt+xs,  c+z*z*t, 0,
2599
      0,       0,       0,       1
2600
    );
2601
  },
2602
  toVector4:function(n){
2603
    return new N3D_M_Vector4(this.x,this.y,this.z,n);
2604
  },
2605
  toString:function(){
2606
    return "N3D.Math.Vector3("+this.x+","+this.y+","+this.z+")";
2607
  }
2608
};
2609
N3D.Math.Vector3.Perp = function(a,axis){
2610
  var x = a.x, y = a.y, z = a.z;
2611
  var par = N3D_M_Vector3.Parallel(a,axis);
2612
  return new N3D_M_Vector3(
2613
    x-par.x,
2614
    y-par.y,
2615
    z-par.z  
2616
  );
2617
};
2618
N3D.Math.Vector3.Parallel = function(a,axis){
2619
  var dot = N3D_M_Vector3.Dot(a,axis);
2620
  var p = axis.clone();
2621
  
2622
  return new N3D_M_Vector3(
2623
    axis.x*dot, axis.y*dot, axis.z*dot
2624
  ); 
2625
};
2626
N3D.Math.Vector3.Identity = function(){
2627
  return new N3D_M_Vector3(0,0,0);
2628
};
2629
N3D.Math.Vector3.Up = new N3D.Math.Vector3(0,1,0);
2630
N3D.Math.Vector3.Right = new N3D.Math.Vector3(1,0,0);
2631
N3D.Math.Vector3.Forward = new N3D.Math.Vector3(0,0,-1);
2632
2633
N3D.Math.Vector3.Lerp = function(v1,v2,a){
2634
  var Lerp = N3D_M.Lerp;
2635
2636
  return new N3D_M_Vector3(
2637
    Lerp(v1.x, v2.x, a),
2638
    Lerp(v1.y, v2.y, a),
2639
    Lerp(v1.z, v2.z, a)
2640
  );
2641
};
2642
N3D.Math.Vector3.Max = function(v1,v2){
2643
  return newN3D_M_Vector3(
2644
    N3D_M.Max(v1.x, v2.x),
2645
    N3D_M.Max(v1.y, v2.y),
2646
    N3D_M.Max(v1.z, v2.z)
2647
  );
2648
};
2649
N3D.Math.Vector3.Min = function(v1,v2){
2650
  return new N3D_M_Vector3(
2651
    N3D_M.Min(v1.x, v2.x),
2652
    N3D_M.Min(v1.y, v2.y),
2653
    N3D_M.Min(v1.z, v2.z)
2654
  );
2655
};
2656
N3D.Math.Vector3.Herminte = function(v1,t1,v2,t2,a){
2657
  return new N3D_M_Vector3(
2658
    N3D_M.Hermite(v1.x, t1.x, v2.x, t2.x, a),
2659
    N3D_M.Hermite(v1.y, t1.y, v2.y, t2.y, a),
2660
    N3D_M.Hermite(v1.z, t1.z, v2.z, t2.z, a)
2661
  );   
2662
};
2663
N3D.Math.Vector3.isZero = function(v){
2664
  return (v.x == 0 && v.y == 0 && v.z==0);
2665
};
2666
N3D.Math.Vector3.Equals = function(v){
2667
  return v instanceof N3D_M_Vector3;
2668
};
2669
N3D.Math.Vector3.DistanceSquared = function(v1,v2){
2670
  return (v1.x-v2.x) * (v1.x-v2.x) + (v1.y-v2.y) * (v1.y-v2.y) + (v1.z-v2.z) * (v1.z-v2.z); 
2671
};
2672
N3D.Math.Vector3.Distance = function(v1,v2){
2673
  return Math.sqrt((v1.x-v2.x) * (v1.x-v2.x) + (v1.y-v2.y) * (v1.y-v2.y) + (v1.z-v2.z) * (v1.z-v2.z));
2674
};
2675
N3D.Math.Vector3.Cross = function(v1, v2){
2676
  return new N3D_M_Vector3(
2677
    v1.y * v2.z - v1.z * v2.y,
2678
    v1.z * v2.x - v1.x * v2.z,
2679
    v1.x * v2.y - v1.y * v2.x  
2680
  );
2681
};
2682
N3D.Math.Vector3.BaryCentric = function(v1,v2,v3,a1,a2,r){
2683
  return new N3D_M_Vector3(
2684
    N3D_M.Barycentric(v1.x, v2.x, v3.x, a1, a2),
2685
    N3D_M.Barycentric(v1.y, v2.y, v3.y, a1, a2),
2686
    N3D_M.Barycentric(v1.z, v2.z, v3.z, a1, a2)
2687
  );
2688
};
2689
2690
N3D.Math.Vector3.CatmullRom = function(v1,v2,v3,v4,a,r){
2691
  return new N3D_M_Vector3(
2692
    N3D_M.CatmullRom(v1.x, v2.x, v3.x, v4.x, a),
2693
    N3D_M.CatmullRom(v1.y, v2.y, v3.y, v4.y, a),
2694
    N3D_M.CatmullRom(v1.z, v2.z, v3.z, v4.z, a)
2695
  );
2696
};
2697
2698
N3D.Math.Vector3.Clamp = function(v1, min, max){
2699
  return new N3D_M_Vector3(
2700
    N3D_M.Clamp(v1.x, min.x, max.x),
2701
    N3D_M.Clamp(v1.y, min.y, max.y),
2702
    N3D_M.Clamp(v1.z, min.z, max.z)
2703
  );
2704
};
2705
N3D.Math.Vector3.Dot = function(v1, v2){
2706
  return (v1.x*v2.x + v1.y*v2.y + v1.z * v2.z);
2707
};
2708
N3D.Math.Vector3.Reflect = function(v,n){
2709
  var dT = 2 * N3D_M_Vector3.Dot(v,n);
2710
  return new N3D_M_Vector3(
2711
    v.x - dT * n.x,
2712
    v.y - dT * n.y,
2713
    v.z - dT * n.z
2714
  );
2715
};
2716
N3D.Math.Vector3.Add = function(v0,v1){
2717
  return new N3D_M_Vector3(
2718
    v0.x+v1.x,
2719
    v0.y+v1.y,
2720
    v0.z+v1.z  
2721
  );
2722
};
2723
2724
N3D.Math.Vector3.Sub = function(v0,v1){
2725
  return new N3D_M_Vector3(
2726
    v0.x-v1.x,
2727
    v0.y-v1.y,
2728
    v0.z-v1.z  
2729
  );
2730
};
2731
N3D.Math.Vector3.MultiplyScalar = function(v0,n){
2732
  return new N3D_M_Vector3(
2733
    v0.x*n,
2734
    v0.y*n,
2735
    v0.z*n  
2736
  );
2737
};
2738
2739
N3D.Math.Vector3.SmoothStep = function(v1,v2,a){
2740
  return new N3D_M_Vector3(
2741
    N3D_M.SmoothStep(v1.x, v2.x, a),
2742
    N3D_M.SmoothStep(v1.y, v2.y, a),
2743
    N3D_M.SmoothStep(v1.z, v2.z, a)
2744
  );
2745
};
2746
2747
N3D_M_Vector3 = N3D.Math.Vector3;
2748
/* <<<< Math.Vector3 <<<< */
2749
2750
/* >>>> Math.Vector4 >>>> */
2751
N3D.isLoaded = true;
2752
2753
N3D.Math.Vector4 = function(x,y,z,w){
2754
  this.x = x;
2755
  this.y = y;
2756
  this.z = z;
2757
  this.w = w;
2758
  
2759
  return this;
2760
};
2761
N3D.Math.Vector4.prototype = {
2762
  constructor:N3D.Math.Vector4,
2763
  clone:function(){
2764
    return new N3D.Math.Vector4(this.x,this.y,this.z,this.w);
2765
  },
2766
  xyz:function(){
2767
    return [this.x,this.y,this.z];
2768
  },
2769
  xyzw:function(){
2770
    return [this.x,this.y,this.z,this.w];
2771
  },
2772
  add:function(v){
2773
    this.x += v.x;
2774
    this.y += v.y;
2775
    this.z += v.z;
2776
    this.w += v.w;
2777
    
2778
    return this;
2779
  },
2780
  sub:function(v){
2781
    this.x -= v.x;
2782
    this.y -= v.y;
2783
    this.z -= v.z;
2784
    this.w -= v.w;
2785
    
2786
    return this;
2787
  },
2788
  multiply:function(v){
2789
    this.x *= v.x;
2790
    this.y *= v.y;
2791
    this.z *= v.z;
2792
    this.w *= v.w;
2793
    
2794
    return this;
2795
  },
2796
  scale:function(n){
2797
    this.x *= n;
2798
    this.y *= n;
2799
    this.z *= n;
2800
    this.w *= n;
2801
    
2802
    return this;
2803
  },
2804
  divide:function(v){
2805
    this.x /= v.x;
2806
    this.y /= v.y;
2807
    this.z /= v.z;
2808
    this.w /= v.w;
2809
    
2810
    return this;
2811
  },
2812
  divideScalar:function(n){
2813
    return this.multiplyScalar(1/n);
2814
  },
2815
  dot:function(v){
2816
    return (this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w);
2817
  },
2818
  normalize:function(){
2819
    var x = this.x, y = this.y, z = this.z, w = this.w;
2820
    var f = 1/Math.sqrt(x*x+y*y+z*z+w*w);
2821
    this.x *= f;
2822
    this.y *= f;
2823
    this.z *= f;
2824
    this.w *= f;
2825
    
2826
    return this;
2827
  },
2828
  length:function(){
2829
    var x = this.x, y = this.y, z = this.z, w = this.w;
2830
    return Math.sqrt(x*x+y*y+z*z+w*w);
2831
  },
2832
  multiplyMatrix4:function(m){
2833
    var m = m.elements;
2834
    var x = this.x, y = this.y, z = this.z,w = this.w;
2835
    
2836
    this.x = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
2837
    this.y = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
2838
    this.z = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
2839
    this.w = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
2840
   
2841
    return this; 
2842
  },
2843
  copyFromVector4:function(o){
2844
    this.x = o.x;
2845
    this.y = o.y;
2846
    this.z = o.z;
2847
    this.w = o.w;
2848
    
2849
    return this;
2850
  },
2851
  toHomogenous: function(width,height){
2852
    var invW = 1/this.w;
2853
    
2854
    var x = this.x*invW,
2855
        y = this.y*invW,
2856
        z = this.z*invW;
2857
2858
    if(-1 < x && x < 1 && -1 < y && y < 1 && -1 < z && z < 1){
2859
      this.x = ~~((x+1)*(width*0.5)); 
2860
      this.y = ~~((y+1)*(height*0.5));
2861
      this.z = z; 
2862
      this.draw = true;
2863
      return this;  
2864
    }
2865
  
2866
    this.draw = false;
2867
    
2868
    return false;
2869
  },
2870
  toString:function(){
2871
    return "Vector4("+this.x+","+this.y+","+this.z+","+this.w+")";
2872
  }
2873
};
2874
N3D.Math.Vector4.Identity = function(){
2875
  return new this(0,0,0,1);
2876
};
2877
N3D.Math.Vector4.CreateFromVector3 = function(v,n){
2878
  return new this(v.x,v.y,v.z,n);
2879
};
2880
2881
N3D.Math.Vector4.Lerp = function(v1,v2,a){
2882
  var Lerp = N3D_M.Lerp;
2883
2884
  return new $V4(
2885
    Lerp(v1.x, v2.x, a),
2886
    Lerp(v1.y, v2.y, a),
2887
    Lerp(v1.z, v2.z, a),
2888
    Lerp(v1.w, v2.w, a)
2889
  );
2890
};
2891
2892
N3D.Math.Vector4.Multiply = function(v1,v2){
2893
  return new this(
2894
    v1.x*v2.x,
2895
    v1.y*v2.y,
2896
    v1.z*v2.z,
2897
    v1.w*v2.w
2898
  );
2899
};
2900
2901
N3D.Math.Vector4.Equals = function(v){
2902
  return v instanceof this;
2903
};
2904
N3D.Math.Vector4.Add = function(v1,v2){
2905
  return new this(
2906
    v1.x+v2.x,
2907
    v1.y+v2.y,
2908
    v1.z+v2.z,
2909
    v1.w+v2.w
2910
  );
2911
};
2912
N3D.Math.Vector4.Sub = function(v1,v2){
2913
  return new this(
2914
    v1.x-v2.x,
2915
    v1.y-v2.y,
2916
    v1.z-v2.z,
2917
    v1.w-v2.w  
2918
  );
2919
};
2920
2921
N3D.Math.Vector4.Projection = function(p,viewport){
2922
  var viewport = viewport || $Game.viewport;
2923
  p.divideNumber(p.w);
2924
  var w = 1;
2925
  
2926
  if(-w <= p.x <= w && -w <= p.y <= w && -w <= p.z <= w){
2927
    var x = (p.x+1)*(viewport.width*0.5); 
2928
    var y = (p.y+1)*(viewport.height*0.5);
2929
    var v = new $V2(~~x,~~y);
2930
    v.z = p.z;
2931
    return v;  
2932
  }
2933
  
2934
  return false;
2935
}; 
2936
/* <<<< Math.Vector4 <<<< */
2937
2938
/* >>>> Math.Quaternion >>>> */
2939
N3D.Math.Quaternion = function(x,y,z,w){
2940
  this.x = x;
2941
  this.y = y;
2942
  this.z = z;
2943
  this.w = w;
2944
};
2945
N3D.Math.Quaternion.prototype = {
2946
  conjugate:function(){
2947
    this.x *= -1;
2948
    this.y *= -1;
2949
    this.z *= -1;
2950
    
2951
    return this;
2952
  },
2953
  scale:function(n){
2954
    this.x *= n;
2955
    this.y *= n;
2956
    this.z *= n;
2957
    this.w *= n;
2958
  },
2959
  multiply:function(q){
2960
    var q1x = this.x, q1y = this.y, q1z = this.z, q1w = this.w;
2961
    var q2x = q.x, q2y = q.y, q2z = q.z, q2w = q.w;
2962
    
2963
    this.x =  q1x * q2w + q1y * q2z - q1z * q2y + q1w * q2x;
2964
    this.y = -q1x * q2z + q1y * q2w + q1z * q2x + q1w * q2y;
2965
    this.z =  q1x * q2y - q1y * q2x + q1z * q2w + q1w * q2z;
2966
    this.w = -q1x * q2x - q1y * q2y - q1z * q2z + q1w * q2w;
2967
    
2968
    return this;
2969
  },
2970
  add:function(q){
2971
    this.x += q.x;
2972
    this.y += q.y;
2973
    this.z += q.z;
2974
    this.w += q.w;
2975
    
2976
    return this;
2977
  },
2978
  toMatrix4:function(){
2979
    var x = this.x, y = this.y, z = this.z, w = this.w;
2980
    
2981
    var xx = x*x, xy = x*y, xz = x*z, xw = x*w;
2982
    var yy = y*y, yz = y*z, yw = y*w;
2983
    var zz = z*z, zw = z*w;
2984
2985
    
2986
    return new N3D_M_Matrix4(
2987
      1 - 2*yy - 2*zz,  2*xy - 2*zw,      2*xz + 2*yw,      0,
2988
      2*xy + 2*zw,      1 - 2*xx - 2*zz,  2*yz - 2*xw,      0,
2989
      2*xz - 2*yw,      2*yz + 2*xw,      1 - 2*xx - 2*yy,  0,
2990
      0,                0,                0,                1
2991
    );
2992
  },
2993
  identity:function(){
2994
    this.x = 0;
2995
    this.y = 0;
2996
    this.z = 0;
2997
    this.w = 1;
2998
    
2999
    return this;
3000
  },
3001
  toTransposedMatrix4:function(){
3002
    var x = this.x, y = this.y, z = this.z, w = this.w;
3003
    
3004
    var xx = x*x, xy = x*y, xz = x*z, xw = x*w;
3005
    var yy = y*y, yz = y*z, yw = y*w;
3006
    var zz = z*z, zw = z*w;
3007
    
3008
    return new N3D_M_Matrix4(
3009
      1 - 2*yy - 2*zz,  2*xy + 2*zw,      2*xz - 2*yw,        0,
3010
      2*xy - 2*zw,      1 - 2*xx - 2*zz,  2*yz + 2*xw,        0,
3011
      2*xz + 2*yw,      2*yz - 2*xw,      1 - 2*xx - 2*yy,    0,
3012
      0,                0,                0,                  1 
3013
    );
3014
  },
3015
  toAngleAxis:function(){
3016
    var x = this.x, y = this.y, z = this.z, w = this.w;
3017
    var scale = Math.sqrt(x*x + y*y + z*z);
3018
3019
    if (scale == 0 || w > 1 || w < -1){
3020
      return {
3021
        angle:0,
3022
        axis:new N3D_M_Vector3(
3023
          0,1,0
3024
        )
3025
      };
3026
    }
3027
    
3028
    var invscale = 1/scale;
3029
3030
    return {
3031
      angle:2 * Math.acos(w),
3032
      axis:new N3D_M_Vector3(
3033
        x * invscale,
3034
        y * invscale,
3035
        z * invscale
3036
      )
3037
    };
3038
  },
3039
  toAngles:function(){
3040
    var x = this.x, y = this.y, z = this.z;
3041
    
3042
    return new N3D_M_Vector3(
3043
      Math.atan(2*(x*y + z*w)/(1-2*(y*y+z*z))),
3044
      Math.asin(2*(x*z - w*y)),
3045
      Math.atan(2*(x*w +y*z)/(1-2*(z*z + w*w)))
3046
    );
3047
  },
3048
  toString:function(){
3049
    return "Quaternion("+this.x+","+this.y+","+this.z+","+this.w+")";
3050
  }
3051
};
3052
3053
N3D.Math.Quaternion.prototype.dot = N3D.Math.Vector4.prototype.dot;
3054
N3D.Math.Quaternion.Equals = N3D.Math.Vector4.Equals;
3055
N3D.Math.Quaternion.prototype.inverse = N3D.Math.Quaternion.prototype.conjugate;
3056
N3D.Math.Quaternion.prototype.normalize = N3D.Math.Vector4.prototype.normalize;
3057
N3D.Math.Quaternion.CreateFromAngles = function(x,y,z){
3058
  x *= 0.5, y *= 0.5, z *= 0.5;
3059
  var cos_x_2 = Math.cos(x), sin_x_2 = Math.sin(x),
3060
      cos_y_2 = Math.cos(y), sin_y_2 = Math.sin(y),
3061
      cos_z_2 = Math.cos(z), sin_z_2 = Math.sin(z);
3062
  
3063
  return new N3D_M_Quaternion(
3064
    cos_z_2*cos_y_2*sin_x_2 - sin_z_2*sin_y_2*cos_x_2,
3065
    cos_z_2*sin_y_2*cos_x_2 + sin_z_2*cos_y_2*sin_x_2,
3066
    sin_z_2*cos_y_2*cos_x_2 - cos_z_2*sin_y_2*sin_x_2,
3067
    cos_z_2*cos_y_2*cos_x_2 + sin_z_2*sin_y_2*sin_x_2
3068
  );
3069
};
3070
N3D.Math.Quaternion.Lerp = function(q1,q2,time){
3071
  var scale = 1 - time;
3072
3073
  return new N3D_M_Quaternion(
3074
    q1.x*scale + q2.x*time,
3075
    q1.y*scale + q2.y*time,
3076
    q1.z*scale + q2.z*time,
3077
    q1.w*scale + q2.w*time
3078
  );
3079
};
3080
N3D.Math.Quaternion.Dot = function(q1,q2){
3081
  var x1 = q1.x, y1 = q1.y, z1 = q1.z, w1 = q1.w;
3082
  var x2 = q2.x, y2 = q2.y, z2 = q2.z, w2 = q2.w;
3083
  
3084
  return x1*x2 + y1*y2 + z1*z2 + w1*w2; 
3085
};
3086
N3D.Math.Quaternion.Slerp = function(q1,q2,time,threshold){
3087
  var angle = q1.dot(q2);
3088
3089
  // make sure we use the short rotation
3090
  if (angle < 0){
3091
    q1.scale(-1);
3092
    angle *= -1;
3093
  }
3094
  
3095
  if (angle <= (1-threshold)){ // spherical interpolation
3096
      var theta = Math.acos(angle);
3097
      var invsintheta = 1/Math.sin(theta);
3098
      var scale = Math.sin(theta * (1-time)) * invsintheta;
3099
      var invscale = Math.sin(theta * time) * invsintheta;
3100
      
3101
      
3102
      return new N3D_M_Quaternion(
3103
        q1.x*scale + q2.x*invscale,
3104
        q1.y*scale + q2.y*invscale,
3105
        q1.z*scale + q2.z*invscale,
3106
        q1.w*scale + q2.w*invscale
3107
      );
3108
  }
3109
  // linear interploation
3110
  return N3D_M_Quaternion.Lerp(q1,q2,time);
3111
};
3112
3113
N3D.Math.Quaternion.CreateFromAngles2 = function(x,y,z){
3114
  x *= 0.5, y *= 0.5, z *= 0.5;
3115
  
3116
  var sx = Math.sin(x), cx = Math.cos(x),
3117
      sy = Math.sin(y), cy = Math.cos(y),
3118
      sz = Math.sin(z), cz = Math.cos(z),
3119
      
3120
      cycz = cy * cz, sycz = sy * cz,
3121
      cysz = cy * sz, sysz= sy * sz;
3122
  
3123
  return new N3D_M_Quaternion(
3124
    sx * cycz - cx * sysz,
3125
    cx * sycz + sx * cysz,
3126
    cx * cysz - sx * sycz,
3127
    cx * cycz + sx * sysz
3128
  );
3129
};
3130
3131
N3D.Math.Quaternion.CreateFromAngles3 = function(x,y,z){
3132
  x *= 0.5, y *= 0.5, z *= 0.5;
3133
  
3134
  var c1 = Math.cos(y), s1 = Math.sin(y),
3135
      c2 = Math.cos(z), s2 = Math.sin(z),
3136
      c3 = Math.cos(x), s3 = Math.sin(x),
3137
      c1c2 = c1*c2,     s1s2 = s1*s2;
3138
  
3139
  return new N3D_M_Quaternion(
3140
    c1c2*s3 + s1s2*c3,
3141
      s1*c2*c3 + c1*s2*s3,
3142
    c1*s2*c3 - s1*c2*s3,
3143
    c1c2*c3 - s1s2*s3
3144
  );
3145
};
3146
3147
N3D.Math.Quaternion.CreateFromAngles4 = function(x,y,z){
3148
  x *= 0.5, y *= 0.5, z *= 0.5, w = 0;
3149
  
3150
  var cx = Math.cos(x), sx = Math.sin(x),
3151
      cy = Math.cos(y), sy = Math.sin(y),
3152
      cz = Math.cos(z), sz = Math.sin(z);
3153
      
3154
      
3155
  
3156
3157
  return new N3D_M_Quaternion(
3158
    cz*cx*cy-sz*sx*sy,
3159
    sz*cx*cy+cz*sx*sy,
3160
    cz*sx*cy-sz*cx*sy,
3161
    cz*cx*sy+sz*sx*cy 
3162
  );
3163
};
3164
3165
3166
N3D_M = N3D.Math;
3167
$M4 = N3D_M_Matrix4 = N3D.Math.Matrix4; 
3168
$M3 = N3D_M_Matrix3 = N3D.Math.Matrix3;
3169
$V2 = N3D_M_Vector2 = N3D.Math.Vector2;
3170
$V3 = N3D_M_Vector3 = N3D.Math.Vector3; 
3171
$V4 = N3D_M_Vector4 = N3D.Math.Vector4;
3172
N3D_M_Quaternion = N3D.Math.Quaternion;
3173
</script>
3174
<script>
3175
goog.require('goog.math.Vec3');
3176
goog.require('goog.math.Matrix');
3177
//N3D.require("Math.Vector3");
3178
</script>
3179
<script>
3180
3181
"use strict";
3182
class CVec{
3183
    constructor(x,y,z){ 
3184
        this.x = x; 
3185
        this.y = y; 
3186
        this.z = z; 
3187
    }
3188
3189
    add(v2){
3190
      this.x+=v2.x;
3191
      this.y+=v2.y;
3192
      this.z+=v2.z;
3193
    }
3194
3195
    normalize(){
3196
      let xx = this.x*this.x; 
3197
      let yy = this.y*this.y;
3198
      let zz = this.z*this.z;
3199
      let invnorm = 1.0/Math.sqrt(xx+yy+zz);
3200
      this.x*=invnorm;
3201
      this.y*=invnorm;
3202
      this.z*=invnorm;
3203
    }
3204
    cross(v2){
3205
      let ax = this.x, ay = this.y, az = this.z;
3206
      let bx = v2.x, by = v2.y, bz = v2.z;
3207
      this.x = ay * bz - az * by;
3208
      this.y = az * bx - ax * bz;
3209
      this.z = ax * by - ay * bx;
3210
    }
3211
3212
}
3213
</script>
Script Preparation code:
 
var mArrData = [0, 0, 0, 0, 0, 0, 0, 0, 0];
var m1ArrData = [0, 0, 0, 0, 0, 0, 0, 0, 0];
var mData = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
var m1Data = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
for(var i = 0; i < 9; i++) {
    mArrData[i] = Math.random();
    m1ArrData[i] = Math.random();
}
for(var i = 0; i < 3; i++) {
    for(var j = 0; j < 3; j++) {
        mData[i][j] = mArrData[i + j];
        m1Data[i][j] = m1ArrData[i + j];
    }
}
Tests:
  • Vanilla

     
    let v1 = [1, 2, 3];
    let v2 = [4, 5, 6];
    // add
    v1[0] += v2[0];
    v1[1] += v2[1];
    v1[2] += v2[2];
    // normalize
    let invLength = 1 / Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]);
    v1[0] *= invLength;
    v1[1] *= invLength;
    v1[2] *= invLength;
    // cross
    let ax = v1[0], ay = v1[1], az = v1[2];
    let bx = v2[0], by = v2[1], bz = v2[2];
    v1[0] = ay * bz - az * by;
    v1[1] = az * bx - ax * bz;
    v1[2] = ax * by - ay * bx;
    floatArray[index] = floatArray[index] + v1[0] - v1[0] + v1[0];
    index = (index + 1) % defaultCount;
  • glmatrix

     
    var v1 = vec3.fromValues(1,2,3);
    var v2 = vec3.fromValues(4,5,6);
    vec3.add(v1, v2, v1);
    vec3.normalize(v1, v1);
    vec3.cross(v1, v2, v1);
    floatArray[index] = floatArray[index] + v1[0] - v1[0] + v1[0];
    index = (index + 1) % defaultCount;
  • ES6 class

     
    var v1 = new CVec(1,2,3);
    var v2 = new CVec(4,5,6);
    v1.add(v2);
    v1.normalize();
    v1.cross(v2);
    floatArray[index] = floatArray[index] + v1.x - v1.y + v1.z;
    index = (index + 1) % defaultCount;
  • No Array

     
    let v1x = 1, v1y = 2, v1z = 3;
    let v2x = 4, v2y = 5, v2z = 6;
    // add
    v1x += v2x;
    v1y += v2y;
    v1z += v2z;
    // normalize
    let invLength = 1 / Math.sqrt(v1x * v1x + v1y * v1y + v1z * v1z);
    v1x *= invLength;
    v1y *= invLength;
    v1z *= invLength;
    // cross
    let ax = v1x, ay = v1y, az = v1z;
    let bx = v2x, by = v2y, bz = v2z;
    v1x = ay * bz - az * by;
    v1y = az * bx - ax * bz;
    v1z = ax * by - ay * bx;
    floatArray[index] = floatArray[index] + v1x - v1y + v1z;
    index = (index + 1) % defaultCount;
  • N3D

     
    var v1 = new $V3(1,2,3);
    var v2 = new $V3(4,5,6);
    v1.add(v2);
    v1.normalize();
    v1.cross(v2);
    floatArray[index] = floatArray[index] + v1.x - v1.y + v1.z;
    index = (index + 1) % defaultCount;
  • Closure

     
    var v1 = new goog.math.Vec3(1,2,3);
    var v2 = new goog.math.Vec3(4,5,6);
    v1.add(v2);
    v1.normalize();
    v1 = goog.math.Vec3.cross(v2, v1);
    floatArray[index] = floatArray[index] + v1.x - v1.y + v1.z;
    index = (index + 1) % defaultCount;
Rendered benchmark preparation results:

Suite status: <idle, ready to run>

Previous results

Experimental features:

  • Test case name Result
    Vanilla
    glmatrix
    ES6 class
    No Array
    N3D
    Closure

    Fastest: N/A

    Slowest: N/A

Latest run results:
Run details: (Test run date: 4 years ago)
Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:85.0) Gecko/20100101 Firefox/85.0
Firefox 85 on Ubuntu
View result in a separate tab
Test name Executions per second
Vanilla 5669577.0 Ops/sec
glmatrix 3535536.8 Ops/sec
ES6 class 4291720.0 Ops/sec
No Array 12171708.0 Ops/sec
N3D 4464935.0 Ops/sec
Closure 3058124.5 Ops/sec