fugafacere
Version:
A pure-JS implementation of the W3C's Canvas-2D Context API that can run on top of either Expo Graphics or a browser WebGL context.
205 lines (200 loc) • 5.79 kB
JavaScript
// Retrieved on 04/26/18 from:
// https://github.com/evanw/lightgl.js/blob/master/src/vector.js
// Last modified by Leo Alterman on 04/26/18
//
// Copyright (C) 2011 by Evan Wallace
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Provides a simple 3D vector class. Vector operations can be done using member
// functions, which return new vectors, or static functions, which reuse
// existing vectors to avoid generating garbage.
//
// TODO(Leo) - Add op_() versions of each method for in-place modification
export default function Vector(x, y, z) {
this.x = x || 0;
this.y = y || 0;
this.z = z || 0;
}
// ### Instance Methods
// The methods `add()`, `subtract()`, `multiply()`, and `divide()` can all
// take either a vector or a number as an argument.
Vector.prototype = {
negative() {
return new Vector(-this.x, -this.y, -this.z);
},
add(v) {
if (v instanceof Vector) return new Vector(this.x + v.x, this.y + v.y, this.z + v.z);
else return new Vector(this.x + v, this.y + v, this.z + v);
},
subtract(v) {
if (v instanceof Vector) return new Vector(this.x - v.x, this.y - v.y, this.z - v.z);
else return new Vector(this.x - v, this.y - v, this.z - v);
},
multiply(v) {
if (v instanceof Vector) return new Vector(this.x * v.x, this.y * v.y, this.z * v.z);
else return new Vector(this.x * v, this.y * v, this.z * v);
},
divide(v) {
if (v instanceof Vector) return new Vector(this.x / v.x, this.y / v.y, this.z / v.z);
else return new Vector(this.x / v, this.y / v, this.z / v);
},
equals(v) {
return this.x === v.x && this.y === v.y && this.z === v.z;
},
dot(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
},
cross(v) {
return new Vector(
this.y * v.z - this.z * v.y,
this.z * v.x - this.x * v.z,
this.x * v.y - this.y * v.x
);
},
length() {
return Math.sqrt(this.dot(this));
},
unit() {
return this.divide(this.length());
},
min() {
return Math.min(Math.min(this.x, this.y), this.z);
},
max() {
return Math.max(Math.max(this.x, this.y), this.z);
},
toAngles() {
return {
theta: Math.atan2(this.z, this.x),
phi: Math.asin(this.y / this.length()),
};
},
angleTo(a) {
return Math.acos(this.dot(a) / (this.length() * a.length()));
},
toArray(n) {
return [this.x, this.y, this.z].slice(0, n || 3);
},
clone() {
return new Vector(this.x, this.y, this.z);
},
init(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
return this;
},
};
// ### Static Methods
// `Vector.randomDirection()` returns a vector with a length of 1 and a
// statistically uniform direction. `Vector.lerp()` performs linear
// interpolation between two vectors.
Vector.negative = function (a, b) {
b.x = -a.x;
b.y = -a.y;
b.z = -a.z;
return b;
};
Vector.add = function (a, b, c) {
if (b instanceof Vector) {
c.x = a.x + b.x;
c.y = a.y + b.y;
c.z = a.z + b.z;
} else {
c.x = a.x + b;
c.y = a.y + b;
c.z = a.z + b;
}
return c;
};
Vector.subtract = function (a, b, c) {
if (b instanceof Vector) {
c.x = a.x - b.x;
c.y = a.y - b.y;
c.z = a.z - b.z;
} else {
c.x = a.x - b;
c.y = a.y - b;
c.z = a.z - b;
}
return c;
};
Vector.multiply = function (a, b, c) {
if (b instanceof Vector) {
c.x = a.x * b.x;
c.y = a.y * b.y;
c.z = a.z * b.z;
} else {
c.x = a.x * b;
c.y = a.y * b;
c.z = a.z * b;
}
return c;
};
Vector.divide = function (a, b, c) {
if (b instanceof Vector) {
c.x = a.x / b.x;
c.y = a.y / b.y;
c.z = a.z / b.z;
} else {
c.x = a.x / b;
c.y = a.y / b;
c.z = a.z / b;
}
return c;
};
Vector.cross = function (a, b, c) {
c.x = a.y * b.z - a.z * b.y;
c.y = a.z * b.x - a.x * b.z;
c.z = a.x * b.y - a.y * b.x;
return c;
};
Vector.unit = function (a, b) {
const length = a.length();
b.x = a.x / length;
b.y = a.y / length;
b.z = a.z / length;
return b;
};
Vector.fromAngles = function (theta, phi) {
return new Vector(
Math.cos(theta) * Math.cos(phi),
Math.sin(phi),
Math.sin(theta) * Math.cos(phi)
);
};
Vector.randomDirection = function () {
return Vector.fromAngles(Math.random() * Math.PI * 2, Math.asin(Math.random() * 2 - 1));
};
Vector.min = function (a, b) {
return new Vector(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z));
};
Vector.max = function (a, b) {
return new Vector(Math.max(a.x, b.x), Math.max(a.y, b.y), Math.max(a.z, b.z));
};
Vector.lerp = function (a, b, fraction) {
return b.subtract(a).multiply(fraction).add(a);
};
Vector.fromArray = function (a) {
return new Vector(a[0], a[1], a[2]);
};
Vector.angleBetween = function (a, b) {
return a.angleTo(b);
};