js打造线状足球3d模型效果可以下载为svg

代码语言:html

所属分类:三维

代码描述:js打造线状足球3d模型效果可以下载为svg

代码标签: 足球 3d 模型 效果 可以 下载 svg

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">

<style>
:root {
  --sq: 500px;
}

html {
  height: 100vh;
}
body {
  align-items: center;
  display: flex;
  height: 100%;
  justify-content: center;
  margin: 0;
  padding: 0;
}
canvas {
  height: auto;
  width: var(--sq);
}

.container {
  height: var(--sq);
  text-align: center;
  width: var(--sq);
}
</style>

</head>
<body translate="no">
<div class="container"></div>
<script type="text/javascript" src="http://repo.bfw.wiki/bfwrepo/js/paper.js"></script>
<script >const conversionFactor = 180 / Math.PI;

var radianToDegrees = function(radian) {
	return radian * conversionFactor;
}

var degreesToRadian = function(degrees) {
	return degrees / conversionFactor;
}

let Vec2, Vec3, Vec4, Mat2, Mat3, Mat4, Quat;

/**
 * A basic 2D Vector class that provides simple algebraic functionality in the form
 * of 2D Vectors.
 *
 * We use Getters/setters for both principle properties (x & y) as well as virtual
 * properties (rotation, length etc.).
 *
 * @class Vec2
 * @author Liam Egan <liam@wethecollective.com>
 * @version 1.0.0
 * @created Jan 07, 2020
 */
Vec2 = class {

	/**
	 * The Vector Class constructor
	 *
	 * @constructor
	 * @param {number} x 				The x coord
	 * @param {number} y 				The y coord
	 */
  constructor(x, y){
    if(isNaN(x)) x = 0;
    if(isNaN(y)) y = 0;
    this.x = x;
    this.y = y;
  }

  /**
   * Resets the vector coordinates
   *
   * @public
	 * @param {number|Array} x 	The x coord, OR the array to reset to
	 * @param {number} y 				The y coord
   */
	reset(x, y) {
    if(x instanceof Array && x.length >= 2) {
      this.x = x[0];
      this.y = x[1];
    } else {
      this.x = x;
      this.y = y;
    }
  }
  
  /**
   * Resets the vector coordinates to another vector object
   *
   * @public
	 * @param {Vector} v 				The vector object to use to reset the coordinates
   */
  resetToVector(v) {
    if(v instanceof Vec2) {
      this.x = v.x;
      this.y = v.y;
    }
  }

	/**
	 * Clones the vector
	 *
	 * @public
	 * @return {Vector}					The cloned vector
	 */
  clone() {
    return new Vec2(this.x, this.y);
  }

  /**
   * Adds one vector to another.
   *
   * @public
   * @chainable
   * @param  {Vec2}  vector The vector to add to this one
   * @return {Vec2}					Returns itself, modified
   */
  add(vector) {
    this.x += vector.x;
    this.y += vector.y;
    return this;
  }
  /**
   * Clones the vector and adds the vector to it instead
   *
   * @public
   * @chainable
   * @param  {Vec2}  vector The vector to add to this one
   * @return {Vec2}					Returns the clone of itself, modified
   */
  addNew(vector) {
    return this.clone().add(vector);
  }

  /**
   * Adds a scalar to the vector, modifying both the x and y
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to add to the vector
   * @return {Vec2}					Returns itself, modified
   */
  addScalar(scalar) {
    return this.add(new Vec2(scalar, scalar));
  }
  /**
   * Clones the vector and adds the scalar to it instead
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to add to the vector
   * @return {Vec2}					Returns the clone of itself, modified
   */
  addScalarNew(scalar) {
    return this.clone().addScalar(scalar);
  }

  /**
   * Subtracts one vector from another.
   *
   * @public
   * @chainable
   * @param  {Vec2}  vector The vector to subtract from this one
   * @return {Vec2}					Returns itself, modified
   */
  subtract(vector) {
    this.x -= vector.x;
    this.y -= vector.y;
    return this;
  }
  /**
   * Clones the vector and subtracts the vector from it instead
   *
   * @public
   * @chainable
   * @param  {Vec2}  vector The vector to subtract from this one
   * @return {Vec2}					Returns the clone of itself, modified
   */
  subtractNew(vector) {
    return this.clone().subtract(vector);
  }

  /**
   * Subtracts a scalar from the vector, modifying both the x and y
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to subtract from the vector
   * @return {Vec2}					Returns itself, modified
   */
  subtractScalar(scalar) {
    return this.subtract(new Vec2(scalar, scalar));
  }
  /**
   * Clones the vector and subtracts the scalar from it instead
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to add to the vector
   * @return {Vec2}					Returns the clone of itself, modified
   */
  subtractScalarNew(scalar) {
    return this.clone().subtractScalar(scalar);
  }

  /**
   * Divides one vector by another.
   *
   * @public
   * @chainable
   * @param  {Vec2}  vector The vector to divide this by
   * @return {Vec2}					Returns itself, modified
   */
  divide(vector) {
    if(vector.x !== 0) {
      this.x /= vector.x
    } else {
      this.x = 0;
    }
    if(vector.y !== 0) {
      this.y /= vector.y
    } else {
      this.y = 0;
    }
    return this;
  }
  /**
   * Clones the vector and divides it by the vector instead
   *
   * @public
   * @chainable
   * @param  {Vec2}  vector The vector to divide the clone by
   * @return {Vec2}					Returns the clone of itself, modified
   */
  divideNew(vector) {
    return this.clone().divide(vector);
  }

  /**
   * Divides the vector by a scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to divide both x and y by
   * @return {Vec2}					Returns itself, modified
   */
  divideScalar(scalar) {
    var v = new Vec2(scalar, scalar);
    return this.divide(v);
  }
  /**
   * Clones the vector and divides it by the provided scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to divide both x and y by
   * @return {Vec2}					Returns the clone of itself, modified
   */
  divideScalarNew(scalar) {
    return this.clone().divideScalar(scalar);
  }

  /**
   * Multiplies one vector by another.
   *
   * @public
   * @chainable
   * @param  {Vec2}  vector The vector to multiply this by
   * @return {Vec2}					Returns itself, modified
   */
  multiply(vector) {
    this.x *= vector.x;
    this.y *= vector.y;
    return this;
  }
  /**
   * Clones the vector and multiplies it by the vector instead
   *
   * @public
   * @chainable
   * @param  {Vec2}  vector The vector to multiply the clone by
   * @return {Vec2}					Returns the clone of itself, modified
   */
  multiplyNew(vector) {
    return this.clone().multiply(vector);
  }

  /**
   * Multiplies the vector by a scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to multiply both x and y by
   * @return {Vec2}					Returns itself, modified
   */
  multiplyScalar(scalar) {
    var v = new Vec2(scalar, scalar);
    return this.multiply(v);
  }
  /**
   * Clones the vector and multiplies it by the provided scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to multiply both x and y by
   * @return {Vec2}					Returns the clone of itself, modified
   */
  multiplyScalarNew(scalar) {
    return this.clone().multiplyScalar(scalar);
  }

  /**
   * Alias of {@link Vector#multiplyScalar__anchor multiplyScalar}
   */
  scale(scalar) {
    return this.multiplyScalar(scalar);
  }
  /**
   * Alias of {@link Vector#multiplyScalarNew__anchor multiplyScalarNew}
   */
  scaleNew(scalar) {
    return this.multiplyScalarNew(scalar);
  }

  /**
   * Rotates a vecor by a given amount, provided in radians.
   *
   * @public
   * @chainable
   * @param  {number}  radian The angle, in radians, to rotate the vector by
   * @return {Vec2}					Returns itself, modified
   */
  rotate(radian) {
  	var x = (this.x * Math.cos(radian)) - (this.y * Math.sin(radian));
  	var y = (this.x * Math.sin(radian)) + (this.y * Math.cos(radian));

		this.x = x;
		this.y = y;

  	return this;
  }
  /**
   * Clones the vector and rotates it by the supplied radian value
   *
   * @public
   * @chainable
   * @param  {number}  radian The angle, in radians, to rotate the vector by
   * @return {Vec2}					Returns the clone of itself, modified
   */
  rotateNew(radian) {
    return this.clone().rotate(radian);
  }

	/**
	 * Rotates a vecor by a given amount, provided in degrees. Converts the degree
	 * value to radians and runs the rotaet method.
	 *
	 * @public
	 * @chainable
	 * @param  {number}  degrees The angle, in degrees, to rotate the vector by
	 * @return {Vec2}						Returns itself, modified
	 */
  rotateDeg(degrees) {
    return this.rotate(degreesToRadian(degrees));
  }
  /**
   * Clones the vector and rotates it by the supplied degree value
   *
   * @public
   * @chainable
	 * @param  {number}  degrees The angle, in degrees, to rotate the vector by
   * @return {Vec2}					 Returns the clone of itself, modified
   */
  rotateDegNew(degrees) {
    return this.rotateNew(degreesToRadian(degrees));
  }

  /**
   * Alias of {@link Vector#rotate__anchor rotate}
   */
  rotateBy(radian) {
		return this.rotate(radian);
  }
  /**
   * Alias of {@link Vector#rotateNew__anchor rotateNew}
   */
  rotateByNew(radian) {
    return this.rotateNew(radian);
  }

  /**
   * Alias of {@link Vector#rotateDeg__anchor rotateDeg}
   */
  rotateDegBy(degrees) {
		return this.rotateDeg(degrees);
  }
  /**
   * Alias of {@link Vector#rotateDegNew__anchor rotateDegNew}
   */
  rotateDegByNew(radian) {
    return this.rotateDegNew(radian);
  }

  /**
   * Rotates a vector to a specific angle
   *
   * @public
   * @chainable
   * @param  {number}  radian The angle, in radians, to rotate the vector to
   * @return {Vec2}					Returns itself, modified
   */
	rotateTo(radian) {
		return this.rotate(radian-this.angle);
	};
  /**
   * Clones the vector and rotates it to the supplied radian value
   *
   * @public
   * @chainable
   * @param  {number}  radian The angle, in radians, to rotate the vector to
   * @return {Vec2}					Returns the clone of itself, modified
   */
	rotateToNew(radian) {
    return this.clone().rotateTo(radian);
	};

	/**
	 * Rotates a vecor to a given amount, provided in degrees. Converts the degree
	 * value to radians and runs the rotateTo method.
	 *
	 * @public
	 * @chainable
	 * @param  {number}  degrees The angle, in degrees, to rotate the vector to
	 * @return {Vec2}						Returns itself, modified
	 */
  rotateToDeg(degrees) {
    return this.rotateTo(degreesToRadian(degrees));
  }
  /**
   * Clones the vector and rotates it to the supplied degree value
   *
   * @public
   * @chainable
	 * @param  {number}  degrees The angle, in degrees, to rotate the vector to
   * @return {Vec2}					 Returns the clone of itself, modified
   */
  rotateToDegNew(degrees) {
    return this.rotateToNew(degreesToRadian(degrees));
  }
  
	/**
	 * Negates the vector.
	 *
	 * @public
	 * @chainable
	 * @return {Vec2}					Returns itself, modified
	 */
  negate() {
    return this.multiplyScalar(-1.);
  }
  
	/**
	 * Clones the vector and negates it.
	 *
	 * @public
	 * @chainable
	 * @return {Vec2}					Returns itself, modified
	 */
  negateNew() {
    return this.multiplyScalarNew(-1.);
  }
  
	/**
	 * Inverses the vector.
	 *
	 * @public
	 * @chainable
	 * @return {Vec2}					Returns itself, modified
	 */
  inverse() {
    this.x = 1./this.x;
    this.y = 1./this.y;
    return this;
  }
  
	/**
	 * Clones the vector and then inverses it.
	 *
	 * @public
	 * @chainable
	 * @return {Vec2}					Returns itself, modified
	 */
  inverseNew() {
    const c = new Vector();
    c.x = 1./this.x;
    c.y = 1./this.y;
    return c;
  }

	/**
	 * Normalises the vector down to a length of 1 unit
	 *
	 * @public
	 * @chainable
	 * @return {Vec2}					Returns itself, modified
	 */
	normalise() {
		return this.divideScalar(this.length);
	}
	/**
	 * Clones the vector and normalises it
	 *
	 * @public
	 * @chainable
	 * @return {Vec2}					Returns a clone of itself, modified
	 */
	normaliseNew() {
		return this.divideScalarNew(this.length);
	}

	/**
	 * Calculates the distance between this and the supplied vector
	 *
	 * @param  {Vec2} vector The vector to calculate the distance from
	 * @return {number}        The distance between this and the supplied vector
	 */
	distance(vector) {
		return this.subtractNew(vector).length;
	}

	/**
	 * Calculates the distance on the X axis between this and the supplied vector
	 *
	 * @param  {Vec2} vector The vector to calculate the distance from
	 * @return {number}        The distance, along the x axis, between this and the supplied vector
	 */
	distanceX(vector) {
		return this.x - vector.x;
	}

	/**
	 * Calculated the distance on the Y axis between this and the supplied vector
	 *
	 * @param  {Vec2} vector The vector to calculate the distance from
	 * @return {number}        The distance, along the y axis, between this and the supplied vector
	 */
	distanceY(vector) {
		return this.y - vector.y;
	}


	/**
	 * Calculates the dot product between this and a supplied vector
	 *
	 * @example
	 * // returns -14
	 * new Vector(2, -3).dot(new Vector(-4, 2))
	 * new Vector(-4, 2).dot(new Vector(2, -3))
	 * new Vector(2, -4).dot(new Vector(-3, 2))
	 *
	 * @param  {Vec2} vector The vector object against which to calculate the dot product
	 * @return {number}        The dot product of the two vectors
	 */
	dot(vector) {
		return (this.x * vector.x) + (this.y * vector.y);
	}

	/**
	 * Calculates the cross product between this and the supplied vector.
	 *
	 * @example
	 * // returns -2
	 * new Vector(2, -3).cross(new Vector(-4, 2))
	 * new Vector(-4, 2).cross(new Vector(2, -3))
	 * // returns 2
	 * new Vector(2, -4).cross(new Vector(-3, 2))
	 *
	 * @param  {Vec2} vector The vector object against which to calculate the cross product
	 * @return {number}        The cross product of the two vectors
	 */
	cross(vector) {
		return (this.x * vector.x) - (this.y * vector.y);
	}
  
  ceil() {
    this.x = Math.ceil(this.x);
    this.y = Math.ceil(this.y);
    return this;
  }
  
  ceilNew() {
    return this.clone().ceil();
  }
  
  floor() {
    this.x = Math.floor(this.x);
    this.y = Math.floor(this.y);
    return this;
  }
  
  floorNew() {
    return this.clone().floor();
  }
  
  round() {
    this.x = Math.round(this.x);
    this.y = Math.round(this.y);
    return this;
  }
  
  roundNew() {
    return this.clone().round();
  }
  
  fract() {
    this.x -= Math.floor(this.x);
    this.y -= Math.floor(this.y);
    return this;
  }
  
  fractNew() {
    return this.clone().fract();
  }
  
  transformByMat2(m) {
    if(m.array) m = m.array; // This just transforms the matrix to an array.
    if(m instanceof Array && m.length >= 4) {
      const c = this.clone();
      this.x = m[0] * c.x + m[2] * c.y;
      this.y = m[1] * c.x + m[3] * c.y;
    }
    return this;
  }
  
  transformByMat2New(m) {
    return this.clone().transformByMat2(m);
  }
  
  transformByMat3(m) {
    if(m.array) m = m.array; // This just transforms the matrix to an array.
    if(m instanceof Array && m.length >= 9) {
      const c = this.clone();
      this.x = m[0] * c.x + m[3] * c.y + m[6];
      this.y = m[1] * c.x + m[4] * c.y + m[7];
    }
    return this;
  }
  
  transformByMat3New(m) {
    return this.clone().transformByMat3(m);
  }


  /**
   * Getters and setters
   */

  /**
   * (getter/setter) The x value of the vector.
   *
   * @type {number}
   * @default 0
   */
  set x(x) {
    if(typeof x == 'number') {
      this._x = x;
    } else {
      throw new TypeError('X should be a number');
    }
  }
  get x() {
    return this._x || 0;
  }

 /**
	* (getter/setter) The y value of the vector.
	*
	* @type {number}
	* @default 0
	*/
  set y(y) {
    if(typeof y == 'number') {
      this._y = y;
    } else {
      throw new TypeError('Y should be a number');
    }
  }
  get y() {
    return this._y || 0;
  }

	/**
	* (getter/setter) The length of the vector presented as a square. If you're using
	* length for comparison, this is quicker.
	*
	* @type {number}
	* @default 0
	*/
  set lengthSquared(length) {
    var factor;
    if(typeof length == 'number') {
      factor = length / this.lengthSquared;
      this.multiplyScalar(factor);
    } else {
      throw new TypeError('length should be a number');
    }
  }
  get lengthSquared() {
    return (this.x * this.x) + (this.y * this.y);
  }

	/**
	* (getter/setter) The length of the vector
	*
	* @type {number}
	* @default 0
	*/
  set length(length) {
    var factor;
    if(typeof length == 'number') {
      factor = length / this.length;
      this.multiplyScalar(factor);
    } else {
      throw new TypeError('length should be a number');
    }
  }
  get length() {
    return Math.sqrt(this.lengthSquared);
  }

	/**
	* (getter/setter) The angle of the vector, in radians
	*
	* @type {number}
	* @default 0
	*/
  set angle(radian) {
    if(typeof radian == 'number') {
      this.rotateTo(radian);
    } else {
      throw new TypeError('angle should be a number');
    }
  }
  get angle() {
    return Math.atan2(this.y, this.x);
  }

	/**
	* (getter/setter) The angle of the vector, in radians
	*
	* @type {number}
	* @default 0
	*/
  set angleInDegrees(degrees) {
    if(typeof degrees == 'number') {
      this.rotateToDeg(degrees);
    } else {
      throw new TypeError('angle should be a number');
    }
  }
  get angleInDegrees() {
    return radianToDegrees(Math.atan2(this.y, this.x));
  }

	/**
	 * (getter/setter) Vector width.
   * Alias of {@link Vector#x x}
	 *
	 * @type {number}
	 */
	set width(w) {
		this.x = w;
	}
	get width() {
		return this.x;
	}

	/**
	 * (getter/setter) Vector height.
   * Alias of {@link Vector#x x}
	 *
	 * @type {number}
	 */
	set height(h) {
		this.y = h;
	}
	get height() {
		return this.y;
	}

	/**
	 * (getter) Vector area.
	 * @readonly
	 *
	 * @type {number}
	 */
	get area() {
		return this.x * this.y;
	}

	/**
	 * (getter/setter) Vector slope.
	 *
	 * @type {number}
	 */
  set slope(value) {
    if(!isNaN(value)) {
      let angle = Math.atan(value);
      this.angle = angle;
    }
  }
  get slope() {
    return this.y / this.x;
  }

	/**
	 * (getter) Returns the basic array representation of this vector.
	 * @readonly
	 *
	 * @type {number}
	 */
  get array() {
    return [this.x, this.y];
  }
  
	/**
	 * (getter/sette) Swizzle XY
	 *
	 * @type {number}
	 */
  get xy() {
    return new Vec2(this.x, this.y);
  }
  set xy(v) {
    if(v instanceof Vec2) {
      this.resetToVector(v);
    } else if(v instanceof Array && v.length >= 2) {
      this.reset(v);
    } else {
      throw new Error('input should be of type Vector');
    }
  }
  
	/**
	 * (getter/sette) Swizzle YX
	 *
	 * @type {number}
	 */
  get yx() {
    return new Vec2(this.y, this.x);
  }
  set yx(v) {
    this.xy = Vec2.interpolate(v).yx;
  }
  
	/**
	 * (getter/sette) Swizzle XX
	 *
	 * @type {number}
	 */
  get xx() {
    return new Vec2(this.x, this.x);
  }
  set xx(v) {
    v = Vec2.interpolate(v);
    this.x = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle YY
	 *
	 * @type {number}
	 */
  get yy() {
    return new Vec2(this.y, this.y);
  }
  set yy(v) {
    v = Vec2.interpolate(v);
    this.y = v.y;
  }


  /**
   * Static methods
   */
  /**
   * Iterpolates a provided anonymous value into a vew Vec2
   *
   * @param {Vec2|array|string|number} v The value to interpolate
   * @returns {Vec2} out
   */
  static interpolate(v) {
    if(v instanceof Vec2) {
      return new Vec2(v.x, v.y);
    } else if(v instanceof Array && v.length >= 2) {
      return new Vec2(v[0], v[1]);
    } else if(!isNaN(v)) {
      return new Vec2(v, v);
    } else if(typeof v === 'string') {
      const nv = v.split(',');
      if(nv.length >= 2 && !isNaN(nv[0]) && !isNaN(nv[1])) {
        return new Vec2(Number(nv[0], nv[1]));
      }
    } else {
      throw new Error('The passed interpolant could not be parsed into a vec2');
    }
  }
  
  /**
   * Performs a linear interpolation between two vec2's
   *
   * @param {vec2} v1 the first operand
   * @param {vec2} v2 the second operand
   * @param {Number} d interpolation amount in the range of 0 - 1
   * @returns {Vec2}
   */
  static lerp(v1, v2, d) {
    return new Vec2(
      v1.x + d * (v2.x - v1.x),
      v1.y + d * (v2.y - v1.y)
    );
  }
  
  static getAngle(a, b) {
    a = a.clone();
    b = b.clone();
    
    a.normalise();
    b.normalise();

    const cosine = a.dot(b);

    if(cosine > 1.0) {
      return 0;
    }
    else if(cosine < -1.0) {
      return Math.PI;
    } else {
      return Math.acos(cosine);
    }
  }

}

/**
 * A basic 3D Vector class that provides simple algebraic functionality in the form
 * of 3D Vectors.
 *
 * We use Getters/setters for both principle properties (x & y) as well as virtual
 * properties (rotation, length etc.).
 *
 * @class Vec3
 * @author Liam Egan <liam@wethecollective.com>
 * @version 1.0.0
 * @created Jan 07, 2020
 */
Vec3 = class {

	/**
	 * The Vector Class constructor
	 *
	 * @constructor
	 * @param {number} x 				The x coord
	 * @param {number} y 				The y coord
	 */
  constructor(x, y, z){
    if(x instanceof Array && x.length >= 3) {
      this.x = x[0];
      this.y = x[1];
      this.z = x[2];
    } else {
      if(isNaN(x)) x = 0;
      if(isNaN(y)) y = 0;
      if(isNaN(z)) z = 0;
      this.x = x;
      this.y = y;
      this.z = z;
    }
  }

  /**
   * Resets the vector coordinates
   *
   * @public
	 * @param {number|Array} x 	The x coord, OR the array to reset to
	 * @param {number} y 				The y coord
   */
	reset(x, y, z) {
    if(x instanceof Array && x.length >= 3) {
      this.x = x[0];
      this.y = x[1];
      this.z = x[2];
    } else {
      this.x = x;
      this.y = y;
      this.z = z;
    }
  }
  
  /**
   * Resets the vector coordinates to another vector object
   *
   * @public
	 * @param {Vector} v 				The vector object to use to reset the coordinates
   */
  resetToVector(v) {
    if(v instanceof Vec3) {
      this.x = v.x;
      this.y = v.y;
      this.z = v.z;
    }
  }

	/**
	 * Clones the vector
	 *
	 * @public
	 * @return {Vec3}					The cloned vector
	 */
  clone() {
    return new Vec3(this.x, this.y, this.z);
  }

  /**
   * Adds one vector to another.
   *
   * @public
   * @chainable
   * @param  {Vec3}  vector The vector to add to this one
   * @return {Vec3}					Returns itself, modified
   */
  add(vector) {
    this.x += vector.x;
    this.y += vector.y;
    this.z += vector.z;
    return this;
  }
  /**
   * Clones the vector and adds the vector to it instead
   *
   * @public
   * @chainable
   * @param  {Vec3}  vector The vector to add to this one
   * @return {Vec3}					Returns the clone of itself, modified
   */
  addNew(vector) {
    return this.clone().add(vector);
  }

  /**
   * Adds a scalar to the vector, modifying both the x and y
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to add to the vector
   * @return {Vec3}					Returns itself, modified
   */
  addScalar(scalar) {
    return this.add(new Vec3(scalar, scalar, scalar));
  }
  /**
   * Clones the vector and adds the scalar to it instead
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to add to the vector
   * @return {Vec3}					Returns the clone of itself, modified
   */
  addScalarNew(scalar) {
    return this.clone().addScalar(scalar);
  }

  /**
   * Subtracts one vector from another.
   *
   * @public
   * @chainable
   * @param  {Vec3}  vector The vector to subtract from this one
   * @return {Vec3}					Returns itself, modified
   */
  subtract(vector) {
    this.x -= vector.x;
    this.y -= vector.y;
    this.z -= vector.z;
    return this;
  }
  /**
   * Clones the vector and subtracts the vector from it instead
   *
   * @public
   * @chainable
   * @param  {Vec3}  vector The vector to subtract from this one
   * @return {Vec3}					Returns the clone of itself, modified
   */
  subtractNew(vector) {
    return this.clone().subtract(vector);
  }

  /**
   * Subtracts a scalar from the vector, modifying both the x and y
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to subtract from the vector
   * @return {Vec3}					Returns itself, modified
   */
  subtractScalar(scalar) {
    return this.subtract(new Vec3(scalar, scalar, scalar));
  }
  /**
   * Clones the vector and subtracts the scalar from it instead
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to add to the vector
   * @return {Vec3}					Returns the clone of itself, modified
   */
  subtractScalarNew(scalar) {
    return this.clone().subtractScalar(scalar);
  }

  /**
   * Divides one vector by another.
   *
   * @public
   * @chainable
   * @param  {Vec3}  vector The vector to divide this by
   * @return {Vec3}					Returns itself, modified
   */
  divide(vector) {
    if(vector.x !== 0) {
      this.x /= vector.x
    } else {
      this.x = 0;
    }
    if(vector.y !== 0) {
      this.y /= vector.y
    } else {
      this.y = 0;
    }
    if(vector.z !== 0) {
      this.z /= vector.z;
    } else {
      this.z = 0;
    }
    return this;
  }
  /**
   * Clones the vector and divides it by the vector instead
   *
   * @public
   * @chainable
   * @param  {Vec3}  vector The vector to divide the clone by
   * @return {Vec3}					Returns the clone of itself, modified
   */
  divideNew(vector) {
    return this.clone().divide(vector);
  }

  /**
   * Divides the vector by a scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to divide both x and y by
   * @return {Vec3}					Returns itself, modified
   */
  divideScalar(scalar) {
    var v = new Vec3(scalar, scalar, scalar);
    return this.divide(v);
  }
  /**
   * Clones the vector and divides it by the provided scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to divide both x and y by
   * @return {Vec3}					Returns the clone of itself, modified
   */
  divideScalarNew(scalar) {
    return this.clone().divideScalar(scalar);
  }

  /**
   * Multiplies one vector by another.
   *
   * @public
   * @chainable
   * @param  {Vec3}  vector The vector to multiply this by
   * @return {Vec3}					Returns itself, modified
   */
  multiply(vector) {
    this.x *= vector.x;
    this.y *= vector.y;
    this.z *= vector.z;
    return this;
  }
  /**
   * Clones the vector and multiplies it by the vector instead
   *
   * @public
   * @chainable
   * @param  {Vec3}  vector The vector to multiply the clone by
   * @return {Vec3}					Returns the clone of itself, modified
   */
  multiplyNew(vector) {
    return this.clone().multiply(vector);
  }

  /**
   * Multiplies the vector by a scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to multiply both x and y by
   * @return {Vec3}					Returns itself, modified
   */
  multiplyScalar(scalar) {
    var v = new Vec3(scalar, scalar, scalar);
    return this.multiply(v);
  }
  /**
   * Clones the vector and multiplies it by the provided scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to multiply both x and y by
   * @return {Vec3}					Returns the clone of itself, modified
   */
  multiplyScalarNew(scalar) {
    return this.clone().multiplyScalar(scalar);
  }

  /**
   * Alias of {@link Vector#multiplyScalar__anchor multiplyScalar}
   */
  scale(scalar) {
    return this.multiplyScalar(scalar);
  }
  /**
   * Alias of {@link Vector#multiplyScalarNew__anchor multiplyScalarNew}
   */
  scaleNew(scalar) {
    return this.multiplyScalarNew(scalar);
  }

  rotateX(origin, radian) {
    
    const s = Math.sin(radian);
    const c = Math.cos(radian);
    
    // Translate to the origin
    const translated = this.subtractNew(origin);
    
    // Rotate
    const rotated = translated.clone();
    rotated.y = rotated.y * c - rotated.z * s;
    rotated.z = rotated.y * s + rotated.z * c;

    // Translate back
		this.y = rotated.y + origin.y;
    this.z = rotated.z + origin.z;

  	return this;
  }
  
  rotateXNew(radian) {
    return this.clone().rotateX(radian);
  }

  rotateY(origin, radian) {
    
    const s = Math.sin(radian);
    const c = Math.cos(radian);
    
    // Translate to the origin
    const translated = this.subtractNew(origin);
    
    // Rotate
    const rotated = translated.clone();
    rotated.x = rotated.z * s + rotated.z * c;
    rotated.z = rotated.z * c - rotated.x * s;

    // Translate back
		this.x = rotated.x + origin.x;
    this.z = rotated.z + origin.z;

  	return this;
  }
  
  rotateyNew(radian) {
    return this.clone().rotateY(radian);
  }

  rotateZ(origin, radian) {
    
    const s = Math.sin(radian);
    const c = Math.cos(radian);
    
    // Translate to the origin
    const translated = this.subtractNew(origin);
    
    // Rotate
    const rotated = translated.clone();
    rotated.x = rotated.x * c - rotated.y * s;
    rotated.y = rotated.x * s + rotated.y * c;

    // Translate back
		this.x = rotated.x + origin.x;
    this.y = rotated.y + origin.y;

  	return this;
  }
  
  rotateZNew(radian) {
    return this.clone().rotateZ(radian);
  }
  
  transformByMat4(m) {
    if(m.array) m = m.array; // This just transforms the matrix to an array.
    if(m instanceof Array && m.length >= 16) {
      const o = this.clone();
      const w = m[3] * o.x + m[7] * o.y + m[11] * o.z + m[15] || 1.;
      this.x = ( m[0] * o.x + m[4] * o.y + m[8] * o.z + m[12] ) / w;
      this.y = ( m[1] * o.x + m[5] * o.y + m[9] * o.z + m[13] ) / w;
      this.y = ( m[2] * o.x + m[6] * o.y + m[10] * o.z + m[14] ) / w;
    }
    return this;
  }
  
  transformByMat4New(m) {
    return this.clone().transformByMat4(m);
  }
  
  transformByMat3(m) {
    if(m.array) m = m.array; // This just transforms the matrix to an array.
    if(m instanceof Array && m.length >= 9) {
      const o = this.clone();
      this.x = m[0] * o.x + m[3] * o.y + m[6] * o.z;
      this.y = m[1] * o.x + m[4] * o.y + m[7] * o.z;
      this.z = m[2] * o.x + m[5] * o.y + m[8] * o.z;
    }
    return this;
  }
  
  transformByMat3New(m) {
    return this.clone().transformByMat3(m);
  }
  
  transformByQuat(q) {
    if(q.array) q = q.array; // This just transforms the quaternion to an array.
    if(q instanceof Array && q.length >= 4) {
      const o = this.clone();
      const uv = new Vec3(
        q[1] * o.z - q[2] * o.y,
        q[2] * o.x - q[0] * o.z,
        q[0] * o.y - q[1] * o.x
      );
      const uuv = new Vec3(
        q[1] * uv.z - q[2] * uv.y,
        q[2] * uv.x - q[0] * uv.z,
        q[0] * uv.y - q[1] * uv.x
      );
      uv.scale(2*q[3]);
      uuv.scale(2*q[3]);
      
      this.add(uv);
      this.add(uuv);
    }
    return this;
  }
  
  transformByQuatNew(q) {
    return this.clone().transformByQuat(q);
  }
  
	/**
	 * Negates the vector.
	 *
	 * @public
	 * @chainable
	 * @return {Vec3}					Returns itself, modified
	 */
  negate() {
    return this.multiplyScalar(-1.);
  }
  
	/**
	 * Clones the vector and negates it.
	 *
	 * @public
	 * @chainable
	 * @return {Vec3}					Returns itself, modified
	 */
  negateNew() {
    return this.multiplyScalarNew(-1.);
  }
  
	/**
	 * Inverses the vector.
	 *
	 * @public
	 * @chainable
	 * @return {Vec3}					Returns itself, modified
	 */
  inverse() {
    this.x = 1./this.x;
    this.y = 1./this.y;
    this.z = 1./this.z;
    return this;
  }
  
	/**
	 * Clones the vector and then inverses it.
	 *
	 * @public
	 * @chainable
	 * @return {Vec3}					Returns itself, modified
	 */
  inverseNew() {
    const c = new Vector();
    c.x = 1./this.x;
    c.y = 1./this.y;
    c.z = 1./this.z;
    return c;
  }

	/**
	 * Normalises the vector down to a length of 1 unit
	 *
	 * @public
	 * @chainable
	 * @return {Vec3}					Returns itself, modified
	 */
	normalise() {
		return this.divideScalar(this.length);
	}
	/**
	 * Clones the vector and normalises it
	 *
	 * @public
	 * @chainable
	 * @return {Vec3}					Returns a clone of itself, modified
	 */
	normaliseNew() {
		return this.divideScalarNew(this.length);
	}

	/**
	 * Calculates the distance between this and the supplied vector
	 *
	 * @param  {Vec3} vector The vector to calculate the distance from
	 * @return {number}        The distance between this and the supplied vector
	 */
	distance(vector) {
		return this.subtractNew(vector).length;
	}

	/**
	 * Calculates the distance on the X axis between this and the supplied vector
	 *
	 * @param  {Vec3} vector The vector to calculate the distance from
	 * @return {number}        The distance, along the x axis, between this and the supplied vector
	 */
	distanceX(vector) {
		return this.x - vector.x;
	}

	/**
	 * Calculated the distance on the Y axis between this and the supplied vector
	 *
	 * @param  {Vec3} vector The vector to calculate the distance from
	 * @return {number}        The distance, along the y axis, between this and the supplied vector
	 */
	distanceY(vector) {
		return this.y - vector.y;
	}

	/**
	 * Calculated the distance on the Z axis between this and the supplied vector
	 *
	 * @param  {Vec3} vector The vector to calculate the distance from
	 * @return {number}        The distance, along the y axis, between this and the supplied vector
	 */
	distanceZ(vector) {
		return this.z - vector.z;
	}


	/**
	 * Calculates the dot product between this and a supplied vector
	 *
	 * @example
	 * // returns -14
	 * new Vector(2, -3).dot(new Vector(-4, 2))
	 * new Vector(-4, 2).dot(new Vector(2, -3))
	 * new Vector(2, -4).dot(new Vector(-3, 2))
	 *
	 * @param  {Vec3} vector The vector object against which to calculate the dot product
	 * @return {number}        The dot product of the two vectors
	 */
	dot(vector) {
		return (this.x * vector.x) + (this.y * vector.y) + (this.z * vector.z);
	}

	/**
	 * Calculates the cross product between this and the supplied vector.
	 *
	 * @example
	 * // returns -2
	 * new Vector(2, -3).cross(new Vector(-4, 2))
	 * new Vector(-4, 2).cross(new Vector(2, -3))
	 * // returns 2
	 * new Vector(2, -4).cross(new Vector(-3, 2))
	 *
	 * @param  {Vec3} vector The vector object against which to calculate the cross product
	 * @return {Vec3}        The cross product of the two vectors
	 */
	cross(vector) {
    return new Vec3(
      this.y * vector.z - this.z * vector.y,
      this.z * vector.x - this.x * vector.z,
      this.x * vector.y - this.y * vector.x
    );
	}
  
  crossNew() {
    this.clone().cross();
  }
  
  ceil() {
    this.x = Math.ceil(this.x);
    this.y = Math.ceil(this.y);
    this.z = Math.ceil(this.z);
    return this;
  }
  
  ceilNew() {
    return this.clone().ceil();
  }
  
  floor() {
    this.x = Math.floor(this.x);
    this.y = Math.floor(this.y);
    this.z = Math.floor(this.z);
    return this;
  }
  
  floorNew() {
    return this.clone().floor();
  }
  
  round() {
    this.x = Math.round(this.x);
    this.y = Math.round(this.y);
    this.z = Math.round(this.z);
    return this;
  }
  
  roundNew() {
    return this.clone().round();
  }
  
  fract() {
    this.x -= Math.floor(this.x);
    this.y -= Math.floor(this.y);
    this.z -= Math.floor(this.z);
    return this;
  }
  
  fractNew() {
    return this.clone().fract();
  }
  
  /**
   * Gets the rotation axis and angle for a given
   *
   * @param  {vec3} axis  Vector receiving the axis of rotation
   * @return {Number}     Angle, in radians, of the rotation
   */
  getAxisAngle(q) {
    if(q.array) q = q.array; // Parsing the quaternion into an array, if possible
    if(q instanceof Array && q.length >= 4) {
      const rad = Math.acos(q[3]) * 2.;
      const s = Math.sin(rad * .5);
      if(s > EPSILON) {
        this.x = q[0] / s;
        this.y = q[1] / s;
        this.z = q[2] / s;
      } else {
        this.x = 1;
        this.y = 0;
        this.z = 0;
      }
    }
  }


  /**
   * Getters and setters
   */

  /**
   * (getter/setter) The x value of the vector.
   *
   * @type {number}
   * @default 0
   */
  set x(x) {
    if(typeof x == 'number') {
      this._x = x;
    } else {
      throw new TypeError('X should be a number');
    }
  }
  get x() {
    return this._x || 0;
  }

 /**
	* (getter/setter) The y value of the vector.
	*
	* @type {number}
	* @default 0
	*/
  set y(y) {
    if(typeof y == 'number') {
      this._y = y;
    } else {
      throw new TypeError('Y should be a number');
    }
  }
  get y() {
    return this._y || 0;
  }

 /**
	* (getter/setter) The y value of the vector.
	*
	* @type {number}
	* @default 0
	*/
  set z(z) {
    if(typeof z == 'number') {
      this._z = z;
    } else {
      throw new TypeError('Y should be a number');
    }
  }
  get z() {
    return this._z || 0;
  }

	/**
	* (getter/setter) The length of the vector presented as a square. If you're using
	* length for comparison, this is quicker.
	*
	* @type {number}
	* @default 0
	*/
  set lengthSquared(length) {
    var factor;
    if(typeof length == 'number') {
      factor = length / this.lengthSquared;
      this.multiplyScalar(factor);
    } else {
      throw new TypeError('length should be a number');
    }
  }
  get lengthSquared() {
    return (this.x * this.x) + (this.y * this.y) + (this.z * this.z);
  }

	/**
	* (getter/setter) The length of the vector
	*
	* @type {number}
	* @default 0
	*/
  set length(length) {
    var factor;
    if(typeof length == 'number') {
      factor = length / this.length;
      this.multiplyScalar(factor);
    } else {
      throw new TypeError('length should be a number');
    }
  }
  get length() {
    return Math.sqrt(this.lengthSquared);
  }

	/**
	 * (getter/setter) Vector width.
   * Alias of {@link Vector#x x}
	 *
	 * @type {number}
	 */
	set width(w) {
		this.x = w;
	}
	get width() {
		return this.x;
	}

	/**
	 * (getter/setter) Vector height.
   * Alias of {@link Vector#x x}
	 *
	 * @type {number}
	 */
	set height(h) {
		this.y = h;
	}
	get height() {
		return this.y;
	}

	/**
	 * (getter/setter) Vector height.
   * Alias of {@link Vector#x x}
	 *
	 * @type {number}
	 */
	set depth(h) {
		this.z = h;
	}
	get depth() {
		return this.z;
	}

	/**
	 * (getter) Vector area.
	 * @readonly
	 *
	 * @type {number}
	 */
	get area() {
		return this.x * this.y * this.z;
	}

	/**
	 * (getter) Returns the basic array representation of this vector.
	 * @readonly
	 *
	 * @type {number}
	 */
  get array() {
    return [this.x, this.y, this.z];
  }
  
	/**
	 * (getter/sette) Swizzle XYZ
	 *
	 * @type {Vec3}
	 */
  get xyz() {
    return new Vec3(this.x, this.y, this.z);
  }
  set xyz(v) {
    if(v instanceof Vec3) {
      this.resetToVector(v);
    } else if(v instanceof Array && v.length >= 3) {
      this.reset(v);
    } else {
      throw new Error('input should be of type Vector');
    }
  }
  
	/**
	 * (getter/sette) Swizzle YZX
	 *
	 * @type {Vec3}
	 */
  get yzx() {
    return new Vec3(this.y, this.z, this.x);
  }
  set yzx(v) {
    this.xyz = Vec3.interpolate(v).yzx;
  }
  
	/**
	 * (getter/sette) Swizzle ZXY
	 *
	 * @type {Vec3}
	 */
  get zxy() {
    return new Vec3(this.z, this.x, this.y);
  }
  set zxy(v) {
    this.xyz = Vec3.interpolate(v).zxy;
  }
  
	/**
	 * (getter/sette) Swizzle XY
	 *
	 * @type {Vec2}
	 */
  get xy() {
    return new Vec2(this.x, this.y);
  }
  set xy(v) {
    v = Vec2.interpolate(v);
    this.x = v.x;
    this.y = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle YZ
	 *
	 * @type {Vec2}
	 */
  get yz() {
    return new Vec2(this.y, this.z);
  }
  set yz(v) {
    v = Vec2.interpolate(v);
    this.y = v.x;
    this.z = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle zx
	 *
	 * @type {Vec2}
	 */
  get zx() {
    return new Vec2(this.z, this.x);
  }
  set zx(v) {
    v = Vec2.interpolate(v);
    this.z = v.x;
    this.x = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle YX
	 *
	 * @type {number}
	 */
  get yx() {
    return new Vec2(this.y, this.x);
  }
  set yx(v) {
    v = Vec2.interpolate(v);
    this.x = v.y;
    this.y = v.x;
  }
  
	/**
	 * (getter/sette) Swizzle ZY
	 *
	 * @type {number}
	 */
  get zy() {
    return new Vec2(this.z, this.y);
  }
  set zy(v) {
    v = Vec2.interpolate(v);
    this.z = v.y;
    this.y = v.x;
  }
  
	/**
	 * (getter/sette) Swizzle XX
	 *
	 * @type {number}
	 */
  get xx() {
    return new Vec2(this.x, this.x);
  }
  set xx(v) {
    v = Vec2.interpolate(v);
    this.x = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle YY
	 *
	 * @type {number}
	 */
  get yy() {
    return new Vec2(this.y, this.y);
  }
  set yy(v) {
    v = Vec2.interpolate(v);
    this.y = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle ZZ
	 *
	 * @type {number}
	 */
  get zz() {
    return new Vec2(this.z, this.z);
  }
  set zz(v) {
    v = Vec2.interpolate(v);
    this.z = v.y;
  }


  /**
   * Static methods
   */
  /**
   * Iterpolates a provided anonymous value into a vew Vec3
   *
   * @param {Vec3|array|string|number} v The value to interpolate
   * @returns {Vec3} out
   */
  static interpolate(v) {
    if(v instanceof Vec3) {
      return new Vec3(v.x, v.y, v.z);
    } else if(v instanceof Array && v.length >= 3) {
      return new Vec3(v[0], v[1], v[2]);
    } else if(!isNaN(v)) {
      return new Vec3(v, v, v);
    } else if(typeof v === 'string') {
      const nv = v.split(',');
      if(nv.length >= 3 && !isNaN(nv[0]) && !isNaN(nv[1]) && !isNaN(nv[2])) {
        return new Vec3(Number(nv[0], nv[1], nv[2]));
      }
    } else {
      throw new Error('The passed interpolant could not be parsed into a Vec3');
    }
  }
  
  /**
   * Performs a linear interpolation between two Vec3's
   *
   * @param {Vec3} v1 the first operand
   * @param {Vec3} v2 the second operand
   * @param {Number} d interpolation amount in the range of 0 - 1
   * @returns {Vec3}
   */
  static lerp(v1, v2, d) {
    return new Vec3(
      v1.x + d * (v2.x - v1.x),
      v1.y + d * (v2.y - v1.y),
      v1.z + d * (v2.z - v1.z)
    );
  }
  
  static getAngle(a, b) {
    const _a = a.xy,
          _b = b.xy;

    let len1 = _a.lengthSquared;
    if (len1 > 0) {
      //TODO: evaluate use of glm_invsqrt here?
      len1 = 1 / Math.sqrt(len1);
    }

    let len2 = _b.lengthSquared;
    if (len2 > 0) {
      //TODO: evaluate use of glm_invsqrt here?
      len2 = 1 / Math.sqrt(len2);
    }

    let cosine = (_a.x * _b.x + _a.y * _b.y) * len1 * len2;

    if(cosine > 1.0) {
      return 0;
    }
    else if(cosine < -1.0) {
      return Math.PI;
    } else {
      return Math.acos(cosine);
    }
  }

}

/**
 * A basic 3D Vector class that provides simple algebraic functionality in the form
 * of 3D Vectors.
 *
 * We use Getters/setters for both principle properties (x & y) as well as virtual
 * properties (rotation, length etc.).
 *
 * @class Vec4
 * @author Liam Egan <liam@wethecollective.com>
 * @version 1.0.0
 * @created Jan 07, 2020
 */
Vec4 = class {

	/**
	 * The Vector Class constructor
	 *
	 * @constructor
	 * @param {number} x 				The x coord
	 * @param {number} y 				The y coord
	 */
  constructor(x, y, z, w){
    if(isNaN(x)) x = 0;
    if(isNaN(y)) y = 0;
    if(isNaN(z)) z = 0;
    if(isNaN(w)) w = 0;
    this.reset(x, y, z, w);
  }

  /**
   * Resets the vector coordinates
   *
   * @public
	 * @param {number|Array} x 	The x coord, OR the array to reset to
	 * @param {number} y 				The y coord
   */
	reset(x, y, z, w) {
    if(x instanceof Array && x.length >= 4) {
      this.x = x[0];
      this.y = x[1];
      this.z = x[2];
      this.w = x[3];
    } else {
      this.x = x;
      this.y = y;
      this.z = z;
      this.w = w;
    }
  }
  
  /**
   * Resets the vector coordinates to another vector object
   *
   * @public
	 * @param {Vector} v 				The vector object to use to reset the coordinates
   */
  resetToVector(v) {
    if(v instanceof Vec4) {
      this.x = v.x;
      this.y = v.y;
      this.z = v.z;
      this.w = v.w;
    }
  }

	/**
	 * Clones the vector
	 *
	 * @public
	 * @return {Vec4}					The cloned vector
	 */
  clone() {
    return new Vec4(this.x, this.y, this.z, this.w);
  }

  /**
   * Adds one vector to another.
   *
   * @public
   * @chainable
   * @param  {Vec4}  vector The vector to add to this one
   * @return {Vec4}					Returns itself, modified
   */
  add(vector) {
    this.x += vector.x;
    this.y += vector.y;
    this.z += vector.z;
    this.w += vector.w;
    return this;
  }
  /**
   * Clones the vector and adds the vector to it instead
   *
   * @public
   * @chainable
   * @param  {Vec4}  vector The vector to add to this one
   * @return {Vec4}					Returns the clone of itself, modified
   */
  addNew(vector) {
    return this.clone().add(vector);
  }

  /**
   * Adds a scalar to the vector, modifying both the x and y
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to add to the vector
   * @return {Vec4}					Returns itself, modified
   */
  addScalar(scalar) {
    return this.add(new Vec4(scalar, scalar, scalar, scalar));
  }
  /**
   * Clones the vector and adds the scalar to it instead
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to add to the vector
   * @return {Vec4}					Returns the clone of itself, modified
   */
  addScalarNew(scalar) {
    return this.clone().addScalar(scalar);
  }

  /**
   * Subtracts one vector from another.
   *
   * @public
   * @chainable
   * @param  {Vec4}  vector The vector to subtract from this one
   * @return {Vec4}					Returns itself, modified
   */
  subtract(vector) {
    this.x -= vector.x;
    this.y -= vector.y;
    this.z -= vector.z;
    this.w -= vector.w;
    return this;
  }
  /**
   * Clones the vector and subtracts the vector from it instead
   *
   * @public
   * @chainable
   * @param  {Vec4}  vector The vector to subtract from this one
   * @return {Vec4}					Returns the clone of itself, modified
   */
  subtractNew(vector) {
    return this.clone().subtract(vector);
  }

  /**
   * Subtracts a scalar from the vector, modifying both the x and y
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to subtract from the vector
   * @return {Vec4}					Returns itself, modified
   */
  subtractScalar(scalar) {
    return this.subtract(new Vec4(scalar, scalar, scalar, scalar));
  }
  /**
   * Clones the vector and subtracts the scalar from it instead
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to add to the vector
   * @return {Vec4}					Returns the clone of itself, modified
   */
  subtractScalarNew(scalar) {
    return this.clone().subtractScalar(scalar);
  }

  /**
   * Divides one vector by another.
   *
   * @public
   * @chainable
   * @param  {Vec4}  vector The vector to divide this by
   * @return {Vec4}					Returns itself, modified
   */
  divide(vector) {
    if(vector.x !== 0) {
      this.x /= vector.x
    } else {
      this.x = 0;
    }
    if(vector.y !== 0) {
      this.y /= vector.y
    } else {
      this.y = 0;
    }
    if(vector.z !== 0) {
      this.z /= vector.z;
    } else {
      this.z = 0;
    }
    if(vector.w !== 0) {
      this.w /= vector.w;
    } else {
      this.w = 0;
    }
    return this;
  }
  /**
   * Clones the vector and divides it by the vector instead
   *
   * @public
   * @chainable
   * @param  {Vec4}  vector The vector to divide the clone by
   * @return {Vec4}					Returns the clone of itself, modified
   */
  divideNew(vector) {
    return this.clone().divide(vector);
  }

  /**
   * Divides the vector by a scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to divide both x and y by
   * @return {Vec4}					Returns itself, modified
   */
  divideScalar(scalar) {
    var v = new Vec4(scalar, scalar, scalar, scalar);
    return this.divide(v);
  }
  /**
   * Clones the vector and divides it by the provided scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to divide both x and y by
   * @return {Vec4}					Returns the clone of itself, modified
   */
  divideScalarNew(scalar) {
    return this.clone().divideScalar(scalar);
  }

  /**
   * Multiplies one vector by another.
   *
   * @public
   * @chainable
   * @param  {Vec4}  vector The vector to multiply this by
   * @return {Vec4}					Returns itself, modified
   */
  multiply(vector) {
    this.x *= vector.x;
    this.y *= vector.y;
    this.z *= vector.z;
    this.w *= vector.w;
    return this;
  }
  /**
   * Clones the vector and multiplies it by the vector instead
   *
   * @public
   * @chainable
   * @param  {Vec4}  vector The vector to multiply the clone by
   * @return {Vec4}					Returns the clone of itself, modified
   */
  multiplyNew(vector) {
    return this.clone().multiply(vector);
  }

  /**
   * Multiplies the vector by a scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to multiply both x and y by
   * @return {Vec4}					Returns itself, modified
   */
  multiplyScalar(scalar) {
    var v = new Vec4(scalar, scalar, scalar, scalar);
    return this.multiply(v);
  }
  /**
   * Clones the vector and multiplies it by the provided scalar.
   *
   * @public
   * @chainable
   * @param  {number}  scalar The scalar to multiply both x and y by
   * @return {Vec4}					Returns the clone of itself, modified
   */
  multiplyScalarNew(scalar) {
    return this.clone().multiplyScalar(scalar);
  }

  /**
   * Alias of {@link Vector#multiplyScalar__anchor multiplyScalar}
   */
  scale(scalar) {
    return this.multiplyScalar(scalar);
  }
  /**
   * Alias of {@link Vector#multiplyScalarNew__anchor multiplyScalarNew}
   */
  scaleNew(scalar) {
    return this.multiplyScalarNew(scalar);
  }

  rotateX(origin, radian) {
    
    const s = Math.sin(radian);
    const c = Math.cos(radian);
    
    // Translate to the origin
    const translated = this.subtractNew(origin);
    
    // Rotate
    const rotated = translated.clone();
    rotated.y = rotated.y * c - rotated.z * s;
    rotated.z = rotated.y * s + rotated.z * c;

    // Translate back
		this.y = rotated.y + origin.y;
    this.z = rotated.z + origin.z;

  	return this;
  }
  
  rotateXNew(radian) {
    return this.clone().rotateX(radian);
  }

  rotateY(origin, radian) {
    
    const s = Math.sin(radian);
    const c = Math.cos(radian);
    
    // Translate to the origin
    const translated = this.subtractNew(origin);
    
    // Rotate
    const rotated = translated.clone();
    rotated.x = rotated.z * s + rotated.z * c;
    rotated.z = rotated.z * c - rotated.x * s;

    // Translate back
		this.x = rotated.x + origin.x;
    this.z = rotated.z + origin.z;

  	return this;
  }
  
  rotateyNew(radian) {
    return this.clone().rotateY(radian);
  }

  rotateZ(origin, radian) {
    
    const s = Math.sin(radian);
    const c = Math.cos(radian);
    
    // Translate to the origin
    const translated = this.subtractNew(origin);
    
    // Rotate
    const rotated = translated.clone();
    rotated.x = rotated.x * c - rotated.y * s;
    rotated.y = rotated.x * s + rotated.y * c;

    // Translate back
		this.x = rotated.x + origin.x;
    this.y = rotated.y + origin.y;

  	return this;
  }
  
  rotateZNew(radian) {
    return this.clone().rotateZ(radian);
  }
  
  transformByMat4(m) {
    if(m.array) m = m.array; // This just transforms the matrix to an array.
    if(m instanceof Array && m.length >= 16) {
      const o = this.clone();
      this.x = ( m[0] * o.x + m[4] * o.y + m[8] * o.z + m[12] ) / this.w;
      this.y = ( m[1] * o.x + m[5] * o.y + m[9] * o.z + m[13] ) / this.w;
      this.z = ( m[2] * o.x + m[6] * o.y + m[10] * o.z + m[14] ) / this.w;
      this.w = ( m[3] * o.x + m[7] * o.y + m[11] * o.z + m[15] ) / this.w;
    }
    return this;
  }
  
  transformByMat4New(m) {
    return this.clone().transformByMat4(m);
  }
  
  transformByQuat(q) {
    if(q.array) q = q.array; // This just transforms the quaternion to an array.
    if(q instanceof Array && q.length >= 4) {
      const uv = new Vec4(
        q.w * this.x + q.y * this.z - q.z * this.y,
        q.w * this.y + q.z * this.x - q.x * this.z,
        q.w * this.z + q.x * this.y - q.y * this.x,
        -q.x * this.x - q.y * this.y - q.z * this.z
      );
      
      this.x = uv.x * q.w + uv.w * -q.x + uv.y * -q.z - uv.z * -q.y;
      this.y = uv.y * q.w + uv.w * -q.y + uv.z * -q.x - uv.x * -q.z;
      this.z = uv.z * q.w + uv.w * -q.z + uv.x * -q.y - uv.y * -q.x;
    }
    return this;
  }
  
  transformByQuatNew(q) {
    return this.clone().transformByQuat(q);
  }
  
	/**
	 * Negates the vector.
	 *
	 * @public
	 * @chainable
	 * @return {Vec4}					Returns itself, modified
	 */
  negate() {
    return this.multiplyScalar(-1.);
  }
  
	/**
	 * Clones the vector and negates it.
	 *
	 * @public
	 * @chainable
	 * @return {Vec4}					Returns itself, modified
	 */
  negateNew() {
    return this.multiplyScalarNew(-1.);
  }
  
	/**
	 * Inverses the vector.
	 *
	 * @public
	 * @chainable
	 * @return {Vec4}					Returns itself, modified
	 */
  inverse() {
    this.x = 1./this.x;
    this.y = 1./this.y;
    this.z = 1./this.z;
    this.w = 1./this.w;
    return this;
  }
  
	/**
	 * Clones the vector and then inverses it.
	 *
	 * @public
	 * @chainable
	 * @return {Vec4}					Returns itself, modified
	 */
  inverseNew() {
    const c = new Vector();
    c.x = 1./this.x;
    c.y = 1./this.y;
    c.z = 1./this.z;
    c.w = 1./this.w;
    return c;
  }

	/**
	 * Normalises the vector down to a length of 1 unit
	 *
	 * @public
	 * @chainable
	 * @return {Vec4}					Returns itself, modified
	 */
	normalise() {
    const l = this.length;
    if(l === 0) {
      this.x = 0;
      this.y = 0;
      this.z = 0;
      this.w = 1;
    } else {
      this.divideScalar(l)
    }
		return this;
	}
	/**
	 * Clones the vector and normalises it
	 *
	 * @public
	 * @chainable
	 * @return {Vec4}					Returns a clone of itself, modified
	 */
	normaliseNew() {
		return this.clone().normalise();
	}

	/**
	 * Calculates the distance between this and the supplied vector
	 *
	 * @param  {Vec4} vector The vector to calculate the distance from
	 * @return {number}        The distance between this and the supplied vector
	 */
	distance(vector) {
		return this.subtractNew(vector).length;
	}

	/**
	 * Calculates the distance on the X axis between this and the supplied vector
	 *
	 * @param  {Vec4} vector The vector to calculate the distance from
	 * @return {number}        The distance, along the x axis, between this and the supplied vector
	 */
	distanceX(vector) {
		return this.x - vector.x;
	}

	/**
	 * Calculated the distance on the Y axis between this and the supplied vector
	 *
	 * @param  {Vec4} vector The vector to calculate the distance from
	 * @return {number}        The distance, along the y axis, between this and the supplied vector
	 */
	distanceY(vector) {
		return this.y - vector.y;
	}

	/**
	 * Calculated the distance on the Z axis between this and the supplied vector
	 *
	 * @param  {Vec4} vector The vector to calculate the distance from
	 * @return {number}        The distance, along the y axis, between this and the supplied vector
	 */
	distanceZ(vector) {
		return this.z - vector.z;
	}


	/**
	 * Calculates the dot product between this and a supplied vector
	 *
	 * @example
	 * // returns -14
	 * new Vector(2, -3).dot(new Vector(-4, 2))
	 * new Vector(-4, 2).dot(new Vector(2, -3))
	 * new Vector(2, -4).dot(new Vector(-3, 2))
	 *
	 * @param  {Vec4} vector The vector object against which to calculate the dot product
	 * @return {number}        The dot product of the two vectors
	 */
	dot(vector) {
		return (this.x * vector.x) + (this.y * vector.y) + (this.z * vector.z) + (this.w * vector.w);
	}

	/**
	 * Calculates the cross product between this and two other supplied vectors
	 *
	 * @example
	 * // returns -2
	 * new Vector(2, -3).cross(new Vector(-4, 2))
	 * new Vector(-4, 2).cross(new Vector(2, -3))
	 * // returns 2
	 * new Vector(2, -4).cross(new Vector(-3, 2))
	 *
	 * @param  {Vec4} vector The vector object against which to calculate the cross product
	 * @return {Vec4}        The cross product of the two vectors
	 */
	cross(v, w) {
    const u = this.clone();
    
    const A = (v[0] * w[1]) - (v[1] * w[0]),
          B = (v[0] * w[2]) - (v[2] * w[0]),
          C = (v[0] * w[3]) - (v[3] * w[0]),
          D = (v[1] * w[2]) - (v[2] * w[1]),
          E = (v[1] * w[3]) - (v[3] * w[1]),
          F = (v[2] * w[3]) - (v[3] * w[2]),
          G = u[0],
          H = u[1],
          I = u[2],
          J = u[3];
    
    return new Vec4(
      (H * F) - (I * E) + (J * D),
      -(G * F) + (I * C) - (J * B),
      (G * E) - (H * C) + (J * A),
      -(G * D) + (H * B) - (I * A)
    );
	}
  
  crossNew() {
    this.clone().cross();
  }
  
  ceil() {
    this.x = Math.ceil(this.x);
    this.y = Math.ceil(this.y);
    this.z = Math.ceil(this.z);
    this.w = Math.ceil(this.w);
    return this;
  }
  
  ceilNew() {
    return this.clone().ceil();
  }
  
  floor() {
    this.x = Math.floor(this.x);
    this.y = Math.floor(this.y);
    this.z = Math.floor(this.z);
    this.w = Math.floor(this.w);
    return this;
  }
  
  floorNew() {
    return this.clone().floor();
  }
  
  round() {
    this.x = Math.round(this.x);
    this.y = Math.round(this.y);
    this.z = Math.round(this.z);
    this.w = Math.round(this.w);
    return this;
  }
  
  roundNew() {
    return this.clone().round();
  }
  
  fract() {
    this.x -= Math.floor(this.x);
    this.y -= Math.floor(this.y);
    this.z -= Math.floor(this.z);
    this.w -= Math.floor(this.w);
    return this;
  }
  
  fractNew() {
    return this.clone().fract();
  }


  /**
   * Getters and setters
   */

  /**
   * (getter/setter) The x value of the vector.
   *
   * @type {number}
   * @default 0
   */
  set x(x) {
    if(typeof x == 'number') {
      this._x = x;
    } else {
      throw new TypeError('X should be a number');
    }
  }
  get x() {
    return this._x || 0;
  }

 /**
	* (getter/setter) The y value of the vector.
	*
	* @type {number}
	* @default 0
	*/
  set y(y) {
    if(typeof y == 'number') {
      this._y = y;
    } else {
      throw new TypeError('Y should be a number');
    }
  }
  get y() {
    return this._y || 0;
  }

 /**
	* (getter/setter) The y value of the vector.
	*
	* @type {number}
	* @default 0
	*/
  set z(z) {
    if(typeof z == 'number') {
      this._z = z;
    } else {
      throw new TypeError('Y should be a number');
    }
  }
  get z() {
    return this._z || 0;
  }

 /**
	* (getter/setter) The y value of the vector.
	*
	* @type {number}
	* @default 0
	*/
  set w(w) {
    if(typeof w == 'number') {
      this._w = w;
    } else {
      throw new TypeError('W should be a number');
    }
  }
  get w() {
    return this._w || 0;
  }

	/**
	* (getter/setter) The length of the vector presented as a square. If you're using
	* length for comparison, this is quicker.
	*
	* @type {number}
	* @default 0
	*/
  set lengthSquared(length) {
    var factor;
    if(typeof length == 'number') {
      factor = length / this.lengthSquared;
      this.multiplyScalar(factor);
    } else {
      throw new TypeError('length should be a number');
    }
  }
  get lengthSquared() {
    return (this.x * this.x) + (this.y * this.y) + (this.z * this.z) + (this.w * this.w);
  }

	/**
	* (getter/setter) The length of the vector
	*
	* @type {number}
	* @default 0
	*/
  set length(length) {
    var factor;
    if(typeof length == 'number') {
      factor = length / this.length;
      this.multiplyScalar(factor);
    } else {
      throw new TypeError('length should be a number');
    }
  }
  get length() {
    return Math.sqrt(this.lengthSquared);
  }

	/**
	 * (getter/setter) Vector width.
   * Alias of {@link Vector#x x}
	 *
	 * @type {number}
	 */
	set width(w) {
		this.x = w;
	}
	get width() {
		return this.x;
	}

	/**
	 * (getter/setter) Vector height.
   * Alias of {@link Vector#x x}
	 *
	 * @type {number}
	 */
	set height(h) {
		this.y = h;
	}
	get height() {
		return this.y;
	}

	/**
	 * (getter/setter) Vector height.
   * Alias of {@link Vector#x x}
	 *
	 * @type {number}
	 */
	set depth(h) {
		this.z = h;
	}
	get depth() {
		return this.z;
	}

	/**
	 * (getter) Vector area.
	 * @readonly
	 *
	 * @type {number}
	 */
	get area() {
		return this.x * this.y * this.z * this.w;
	}

	/**
	 * (getter) Returns the basic array representation of this vector.
	 * @readonly
	 *
	 * @type {number}
	 */
  get array() {
    return [this.x, this.y, this.z, this.w];
  }
  
	/**
	 * (getter/sette) Swizzle XYZW
	 *
	 * @type {Vec4}
	 */
  get xyzw() {
    return new Vec4(this.x, this.y, this.z, this.w);
  }
  set xyzw(v) {
    if(v instanceof Vec4) {
      this.resetToVector(v);
    } else if(v instanceof Array && v.length >= 4) {
      this.reset(v);
    } else {
      throw new Error('input should be of type Vector');
    }
  }
  
	/**
	 * (getter/sette) Swizzle XYZW
	 *
	 * @type {Vec4}
	 */
  get yzwx() {
    return new Vec4(this.y, this.z, this.w, this.x);
  }
  set yzwx(v) {
    this.xyzw = Vec4.interpolate(v).yzwx;
  }
  
	/**
	 * (getter/sette) Swizzle XYZW
	 *
	 * @type {Vec4}
	 */
  get zwxy() {
    return new Vec4(this.z, this.w, this.x, this.y);
  }
  set zwxy(v) {
    this.xyzw = Vec4.interpolate(v).zwxy;
  }
  
	/**
	 * (getter/sette) Swizzle XYZW
	 *
	 * @type {Vec4}
	 */
  get wxyz() {
    return new Vec4(this.w, this.x, this.y, this.z);
  }
  set wxyz(v) {
    this.xyzw = Vec4.interpolate(v).wxyz;
  }
  
  // I'm skipping all the silly combinations of 4 here because they're largely useless
  
	/**
	 * (getter/sette) Swizzle YZX
	 *
	 * @type {Vec3}
	 */
  get xyz() {
    return new Vec3(this.x, this.y, this.z);
  }
  set xyz(v) {
    v = Vec3.interpolate(v);
    this.x = v.x;
    this.y = v.y;
    this.z = v.z;
  }
  
	/**
	 * (getter/sette) Swizzle YZX
	 *
	 * @type {Vec3}
	 */
  get yzx() {
    return new Vec3(this.y, this.z, this.x);
  }
  set yzx(v) {
    v = Vec3.interpolate(v);
    this.x = v.y;
    this.y = v.z;
    this.z = v.x;
  }
  
	/**
	 * (getter/sette) Swizzle ZXY
	 *
	 * @type {Vec3}
	 */
  get zxy() {
    return new Vec3(this.z, this.x, this.y);
  }
  set zxy(v) {
    v = Vec3.interpolate(v);
    this.x = v.z;
    this.y = v.x;
    this.z = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle XX
	 *
	 * @type {number}
	 */
  get xx() {
    return new Vec2(this.x, this.x);
  }
  set xx(v) {
    v = Vec2.interpolate(v);
    this.x = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle XY
	 *
	 * @type {Vec2}
	 */
  get xy() {
    return new Vec2(this.x, this.y);
  }
  set xy(v) {
    v = Vec2.interpolate(v);
    this.x = v.x;
    this.y = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle XY
	 *
	 * @type {Vec2}
	 */
  get xz() {
    return new Vec2(this.x, this.z);
  }
  set xz(v) {
    v = Vec2.interpolate(v);
    this.x = v.x;
    this.z = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle XY
	 *
	 * @type {Vec2}
	 */
  get xw() {
    return new Vec2(this.x, this.w);
  }
  set xw(v) {
    v = Vec2.interpolate(v);
    this.x = v.x;
    this.z = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle YX
	 *
	 * @type {number}
	 */
  get yx() {
    return new Vec2(this.y, this.x);
  }
  set yx(v) {
    v = Vec2.interpolate(v);
    this.x = v.y;
    this.y = v.x;
  }
  
	/**
	 * (getter/sette) Swizzle YY
	 *
	 * @type {number}
	 */
  get yy() {
    return new Vec2(this.y, this.y);
  }
  set yy(v) {
    v = Vec2.interpolate(v);
    this.y = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle YZ
	 *
	 * @type {Vec2}
	 */
  get yz() {
    return new Vec2(this.y, this.z);
  }
  set yz(v) {
    v = Vec2.interpolate(v);
    this.y = v.x;
    this.z = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle YZ
	 *
	 * @type {Vec2}
	 */
  get yw() {
    return new Vec2(this.y, this.w);
  }
  set yw(v) {
    v = Vec2.interpolate(v);
    this.y = v.x;
    this.w = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle zx
	 *
	 * @type {Vec2}
	 */
  get zx() {
    return new Vec2(this.z, this.x);
  }
  set zx(v) {
    v = Vec2.interpolate(v);
    this.z = v.x;
    this.x = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle ZY
	 *
	 * @type {number}
	 */
  get zy() {
    return new Vec2(this.z, this.y);
  }
  set zy(v) {
    v = Vec2.interpolate(v);
    this.z = v.y;
    this.y = v.x;
  }
  
	/**
	 * (getter/sette) Swizzle ZZ
	 *
	 * @type {number}
	 */
  get zz() {
    return new Vec2(this.z, this.z);
  }
  set zz(v) {
    v = Vec2.interpolate(v);
    this.z = v.y;
  }
  
	/**
	 * (getter/sette) Swizzle XY
	 *
	 * @type {Vec2}
	 */
  get zw() {
    ret.........完整代码请登录后点击上方下载按钮下载查看

网友评论0