Source: /home/dsfig/components/vec2-recycle/index.js

/home/dsfig/components/vec2-recycle/index.js

// recyclable vector2 library
// based on https://github.com/tmpvar/vec2.js

const cache = []

export function create(x = 0, y = 0) {
    if (cache.length) {
        const v = cache.pop()
        v[0] = x
        v[1] = y
        return v
    } else {
        return [x, y]
    }
}

/**
 * get cache size
 * @returns {number}
 */
export function cacheSize() {
    return cache.length
}

/** remove all entries from cache */
export function cacheClean() {
    while (cache.length) {
        cache.pop()
    }
}

/**
 * shrink cache to a certain number of entries
 * @param {number} length
 */
export function shrinkCache(length) {
    const shrink = cache.length - length
    for (let i = 0; i < shrink; i++) {
        cache.pop()
    }
}

/**
 * set the value of vec2
 * @param {number[]} v
 * @param {number} x
 * @param {number} [y] - use x if not set
 * @returns {number[]}
 */
export function set(v, x, y = x) {
    v[0] = x
    v[1] = y
    return v
}

/**
 * copy source values to destination
 * @param {number[]} destination
 * @param {number[]} source
 * @returns {number[]} destination
 */
export function copy(destination, source) {
    destination[0] = source[0]
    destination[1] = source[1]
    return destination
}

/**
 * @param {number[]} v
 */
export function clone(v) {
    return create(v[0], v[1])
}

/**
 * recycle the vector for future use
 * @param {number[]} v
 */
export function recycle(v) {
    cache.push(v)
}

/**
 * @param {number[]} v
 * @param {boolean} [clone] leave original vec2 and return a cloned, negated version
 * @returns {number[]}
 */
export function negate(v, clone) {
    if (clone) {
        const v2 = create(v[0], v[1])
        v2[0] = -v[0]
        v2[1] = -v[1]
        return v2
    } else {
        v[0] = -v[0]
        v[1] = -v[1]
        return v
    }
}

/**
 * v1 + v2
 * note: if clone is false, v1 is returned with the result
 * @param {number[]} v1
 * @param {number[]} v2
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v1 or the cloned vector
 */
export function add(v1, v2, clone) {
    if (clone) {
        return create(v1[0] + v2[0], v1[1] + v2[1])
    } else {
        v1[0] += v2[0]
        v1[1] += v2[1]
        return v1
    }
}

/**
 * v1 - v2
 * @param {number[]} v1
 * @param {number[]} v2
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v1 or the cloned vector
 */
export function subtract(v1, v2, clone) {
    if (clone) {
        return create(v1[0] - v2[0], v1[1] - v2[1])
    } else {
        v1[0] -= v2[0]
        v1[1] -= v2[1]
        return v1
    }
}

/**
 * v1 * v2
 * @param {number[]} v1
 * @param {number[]} v2
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v1 or the cloned vector
 */
export function multiply(v1, v2, clone) {
    if (clone) {
        return create(v1[0] * v2[0], v1[1] * v2[1])
    } else {
        v1[0] *= v2[0]
        v1[1] *= v2[1]
        return v1
    }
}

/**
 * v * scalar
 * @param {number[]} v
 * @param {number} scalar
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v or the cloned vector
 */
export function multiplyScalar(v, scalar, clone) {
    if (clone) {
        return create(v[0] * scalar, v[1] * scalar)
    } else {
        v[0] *= scalar
        v[1] *= scalar
        return v
    }
}

/**
 * v1 / v2
 * @param {number[]} v1
 * @param {number[]} v2
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v1 or the cloned vector
 */
export function divide(v1, v2, clone) {
    if (clone) {
        return create(v1[0] / v2[0], v1[1] / v2[1])
    } else {
        v1[0] /= v2[0]
        v1[1] /= v2[1]
        return v1
    }
}

/**
 * v / scalar
 * @param {number[]} v
 * @param {number} scalar
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v or the cloned vector
 */
export function divideScalar(v, scalar, clone) {
    if (clone) {
        return create(v[0] / scalar, v[1] / scalar)
    } else {
        v[0] /= scalar
        v[1] /= scalar
        return v
    }
}

/**
 * rotates a vector
 * @param {number[]} v
 * @param {number} radians
 * @param {options} [options]
 * @param {boolean} [options.inverse]
 * @param {boolean} [options.clone] returns a new vector with the result
 * @returns {number[]} v or the cloned vector
 */
export function rotate(v, radians, options = {}) {
    const cos = Math.cos(radians)
    const sin = Math.sin(radians)
    const inverse = options.inverse ? -1 : 1
    const rx = cos * v[0] - (inverse * sin) * v[1]
    const ry = (inverse * sin) * v[0] + cos * v[1]

    if (options.clone) {
        return create(rx, ry)
    } else {
        v[0] = rx
        v[1] = ry
        return v
    }
}

/**
 * length of vector
 * @param {number[]} v
 * @returns {number}
 */
export function length(v) {
    return Math.sqrt(v[0] * v[0] + v[1] * v[1])
}

/**
 * squared length of vector
 * @param {number[]} v
 * @returns {number}
 */
export function lengthSquared(v) {
    return v[0] * v[0] + v[1] * v[1]
}

/**
 * distance between two vectors
 * @param {number[]} v1
 * @param {number[]} v2
 * @returns {number}
 */
export function distance(v1, v2) {
    return Math.sqrt(Math.pow(v1[0] - v2[0], 2) + Math.pow(v1[1] - v2[1], 2))
}

/**
 * distance squared between two vectors
 * @param {number[]} v1
 * @param {number[]} v2
 * @returns {number}
 */
export function distanceSquared(v1, v2) {
    return Math.pow(v1[0] - v2[0], 2) + Math.pow(v1[1] - v2[1], 2)
}

/**
 * returns closest vec2 to v in array
 * @param {number[]} v
 * @param {number[]} array
 * @returns {number[]}
 */
export function nearest(v, array) {
    let closestDistance = Infinity
    let closest = null
    let current
    for (const v2 of array) {
        current = distanceSquared(v, v2)
        if (current < closestDistance) {
            closestDistance = current
            closest = v2
        }
    }
    return closest
}

/**
 * converts to unit vector
 * @param {number[]} v
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v1 or the cloned vector
 */
export function normalize(v, clone) {
    const len = length(v)
    const invertedLength = len === 0 ? 0 : 1 / len
    const x = v[0] * invertedLength
    const y = v[1] * invertedLength
    if (clone) {
        return create(x, y)
    } else {
        v[0] = x
        v[1] = y
        return v
    }
}

/**
 * @param {number[]} v1
 * @param {number[]} v2
 * @param {number} [precision] difference between components to still be equal
 */
export function equal(v1, v2, precision) {
    if (precision) {
        return Math.abs(v1[0] - v2[0]) < precision && Math.abs(v1[1] - v2[1]) < precision
    } else {
        return v1[0] === v2[0] && v1[1] === v2[1]
    }
}

/**
 * @param {number[]} v
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v or the cloned vector
 */
export function abs(v, clone) {
    if (clone) {
        return create(Math.abs(v[0]), Math.abs(v[1]))
    } else {
        v[0] = Math.abs(v[0])
        v[1] = Math.abs(v[1])
        return v
    }
}

/**
 * clamp vector using min/max values or vectors
 * @param {number[]} v
 * @param {(number|number[])} [min]
 * @param {(number|number[])} [max]
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v or the cloned vector
 */
export function clamp(v, min=null, max=null, clone) {
    if (clone) {
        const v2 = create()
        if (min !== null) {
            if (Array.isArray(min)) {
                v2[0] = v[0] < min[0] ? min[0] : v[0]
                v2[1] = v[1] < min[1] ? min[1] : v[1]
            } else {
                v2[0] = v[0] < min ? min : v[0]
                v2[1] = v[1] < min ? min : v[1]
            }
        }
        if (max !== null) {
            if (Array.isArray(max)) {
                v2[0] = v[0] > max[0] ? max[0] : v[0]
                v2[1] = v[1] > max[1] ? max[1] : v[1]
            } else {
                v2[0] = v[0] > max ? max : v[0]
                v2[1] = v[1] > max ? max : v[1]
            }
        }
        return v2
    } else {
        if (min !== null) {
            if (Array.isArray(min)) {
                v[0] = v[0] < min[0] ? min[0] : v[0]
                v[1] = v[1] < min[1] ? min[1] : v[1]
            } else {
                v[0] = v[0] < min ? min : v[0]
                v[1] = v[1] < min ? min : v[1]
            }
        }
        if (max !== null) {
            if (Array.isArray(max)) {
                v[0] = v[0] > max[0] ? max[0] : v[0]
                v[1] = v[1] > max[1] ? max[1] : v[1]
            } else {
                v[0] = v[0] > max ? max : v[0]
                v[1] = v[1] > max ? max : v[1]
            }
        }
        return v
    }
}

/**
 * linear interpolation between two vectors
 * @param {number[]} v1
 * @param {number[]} v2
 * @param {number} percent - between 0 and 1
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v1 or the cloned vector
 */
export function lerp(v1, v2, percent, clone) {
    const v = subtract(v2, v1, true)
    multiplyScalar(v, percent)
    if (clone) {
        return add(v, v1)
    } else {
        return add(v1, v)
    }
}

/**
 * @param {number[]} v
 * @param {boolean} [clone] return a new vector with the result
 * @returns {number[]} v or the cloned vector
 */
export function normal(v, clone) {
    if (clone) {
        return create(-v[1], v[0])
    } else {
        const swap = v[0]
        v[0] = -v[1]
        v[1] = swap
        return v
    }
}

/**
 * @param {number[]} v1
 * @param {number[]} v2
 * @returns {number}
 */
export function dot(v1, v2) {
    return v1[0] * v2[0] + v1[1] * v2[1]
}

/**
 * @param {number[]} v1
 * @param {number[]} v2
 * @returns {number}
 */
export function dotPerpendicular(v1, v2) {
    return v1[0] * v2[1] - v1[1] * v2[0]
}

/**
 * angle between v1 and v2
 * @param {number[]} v1
 * @param {number[]} v2
 * @returns {number}
 */
export function angle(v1, v2) {
    return Math.atan2(v2[1] - v1[1], v2[0] - v1[0])
}

/**
 * @param {number[]} point
 * @param {number[]} start
 * @param {number[]} end
 * @returns {boolean}
 */
export function isPointOnLine(point, start, end) {
    return (start[1] - point[1]) * (start[0] - end[0]) === (start[1] - end[1]) * (start[0] - point[0])
}