@awayjs/stage
Version:
Stage for AwayJS
192 lines (191 loc) • 7.33 kB
JavaScript
/**
* Implement Turbulence for noise
* Port from Ruffle/Rust
* @see https://github.com/ruffle-rs/ruffle/blob/04d80e5e4e5a3023117e0410378fc623a8bfbbf5/core/src/bitmap/turbulence.rs
*/
var RAND_M = 2147483647; // 2**31 - 1
var RAND_A = 16807; // 7**5; primitive root of m
var RAND_Q = 127773; // m / a
var RAND_R = 2836; // m % a
var B_SIZE = 0x100;
var BM = 0xff;
var PERLIN_N = 0x1000;
var SeededRandom = /** @class */ (function () {
function SeededRandom(seed) {
this.seed = 0;
if (seed <= 0) {
seed = -(seed % (RAND_M - 1)) + 1;
}
if (seed > RAND_M - 1) {
seed = RAND_M - 1;
}
this.seed = seed;
}
SeededRandom.prototype.next = function () {
var result = RAND_A * (this.seed % RAND_Q) - RAND_R * (this.seed / RAND_Q | 0);
if (result <= 0) {
result += RAND_M;
}
return result;
};
return SeededRandom;
}());
function curve(t) {
return t * t * (3. - 2. * t);
}
function lerp(t, a, b) {
return a + t * (b - a);
}
var Turbulence = /** @class */ (function () {
function Turbulence(gradient, lattice_selector) {
this.gradient = gradient;
this.lattice_selector = lattice_selector;
}
Turbulence.prototype.noise2 = function (color_channel, vec, stitch_info) {
var t = vec[0] + PERLIN_N;
var bx0 = t | 0;
var bx1 = bx0 + 1;
var rx0 = t - (t | 0);
var rx1 = rx0 - 1.0;
var t2 = vec[1] + PERLIN_N;
var by0 = t2 | 0;
var by1 = by0 + 1;
var ry0 = t2 - (t2 | 0);
var ry1 = ry0 - 1.0;
// If stitching, adjust lattice points accordingly.
if (stitch_info) {
if (bx0 >= stitch_info.wrap_x) {
bx0 -= stitch_info.width;
}
if (bx1 >= stitch_info.wrap_x) {
bx1 -= stitch_info.width;
}
if (by0 >= stitch_info.wrap_y) {
by0 -= stitch_info.height;
}
if (by1 >= stitch_info.wrap_y) {
by1 -= stitch_info.height;
}
}
bx0 &= BM;
bx1 &= BM;
by0 &= BM;
by1 &= BM;
var i = this.lattice_selector[bx0];
var j = this.lattice_selector[bx1];
var b00 = this.lattice_selector[(i + by0)];
var b10 = this.lattice_selector[(j + by0)];
var b01 = this.lattice_selector[(i + by1)];
var b11 = this.lattice_selector[(j + by1)];
var sx = curve(rx0);
var sy = curve(ry0);
var q = this.gradient[color_channel][b00];
var u = rx0 * q[0] + ry0 * q[1];
var q2 = this.gradient[color_channel][b10];
var v = rx1 * q2[0] + ry0 * q2[1];
var a = lerp(sx, u, v);
var q3 = this.gradient[color_channel][b01];
var u2 = rx0 * q3[0] + ry1 * q3[1];
var q4 = this.gradient[color_channel][b11];
var v2 = rx1 * q4[0] + ry1 * q4[1];
var b = lerp(sx, u2, v2);
return lerp(sy, a, b);
};
Turbulence.prototype.turbulence = function (color_channel, point, base_freq, num_octaves, fractal_sum, do_stitching, tile_pos, tile_size, octave_offsets) {
if (octave_offsets === void 0) { octave_offsets = null; }
var stitch_info = null; // Not stitching when None.
// Adjust the base frequencies if necessary for stitching.
if (do_stitching) {
// When stitching tiled turbulence, the frequencies must be adjusted
// so that the tile borders will be continuous.
if (base_freq[0] !== 0.0) {
var lo_freq = Math.floor(tile_size[0] * base_freq[0]) / tile_size[0];
var hi_freq = Math.ceil(tile_size[0] * base_freq[0]) / tile_size[0];
if (base_freq[0] / lo_freq < hi_freq / base_freq[0]) {
base_freq[0] = lo_freq;
}
else {
base_freq[0] = hi_freq;
}
}
if (base_freq[1] !== 0.0) {
var lo_freq = Math.floor(tile_size[1] * base_freq[1]) / tile_size[1];
var hi_freq = Math.ceil(tile_size[1] * base_freq[1]) / tile_size[1];
if (base_freq[1] / lo_freq < hi_freq / base_freq[1]) {
base_freq[1] = lo_freq;
}
else {
base_freq[1] = hi_freq;
}
}
// Set up initial stitch values.
var w = (tile_size[0] * base_freq[0] + 0.5) | 0;
var h = (tile_size[1] * base_freq[1] + 0.5) | 0;
stitch_info = {
width: w,
height: h,
wrap_x: (tile_pos[0] * base_freq[0]) | 0 + PERLIN_N + w,
wrap_y: (tile_pos[1] * base_freq[1]) | 0 + PERLIN_N + h,
};
}
var sum = 0.0;
var ratio = 1.0;
var nullOffset = [0, 0];
for (var octave = 0; octave < num_octaves; octave++) {
var offset = octave_offsets ? octave_offsets[octave] : nullOffset;
var vec = [
(point[0] + offset[0]) * base_freq[0] * ratio,
(point[1] + offset[1]) * base_freq[1] * ratio
];
var noise = this.noise2(color_channel, vec, stitch_info);
sum += (fractal_sum ? noise : Math.abs(noise)) / ratio;
ratio *= 2.0;
if (stitch_info) {
stitch_info.width *= 2;
stitch_info.wrap_x = 2 * stitch_info.wrap_x - PERLIN_N;
stitch_info.height *= 2;
stitch_info.wrap_y = 2 * stitch_info.wrap_y - PERLIN_N;
}
}
return sum;
};
Turbulence.fromSeed = function (seed) {
var s;
var lattice_selector = Array.from({ length: B_SIZE * 2 + 2 }, function () { return 0; });
var gradient = [];
var rnd = new SeededRandom(seed);
for (var k = 0; k < 4; k++) {
gradient[k] = Array.from({ length: B_SIZE * 2 + 2 }, function () { return [0, 0]; });
for (var i = 0; i < B_SIZE; i++) {
lattice_selector[i] = i;
for (var j = 0; j < 2; j++) {
seed = rnd.next();
rnd.seed = seed;
gradient[k][i][j] = (seed % (B_SIZE + B_SIZE) - B_SIZE) / B_SIZE;
}
s = Math.sqrt(gradient[k][i][0] * gradient[k][i][0] + gradient[k][i][1] * gradient[k][i][1]);
gradient[k][i][0] /= s;
gradient[k][i][1] /= s;
}
}
for (var i = B_SIZE - 1; i >= 0; i--) {
var k = lattice_selector[i];
seed = rnd.next();
rnd.seed = seed;
var j = seed % B_SIZE;
lattice_selector[i] = lattice_selector[j];
lattice_selector[j] = k;
}
for (var i = 0; i < B_SIZE + 2; i++) {
lattice_selector[B_SIZE + i] = lattice_selector[i];
for (var k = 0; k < 4; k++) {
for (var j = 0; j < 2; j++) {
gradient[k][B_SIZE + i][j] = gradient[k][i][j];
}
}
}
return new Turbulence(gradient, lattice_selector);
};
return Turbulence;
}());
export { Turbulence };