Source: linalg.js

/** @module linalg */

/**
 * compute the length of a 2d vector
 * @param {Array<float>} vec - a vector whose length we want to compute
 * @returns {float} the length of the vector
 */
export function vec_len(vec) {
  return Math.sqrt(dot(vec, vec));
}

/**
 * computes the 2d dot product
 * @param {Array<float>} u - first vector
 * @param {Array<float>} v - second vector
 * @returns {float} <u, v> (u dot v)
 */
export function dot(u, v) {
  return u[0]*v[0] + u[1]*v[1];
}

/**
 * projection of the 2d vector u on to v
 * @param {Array<float>} u - first vector
 * @param {Array<float>} v - second vector (onto which to project the first)
 * @returns {Array<float>} the component of the first vector in the direction of the second
 */
export function proj(u, v) {
  const unit_v = normalize(v);
  const dot_prod = u[0]*unit_v[0] + u[1]*unit_v[1];
  return [dot_prod*unit_v[0], dot_prod*unit_v[1]];
}

/**
 * computes a orthogonal vector
 * @param {Array<float>} vec - a vector on which to calculate the orthogonal vector
 * @returns {Array<float>} a vector orthogonal of the original vector
 */
export function normal_vec(vec, clockwise=false) {
  return clockwise ? [vec[1], -vec[0]] : [-vec[1], vec[0]];
}

/**
 * normalizes a vector to a specific length
 * @param {Array<float>} vec - the vector you want to scale
 * @param {float} final_length - the length you want the final vector to end up
 * @returns {Array<float>} the normalized vector
 */
export function normalize(vec, final_length=1) {
  const adjusted_vec = [];
  const length_adj = vec_len(vec)/final_length;
  for (const component of vec) {
    adjusted_vec.push(component/length_adj);
  }
  return adjusted_vec;
}

/**
 * scale the 2d vector v by a scalar a
 * @param {float} a - the scalar
 * @param {Array<float>} v - the vector
 * @returns {Array<float>} av
 */
export function scale(a, v) {
  return [a*v[0], a*v[1]];
}

/**
 * 2d vector addition
 * @param {float|Array<float>} v1 
 * @param {Array<float>} v2 
 * @returns v1+v2
 */
export function add(v1, v2) {
  if (!isNaN(v1)) {
    v1 = [parseFloat(v1), parseFloat(v1)];
  }  // incase someone passes in a scalar
  return [v1[0]+v2[0], v1[1]+v2[1]];
}

/**
 * 2d vector subtraction
 * @param {Array<float>} v1
 * @param {Array<float>} v2
 * @returns v1-v2
 */
export function sub(v1, v2) {
  return [v1[0]-v2[0], v1[1]-v2[1]];
}

/**
 * calculate the linear combination of a1*v1+a2*v2, also known as the matrix vector product
 * @param {Array<float>} v1 - vector1
 * @param {Array<float>} v2 - vector2
 * @param {float} a1 - scalar1
 * @param {float} a2 - scalar2
 * @returns {Array<float>} the result of a1*v1+a2*v2
 */
export function linear_comb(v1, v2, a1=1, a2=1) {
  return add(scale(a1, v1), scale(a2, v2));
}

/**
 * determinant of a column major 2x2 matrix
 * @param {Array<float>} v1 - first column
 * @param {Array<float>} v2 - second column
 * @returns {float} det([v1, v2])
 */
export function det(v1, v2) {
  return v1[0]*v2[1]-v1[1]*v2[0];
}

/**
 * computes the inverse of a column major 2x2 matrix
 * @param {Array<float>} v1 - first column
 * @param {Array<float>} v2 - second column
 * @returns {Array<Array<float>>} the inverse matrix
 */
export function inv(v1, v2) {
  const inv_det = 1/det(v1, v2);
  return [scale(inv_det, [v2[1], -v1[1]]), scale(inv_det, [-v2[0], v1[0]])];
}