sylvester-es6
Version:
Fork of the famous Sylvester vector, matrix and geometry library. Rewritten in ES6 and including the glUtils.js add-ons.
413 lines (376 loc) • 9.37 kB
JavaScript
"use strict";
import { Matrix } from "./Matrix";
import { PRECISION } from "./PRECISION";
export class Vector
{
constructor (elements)
{
this.setElements(elements);
}
e (i)
{
return (i < 1 || i > this.elements.length) ? null : this.elements[i-1];
}
dimensions ()
{
return this.elements.length;
}
modulus ()
{
return Math.sqrt(this.dot(this));
}
eql (vector)
{
var n = this.elements.length;
var V = vector.elements || vector;
if (n !== V.length)
{
return false;
}
while (n--)
{
if (Math.abs(this.elements[n] - V[n]) > PRECISION)
{
return false;
}
}
return true;
}
dup () {
return new Vector(this.elements);
}
map (fn, context)
{
var elements = [];
this.each(function(x, i)
{
elements.push(fn.call(context, x, i));
});
return new Vector(elements);
}
forEach (fn, context)
{
var n = this.elements.length;
for (var i = 0; i < n; i++)
{
fn.call(context, this.elements[i], i+1);
}
}
toUnitVector ()
{
var r = this.modulus();
if (r === 0)
{
return this.dup();
}
return this.map(function(x)
{
return x/r;
});
}
angleFrom (vector)
{
var V = vector.elements || vector;
var n = this.elements.length, k = n, i;
if (n !== V.length)
{
return null;
}
var dot = 0, mod1 = 0, mod2 = 0;
// Work things out in parallel to save time
this.each(function(x, i)
{
dot += x * V[i-1];
mod1 += x * x;
mod2 += V[i-1] * V[i-1];
});
mod1 = Math.sqrt(mod1); mod2 = Math.sqrt(mod2);
if (mod1*mod2 === 0)
{
return null;
}
var theta = dot / (mod1*mod2);
if (theta < -1)
{
theta = -1;
}
if (theta > 1)
{
theta = 1;
}
return Math.acos(theta);
}
isParallelTo (vector)
{
var angle = this.angleFrom(vector);
return (angle === null) ? null : (angle <= PRECISION);
}
isAntiparallelTo (vector)
{
var angle = this.angleFrom(vector);
return (angle === null) ? null : (Math.abs(angle - Math.PI) <= PRECISION);
}
isPerpendicularTo (vector)
{
var dot = this.dot(vector);
return (dot === null) ? null : (Math.abs(dot) <= PRECISION);
}
add (vector)
{
var V = vector.elements || vector;
if (this.elements.length !== V.length)
{
return null;
}
return this.map(function(x, i) { return x + V[i-1]; });
}
subtract (vector)
{
var V = vector.elements || vector;
if (this.elements.length !== V.length)
{
return null;
}
return this.map(function(x, i)
{
return x - V[i-1];
});
}
multiply (k)
{
return this.map(function(x)
{
return x*k;
});
}
dot (vector)
{
var V = vector.elements || vector;
var i, product = 0, n = this.elements.length;
if (n !== V.length)
{
return null;
}
while (n--)
{
product += this.elements[n] * V[n];
}
return product;
}
cross (vector)
{
var B = vector.elements || vector;
if (this.elements.length !== 3 || B.length !== 3)
{
return null;
}
var A = this.elements;
return new Vector([
(A[1] * B[2]) - (A[2] * B[1]),
(A[2] * B[0]) - (A[0] * B[2]),
(A[0] * B[1]) - (A[1] * B[0])
]);
}
max ()
{
var m = 0, i = this.elements.length;
while (i--)
{
if (Math.abs(this.elements[i]) > Math.abs(m))
{
m = this.elements[i];
}
}
return m;
}
indexOf (x)
{
var index = null, n = this.elements.length;
for (var i = 0; i < n; i++)
{
if (index === null && this.elements[i] === x)
{
index = i + 1;
}
}
return index;
}
toDiagonalMatrix ()
{
return Matrix.Diagonal(this.elements);
}
round ()
{
return this.map(function(x)
{
return Math.round(x);
});
}
snapTo (x)
{
return this.map(function(y)
{
return (Math.abs(y - x) <= PRECISION) ? x : y;
});
}
distanceFrom (obj)
{
if (obj.anchor || (obj.start && obj.end))
{
return obj.distanceFrom(this);
}
var V = obj.elements || obj;
if (V.length !== this.elements.length)
{
return null;
}
var sum = 0, part;
this.each(function(x, i)
{
part = x - V[i-1];
sum += part * part;
});
return Math.sqrt(sum);
}
liesOn (line)
{
return line.contains(this);
}
liesIn (plane)
{
return plane.contains(this);
}
rotate (t, obj)
{
var V, R = null, x, y, z;
if (t.determinant)
{
R = t.elements;
}
switch (this.elements.length)
{
case 2:
{
V = obj.elements || obj;
if (V.length !== 2)
{
return null;
}
if (!R)
{
R = Matrix.Rotation(t).elements;
}
x = this.elements[0] - V[0];
y = this.elements[1] - V[1];
return new Vector([
V[0] + R[0][0] * x + R[0][1] * y,
V[1] + R[1][0] * x + R[1][1] * y
]);
break;
}
case 3:
{
if (!obj.direction)
{
return null;
}
var C = obj.pointClosestTo(this).elements;
if (!R)
{
R = Matrix.Rotation(t, obj.direction).elements;
}
x = this.elements[0] - C[0];
y = this.elements[1] - C[1];
z = this.elements[2] - C[2];
return new Vector([
C[0] + R[0][0] * x + R[0][1] * y + R[0][2] * z,
C[1] + R[1][0] * x + R[1][1] * y + R[1][2] * z,
C[2] + R[2][0] * x + R[2][1] * y + R[2][2] * z
]);
break;
}
default:
{
return null;
}
}
}
reflectionIn (obj)
{
if (obj.anchor)
{
// obj is a plane or line
var P = this.elements.slice();
var C = obj.pointClosestTo(P).elements;
return new Vector([C[0] + (C[0] - P[0]), C[1] + (C[1] - P[1]), C[2] + (C[2] - (P[2] || 0))]);
}
else
{
// obj is a point
var Q = obj.elements || obj;
if (this.elements.length !== Q.length)
{
return null;
}
return this.map(function(x, i) { return Q[i-1] + (Q[i-1] - x); });
}
}
to3D ()
{
var V = this.dup();
switch (V.elements.length)
{
case 3:
{
break;
}
case 2:
{
V.elements.push(0);
break;
}
default:
{
return null;
}
}
return V;
}
inspect ()
{
return '[' + this.elements.join(', ') + ']';
}
setElements (els)
{
this.elements = (els.elements || els).slice();
return this;
}
//From glUtils.js
flatten ()
{
return this.elements;
}
}
Vector.Random = function(n)
{
var elements = [];
while (n--)
{
elements.push(Math.random());
}
return new Vector(elements);
};
Vector.Zero = function(n)
{
var elements = [];
while (n--)
{
elements.push(0);
}
return new Vector(elements);
};
Vector.prototype.x = Vector.prototype.multiply;
Vector.prototype.each = Vector.prototype.forEach;
Vector.i = new Vector([1,0,0]);
Vector.j = new Vector([0,1,0]);
Vector.k = new Vector([0,0,1]);