Source: math/Vector3.js

var _vector = new Vector3();

/**
 * a vector 3 class
 * @constructor
 * @memberof zen3d
 * @param {number} [x=0]
 * @param {number} [y=0]
 * @param {number} [z=0]
 */
function Vector3(x, y, z) {
	this.x = x || 0;
	this.y = y || 0;
	this.z = z || 0;
}

Object.assign(Vector3.prototype, /** @lends zen3d.Vector3.prototype */{

	/**
     *
     */
	lerpVectors: function(v1, v2, ratio) {
		return this.subVectors(v2, v1).multiplyScalar(ratio).add(v1);
	},

	/**
     *
     */
	set: function(x, y, z) {
		this.x = x || 0;
		this.y = y || 0;
		this.z = z || 0;

		return this;
	},

	/**
     *
     */
	min: function(v) {
		this.x = Math.min(this.x, v.x);
		this.y = Math.min(this.y, v.y);
		this.z = Math.min(this.z, v.z);

		return this;
	},

	/**
     *
     */
	max: function(v) {
		this.x = Math.max(this.x, v.x);
		this.y = Math.max(this.y, v.y);
		this.z = Math.max(this.z, v.z);

		return this;
	},

	/**
     *
     */
	getLength: function() {
		return Math.sqrt(this.getLengthSquared());
	},

	/**
     *
     */
	getLengthSquared: function() {
		return this.x * this.x + this.y * this.y + this.z * this.z;
	},

	/**
     *
     */
	normalize: function(thickness) {
		thickness = thickness || 1;
		var length = this.getLength();
		if (length != 0) {
			var invLength = thickness / length;
			this.x *= invLength;
			this.y *= invLength;
			this.z *= invLength;
			return this;
		}
	},

	/**
     *
     */
	subtract: function(a, target) {
		if (!target) {
			target = new Vector3();
		}
		target.set(this.x - a.x, this.y - a.y, this.z - a.z);
		return target;
	},

	/**
     *
     */
	multiply: function (v) {
		this.x *= v.x;
		this.y *= v.y;
		this.z *= v.z;

		return this;
	},

	/**
     *
     */
	crossVectors: function(a, b) {
		var ax = a.x,
			ay = a.y,
			az = a.z;
		var bx = b.x,
			by = b.y,
			bz = b.z;

		this.x = ay * bz - az * by;
		this.y = az * bx - ax * bz;
		this.z = ax * by - ay * bx;

		return this;
	},

	/**
     *
     */
	cross: function(v) {
		var x = this.x,
			y = this.y,
			z = this.z;

		this.x = y * v.z - z * v.y;
		this.y = z * v.x - x * v.z;
		this.z = x * v.y - y * v.x;

		return this;
	},

	/**
     *
     */
	negate: function () {
		this.x = -this.x;
		this.y = -this.y;
		this.z = -this.z;

		return this;
	},

	/**
     *
     */
	dot: function(a) {
		return this.x * a.x + this.y * a.y + this.z * a.z;
	},

	/**
     *
     */
	applyQuaternion: function(q) {
		var x = this.x,
			y = this.y,
			z = this.z;
		var qx = q._x,
			qy = q._y,
			qz = q._z,
			qw = q._w;

		// calculate quat * vector

		var ix = qw * x + qy * z - qz * y;
		var iy = qw * y + qz * x - qx * z;
		var iz = qw * z + qx * y - qy * x;
		var iw = -qx * x - qy * y - qz * z;

		// calculate result * inverse quat

		this.x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
		this.y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
		this.z = iz * qw + iw * -qz + ix * -qy - iy * -qx;

		return this;
	},

	/**
     *
     */
	applyMatrix4: function(m) {
		var x = this.x,
			y = this.y,
			z = this.z;
		var e = m.elements;

		var w = 1 / (e[3] * x + e[7] * y + e[11] * z + e[15]);

		this.x = (e[0] * x + e[4] * y + e[8] * z + e[12]) * w;
		this.y = (e[1] * x + e[5] * y + e[9] * z + e[13]) * w;
		this.z = (e[2] * x + e[6] * y + e[10] * z + e[14]) * w;

		return this;
	},

	/**
     *
     */
	applyMatrix3: function (m) {
		var x = this.x, y = this.y, z = this.z;
		var e = m.elements;

		this.x = e[0] * x + e[3] * y + e[6] * z;
		this.y = e[1] * x + e[4] * y + e[7] * z;
		this.z = e[2] * x + e[5] * y + e[8] * z;

		return this;
	},

	/**
     *
     */
	transformDirection: function(m) {
		// input: Matrix4 affine matrix
		// vector interpreted as a direction

		var x = this.x,
			y = this.y,
			z = this.z;
		var e = m.elements;

		this.x = e[0] * x + e[4] * y + e[8] * z;
		this.y = e[1] * x + e[5] * y + e[9] * z;
		this.z = e[2] * x + e[6] * y + e[10] * z;

		return this.normalize();
	},

	/**
     *
     */
	setFromMatrixPosition: function(m) {
		return this.setFromMatrixColumn(m, 3);
	},

	/**
     *
     */
	setFromMatrixColumn: function(m, index) {
		return this.fromArray(m.elements, index * 4);
	},

	/**
     *
     */
	fromArray: function(array, offset) {
		if (offset === undefined) offset = 0;

		this.x = array[offset];
		this.y = array[offset + 1];
		this.z = array[offset + 2];

		return this;
	},

	/**
     *
     */
	toArray: function (array, offset) {
		if (array === undefined) array = [];
		if (offset === undefined) offset = 0;

		array[offset] = this.x;
		array[offset + 1] = this.y;
		array[offset + 2] = this.z;

		return array;
	},

	/**
     *
     */
	copy: function(v) {
		this.x = v.x;
		this.y = v.y;
		this.z = v.z;

		return this;
	},

	/**
     *
     */
	addVectors: function(a, b) {
		this.x = a.x + b.x;
		this.y = a.y + b.y;
		this.z = a.z + b.z;

		return this;
	},

	/**
     *
     */
	addScalar: function(s) {
		this.x += s;
		this.y += s;
		this.z += s;

		return this;
	},

	/**
     *
     */
	add: function(v) {
		this.x += v.x;
		this.y += v.y;
		this.z += v.z;

		return this;
	},

	/**
     *
     */
	addScaledVector: function (v, s) {
		this.x += v.x * s;
		this.y += v.y * s;
		this.z += v.z * s;

		return this;
	},

	/**
     *
     */
	subVectors: function(a, b) {
		this.x = a.x - b.x;
		this.y = a.y - b.y;
		this.z = a.z - b.z;

		return this;
	},

	/**
     *
     */
	sub: function(v) {
		this.x -= v.x;
		this.y -= v.y;
		this.z -= v.z;

		return this;
	},

	/**
     *
     */
	multiplyScalar: function(scalar) {
		this.x *= scalar;
		this.y *= scalar;
		this.z *= scalar;

		return this;
	},

	/**
     *
     */
	distanceToSquared: function(v) {
		var dx = this.x - v.x,
			dy = this.y - v.y,
			dz = this.z - v.z;

		return dx * dx + dy * dy + dz * dz;
	},

	/**
     *
     */
	distanceTo: function(v) {
		return Math.sqrt(this.distanceToSquared(v));
	},

	/**
     *
     */
	setFromSpherical: function (s) {
		var sinPhiRadius = Math.sin(s.phi) * s.radius;

		this.x = sinPhiRadius * Math.sin(s.theta);
		this.y = Math.cos(s.phi) * s.radius;
		this.z = sinPhiRadius * Math.cos(s.theta);

		return this;
	},

	/**
     *
     */
	project: function(camera) {
		return this.applyMatrix4(camera.viewMatrix).applyMatrix4(camera.projectionMatrix);
	},

	/**
     *
     */
	unproject: function(camera) {
		return this.applyMatrix4(camera.projectionMatrixInverse).applyMatrix4(camera.worldMatrix);
	},

	/**
     *
     */
	reflect: function (normal) {
		// reflect incident vector off plane orthogonal to normal
		// normal is assumed to have unit length

		return this.sub(_vector.copy(normal).multiplyScalar(2 * this.dot(normal)));
	},

	/**
     *
     */
	equals: function(v) {
		return ((v.x === this.x) && (v.y === this.y) && (v.z === this.z));
	},

	/**
     *
     */
	clone: function() {
		return new Vector3(this.x, this.y, this.z);
	},

	applyProjection: function(m) {
		console.error("zen3d.Vector3: .applyProjection has been removed. Use .applyMatrix4 instead.");
	}

});

export { Vector3 };