UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

252 lines (214 loc) 6.57 kB
import { assert } from "../../assert.js"; import { UNIT_DIMENSION_COUNT } from "./UnitDimension.js"; /** * Represents a physical unit as a vector of exponents over the 7 SI base dimensions. * * Composition rules: * - multiplying two units adds their exponents component-wise (e.g. m * s = m·s, m * m = m^2) * - dividing two units subtracts their exponents (e.g. m / s = m/s, m / m = dimensionless) * - raising a unit to a power scales every exponent by that power (e.g. m^3 = volume) * * Storage uses a fixed-size {@link Float64Array} so fractional exponents (e.g. sqrt of a unit) * are representable, and so that composition can be performed without per-call allocation. */ export class UnitMatrix { constructor() { /** * Exponent for each SI base dimension, indexed by {@link UnitDimension}. * @type {Float64Array} */ this.exponents = new Float64Array(UNIT_DIMENSION_COUNT); } /** * Set the exponent of a single base dimension. * @param {number} dimension Index from {@link UnitDimension}. * @param {number} value */ set_exponent(dimension, value) { assert.isNonNegativeInteger(dimension, 'dimension'); assert.lessThan(dimension, UNIT_DIMENSION_COUNT, 'dimension overflow'); assert.isNumber(value, 'value'); this.exponents[dimension] = value; } /** * Read the exponent of a single base dimension. * @param {number} dimension Index from {@link UnitDimension}. * @returns {number} */ get_exponent(dimension) { assert.isNonNegativeInteger(dimension, 'dimension'); assert.lessThan(dimension, UNIT_DIMENSION_COUNT, 'dimension overflow'); return this.exponents[dimension]; } /** * Bulk-set every dimension exponent in a single call. * @param {number} length * @param {number} mass * @param {number} time * @param {number} electric_current * @param {number} temperature * @param {number} amount_of_substance * @param {number} luminous_intensity * @returns {this} */ set(length, mass, time, electric_current, temperature, amount_of_substance, luminous_intensity) { const e = this.exponents; e[0] = length; e[1] = mass; e[2] = time; e[3] = electric_current; e[4] = temperature; e[5] = amount_of_substance; e[6] = luminous_intensity; return this; } /** * Reset to dimensionless (every exponent is 0). */ set_zero() { this.exponents.fill(0); } /** * Copy values from another unit matrix. * @param {UnitMatrix} other */ copy(other) { this.exponents.set(other.exponents); } /** * Allocate a new unit matrix with the same exponents. * @returns {UnitMatrix} */ clone() { const r = new UnitMatrix(); r.copy(this); return r; } /** * Strict structural equality between two unit matrices. * Two matrices are equal when every exponent matches exactly. * @param {UnitMatrix} other * @returns {boolean} */ equals(other) { const a = this.exponents; const b = other.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { if (a[i] !== b[i]) { return false; } } return true; } /** * Whether this matrix represents a dimensionless quantity (all exponents are 0). * @returns {boolean} */ is_dimensionless() { const e = this.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { if (e[i] !== 0) { return false; } } return true; } /** * Multiply this matrix by another in-place (this *= other). * Adds exponents component-wise. * @param {UnitMatrix} other */ multiply(other) { const e = this.exponents; const o = other.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { e[i] += o[i]; } } /** * Divide this matrix by another in-place (this /= other). * Subtracts exponents component-wise. * @param {UnitMatrix} other */ divide(other) { const e = this.exponents; const o = other.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { e[i] -= o[i]; } } /** * Store the product a * b into this matrix (this = a * b). * Safe to call with this === a or this === b. * @param {UnitMatrix} a * @param {UnitMatrix} b */ multiply_matrices(a, b) { const out = this.exponents; const ax = a.exponents; const bx = b.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { out[i] = ax[i] + bx[i]; } } /** * Store the quotient a / b into this matrix (this = a / b). * Safe to call with this === a or this === b. * @param {UnitMatrix} a * @param {UnitMatrix} b */ divide_matrices(a, b) { const out = this.exponents; const ax = a.exponents; const bx = b.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { out[i] = ax[i] - bx[i]; } } /** * Raise this matrix to a power in-place (this **= n). * @param {number} n */ power(n) { assert.isNumber(n, 'n'); const e = this.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { e[i] *= n; } } /** * Store a ** n into this matrix (this = a ** n). * Safe to call with this === a. * @param {UnitMatrix} a * @param {number} n */ power_of_matrix(a, n) { assert.isNumber(n, 'n'); const out = this.exponents; const ax = a.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { out[i] = ax[i] * n; } } /** * Invert in-place: every exponent is negated, so this becomes 1 / this. */ invert() { const e = this.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { e[i] = -e[i]; } } /** * Store 1 / a into this matrix. * Safe to call with this === a. * @param {UnitMatrix} a */ invert_matrix(a) { const out = this.exponents; const ax = a.exponents; for (let i = 0; i < UNIT_DIMENSION_COUNT; i++) { out[i] = -ax[i]; } } }