UNPKG

@litecanvas/utils

Version:

Utilities to help build litecanvas games

170 lines (147 loc) 3.74 kB
/** * Further adapted from p5 Noise module * https://github.com/processing/p5.js/blob/v1.11.1/src/math/noise.js */ // import litecanvas types import "litecanvas" /** * Constants for Perlin noise calculations. */ const PERLIN_YWRAPB = 4 const PERLIN_YWRAP = 1 << PERLIN_YWRAPB const PERLIN_ZWRAPB = 8 const PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB const PERLIN_SIZE = 4095 /** * Scaled cosine function used for smoothing transitions in Perlin noise. * @param {number} i - Input value. * @returns {number} Scaled cosine value. */ const scaled_cosine = (i) => 0.5 * (1.0 - Math.cos(i * Math.PI)) /** * Class for generating Perlin noise, a type of gradient noise often used in procedural generation. */ export class Noise { /** * Array to store Perlin noise values. * @type {number[]} * @private */ _p = [] /** * Number of octaves for the Perlin noise. Higher values create more detail. * @type {number} * @private */ _po = 4 /** * Amplitude falloff factor for Perlin noise. Determines the reduction of amplitude per octave. * @type {number} * @private */ _pf = 0.5 /** * @type {LitecanvasInstance} * @private */ _e = null /** * @param {LitecanvasInstance} engine */ constructor(engine) { this._e = engine || globalThis this.noiseSeed() } /** * Generates Perlin noise for the given coordinates. * @param {number} x - X-coordinate. * @param {number} [y=0] - Y-coordinate (default is 0). * @param {number} [z=0] - Z-coordinate (default is 0). * @returns {number} A noise value in the range [0, 1). */ noise(x, y = 0, z = 0) { if (x < 0) { x = -x } if (y < 0) { y = -y } if (z < 0) { z = -z } let xi = Math.floor(x), yi = Math.floor(y), zi = Math.floor(z) let xf = x - xi let yf = y - yi let zf = z - zi let rxf, ryf let r = 0 let ampl = 0.5 let n1, n2, n3 for (let o = 0; o < this._po; o++) { let of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB) rxf = scaled_cosine(xf) ryf = scaled_cosine(yf) n1 = this._p[of & PERLIN_SIZE] n1 += rxf * (this._p[(of + 1) & PERLIN_SIZE] - n1) n2 = this._p[(of + PERLIN_YWRAP) & PERLIN_SIZE] n2 += rxf * (this._p[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2) n1 += ryf * (n2 - n1) of += PERLIN_ZWRAP n2 = this._p[of & PERLIN_SIZE] n2 += rxf * (this._p[(of + 1) & PERLIN_SIZE] - n2) n3 = this._p[(of + PERLIN_YWRAP) & PERLIN_SIZE] n3 += rxf * (this._p[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3) n2 += ryf * (n3 - n2) n1 += scaled_cosine(zf) * (n2 - n1) r += n1 * ampl ampl *= this._pf xi <<= 1 xf *= 2 yi <<= 1 yf *= 2 zi <<= 1 zf *= 2 if (xf >= 1.0) { xi++ xf-- } if (yf >= 1.0) { yi++ yf-- } if (zf >= 1.0) { zi++ zf-- } } return r } /** * Adjusts the detail level of the noise by setting the number of octaves and amplitude falloff. * @param {number} lod - Level of detail (number of octaves). * @param {number} falloff - Amplitude falloff per octave. */ noiseDetail(lod, falloff) { if (lod > 0) { this._po = lod } if (falloff > 0) { this._pf = falloff } } /** * Sets a seed for the Perlin noise generator, ensuring deterministic results. * @param {number} value - Seed value. */ noiseSeed(value = null) { if (value != null) { this._e.seed(value) } const random = this._e.rand || Math.random for (let i = 0; i < PERLIN_SIZE + 1; i++) { this._p[i] = random() } } }