open-simplex-noise
Version:
OpenSimplex noise for TypeScript/JavaScript
391 lines (375 loc) • 11 kB
text/typescript
import {
NORM_2D,
NORM_3D,
NORM_4D,
SQUISH_2D,
SQUISH_3D,
SQUISH_4D,
STRETCH_2D,
STRETCH_3D,
STRETCH_4D,
base2D,
base3D,
base4D,
gradients2D,
gradients3D,
gradients4D,
lookupPairs2D,
lookupPairs3D,
lookupPairs4D,
p2D,
p3D,
p4D
} from './constants';
class Contribution2 {
dx: number;
dy: number;
next: Contribution2;
xsb: number;
ysb: number;
constructor(multiplier: number, xsb: number, ysb: number) {
this.dx = -xsb - multiplier * SQUISH_2D;
this.dy = -ysb - multiplier * SQUISH_2D;
this.xsb = xsb;
this.ysb = ysb;
}
}
class Contribution3 {
dx: number;
dy: number;
dz: number;
next: Contribution3;
xsb: number;
ysb: number;
zsb: number;
constructor(multiplier: number, xsb: number, ysb: number, zsb: number) {
this.dx = -xsb - multiplier * SQUISH_3D;
this.dy = -ysb - multiplier * SQUISH_3D;
this.dz = -zsb - multiplier * SQUISH_3D;
this.xsb = xsb;
this.ysb = ysb;
this.zsb = zsb;
}
}
class Contribution4 {
dw: number;
dx: number;
dy: number;
dz: number;
next: Contribution4;
wsb: number;
xsb: number;
ysb: number;
zsb: number;
constructor(
multiplier: number,
xsb: number,
ysb: number,
zsb: number,
wsb: number
) {
this.dx = -xsb - multiplier * SQUISH_4D;
this.dy = -ysb - multiplier * SQUISH_4D;
this.dz = -zsb - multiplier * SQUISH_4D;
this.dw = -wsb - multiplier * SQUISH_4D;
this.xsb = xsb;
this.ysb = ysb;
this.zsb = zsb;
this.wsb = wsb;
}
}
function shuffleSeed(seed: Uint32Array): Uint32Array {
const newSeed = new Uint32Array(1);
newSeed[0] = seed[0] * 1664525 + 1013904223;
return newSeed;
}
export default class OpenSimplexNoise {
private lookup2D: Contribution2[];
private lookup3D: Contribution3[];
private lookup4D: Contribution4[];
private perm: Uint8Array;
private perm2D: Uint8Array;
private perm3D: Uint8Array;
private perm4D: Uint8Array;
constructor(clientSeed: number) {
this.initialize();
this.perm = new Uint8Array(256);
this.perm2D = new Uint8Array(256);
this.perm3D = new Uint8Array(256);
this.perm4D = new Uint8Array(256);
const source = new Uint8Array(256);
for (let i = 0; i < 256; i++) source[i] = i;
let seed = new Uint32Array(1);
seed[0] = clientSeed;
seed = shuffleSeed(shuffleSeed(shuffleSeed(seed)));
for (let i = 255; i >= 0; i--) {
seed = shuffleSeed(seed);
const r = new Uint32Array(1);
r[0] = (seed[0] + 31) % (i + 1);
if (r[0] < 0) r[0] += i + 1;
this.perm[i] = source[r[0]];
this.perm2D[i] = this.perm[i] & 0x0e;
this.perm3D[i] = (this.perm[i] % 24) * 3;
this.perm4D[i] = this.perm[i] & 0xfc;
source[r[0]] = source[i];
}
}
noise2D(x: number, y: number): number {
const stretchOffset = (x + y) * STRETCH_2D;
const [xs, ys] = [x + stretchOffset, y + stretchOffset];
const [xsb, ysb] = [Math.floor(xs), Math.floor(ys)];
const squishOffset = (xsb + ysb) * SQUISH_2D;
const [dx0, dy0] = [x - (xsb + squishOffset), y - (ysb + squishOffset)];
const [xins, yins] = [xs - xsb, ys - ysb];
const inSum = xins + yins;
const hashVals = new Uint32Array(4);
hashVals[0] = xins - yins + 1;
hashVals[1] = inSum;
hashVals[2] = inSum + yins;
hashVals[3] = inSum + xins;
const hash =
hashVals[0] |
(hashVals[1] << 1) |
(hashVals[2] << 2) |
(hashVals[3] << 4);
let c = this.lookup2D[hash];
let value = 0.0;
while (typeof c !== 'undefined') {
const [dx, dy] = [dx0 + c.dx, dy0 + c.dy];
let attn = 2 - dx * dx - dy * dy;
if (attn > 0) {
const [px, py] = [xsb + c.xsb, ysb + c.ysb];
const i = this.perm2D[(this.perm[px & 0xff] + py) & 0xff];
const valuePart = gradients2D[i] * dx + gradients2D[i + 1] * dy;
attn *= attn;
value += attn * attn * valuePart;
}
c = c.next;
}
return value * NORM_2D;
}
noise3D(x: number, y: number, z: number): number {
const stretchOffset = (x + y + z) * STRETCH_3D;
const [xs, ys, zs] = [
x + stretchOffset,
y + stretchOffset,
z + stretchOffset
];
const [xsb, ysb, zsb] = [Math.floor(xs), Math.floor(ys), Math.floor(zs)];
const squishOffset = (xsb + ysb + zsb) * SQUISH_3D;
const [dx0, dy0, dz0] = [
x - (xsb + squishOffset),
y - (ysb + squishOffset),
z - (zsb + squishOffset)
];
const [xins, yins, zins] = [xs - xsb, ys - ysb, zs - zsb];
const inSum = xins + yins + zins;
const hashVals = new Uint32Array(7);
hashVals[0] = yins - zins + 1;
hashVals[1] = xins - yins + 1;
hashVals[2] = xins - zins + 1;
hashVals[3] = inSum;
hashVals[4] = inSum + zins;
hashVals[5] = inSum + yins;
hashVals[6] = inSum + xins;
const hash =
hashVals[0] |
(hashVals[1] << 1) |
(hashVals[2] << 2) |
(hashVals[3] << 3) |
(hashVals[4] << 5) |
(hashVals[5] << 7) |
(hashVals[6] << 9);
let c = this.lookup3D[hash];
let value = 0.0;
while (typeof c !== 'undefined') {
const [dx, dy, dz] = [dx0 + c.dx, dy0 + c.dy, dz0 + c.dz];
let attn = 2 - dx * dx - dy * dy - dz * dz;
if (attn > 0) {
const [px, py, pz] = [xsb + c.xsb, ysb + c.ysb, zsb + c.zsb];
const i = this.perm3D[
(this.perm[(this.perm[px & 0xff] + py) & 0xff] + pz) & 0xff
];
const valuePart =
gradients3D[i] * dx +
gradients3D[i + 1] * dy +
gradients3D[i + 2] * dz;
attn *= attn;
value += attn * attn * valuePart;
}
c = c.next;
}
return value * NORM_3D;
}
noise4D(x: number, y: number, z: number, w: number): number {
const stretchOffset = (x + y + z + w) * STRETCH_4D;
const [xs, ys, zs, ws] = [
x + stretchOffset,
y + stretchOffset,
z + stretchOffset,
w + stretchOffset
];
const [xsb, ysb, zsb, wsb] = [
Math.floor(xs),
Math.floor(ys),
Math.floor(zs),
Math.floor(ws)
];
const squishOffset = (xsb + ysb + zsb + wsb) * SQUISH_4D;
const dx0 = x - (xsb + squishOffset);
const dy0 = y - (ysb + squishOffset);
const dz0 = z - (zsb + squishOffset);
const dw0 = w - (wsb + squishOffset);
const [xins, yins, zins, wins] = [xs - xsb, ys - ysb, zs - zsb, ws - wsb];
const inSum = xins + yins + zins + wins;
const hashVals = new Uint32Array(11);
hashVals[0] = zins - wins + 1;
hashVals[1] = yins - zins + 1;
hashVals[2] = yins - wins + 1;
hashVals[3] = xins - yins + 1;
hashVals[4] = xins - zins + 1;
hashVals[5] = xins - wins + 1;
hashVals[6] = inSum;
hashVals[7] = inSum + wins;
hashVals[8] = inSum + zins;
hashVals[9] = inSum + yins;
hashVals[10] = inSum + xins;
const hash =
hashVals[0] |
(hashVals[1] << 1) |
(hashVals[2] << 2) |
(hashVals[3] << 3) |
(hashVals[4] << 4) |
(hashVals[5] << 5) |
(hashVals[6] << 6) |
(hashVals[7] << 8) |
(hashVals[8] << 11) |
(hashVals[9] << 14) |
(hashVals[10] << 17);
let c = this.lookup4D[hash];
let value = 0.0;
while (typeof c !== 'undefined') {
const [dx, dy, dz, dw] = [dx0 + c.dx, dy0 + c.dy, dz0 + c.dz, dw0 + c.dw];
let attn = 2 - dx * dx - dy * dy - dz * dz - dw * dw;
if (attn > 0) {
const [px, py, pz, pw] = [
xsb + c.xsb,
ysb + c.ysb,
zsb + c.zsb,
wsb + c.wsb
];
const i = this.perm4D[
(this.perm[
(this.perm[(this.perm[px & 0xff] + py) & 0xff] + pz) & 0xff
] +
pw) &
0xff
];
const valuePart =
gradients4D[i] * dx +
gradients4D[i + 1] * dy +
gradients4D[i + 2] * dz +
gradients4D[i + 3] * dw;
attn *= attn;
value += attn * attn * valuePart;
}
c = c.next;
}
return value * NORM_4D;
}
private initialize() {
const contributions2D: Contribution2[] = [];
for (let i = 0; i < p2D.length; i += 4) {
const baseSet = base2D[p2D[i]];
let previous: Contribution2 = null;
let current: Contribution2 = null;
for (let k = 0; k < baseSet.length; k += 3) {
current = new Contribution2(baseSet[k], baseSet[k + 1], baseSet[k + 2]);
if (previous === null) contributions2D[i / 4] = current;
else previous.next = current;
previous = current;
}
current.next = new Contribution2(p2D[i + 1], p2D[i + 2], p2D[i + 3]);
}
this.lookup2D = [];
for (let i = 0; i < lookupPairs2D.length; i += 2) {
this.lookup2D[lookupPairs2D[i]] = contributions2D[lookupPairs2D[i + 1]];
}
const contributions3D: Contribution3[] = [];
for (let i = 0; i < p3D.length; i += 9) {
const baseSet = base3D[p3D[i]];
let previous: Contribution3 = null;
let current: Contribution3 = null;
for (let k = 0; k < baseSet.length; k += 4) {
current = new Contribution3(
baseSet[k],
baseSet[k + 1],
baseSet[k + 2],
baseSet[k + 3]
);
if (previous === null) contributions3D[i / 9] = current;
else previous.next = current;
previous = current;
}
current.next = new Contribution3(
p3D[i + 1],
p3D[i + 2],
p3D[i + 3],
p3D[i + 4]
);
current.next.next = new Contribution3(
p3D[i + 5],
p3D[i + 6],
p3D[i + 7],
p3D[i + 8]
);
}
this.lookup3D = [];
for (let i = 0; i < lookupPairs3D.length; i += 2) {
this.lookup3D[lookupPairs3D[i]] = contributions3D[lookupPairs3D[i + 1]];
}
const contributions4D: Contribution4[] = [];
for (let i = 0; i < p4D.length; i += 16) {
const baseSet = base4D[p4D[i]];
let previous: Contribution4 = null;
let current: Contribution4 = null;
for (let k = 0; k < baseSet.length; k += 5) {
current = new Contribution4(
baseSet[k],
baseSet[k + 1],
baseSet[k + 2],
baseSet[k + 3],
baseSet[k + 4]
);
if (previous === null) contributions4D[i / 16] = current;
else previous.next = current;
previous = current;
}
current.next = new Contribution4(
p4D[i + 1],
p4D[i + 2],
p4D[i + 3],
p4D[i + 4],
p4D[i + 5]
);
current.next.next = new Contribution4(
p4D[i + 6],
p4D[i + 7],
p4D[i + 8],
p4D[i + 9],
p4D[i + 10]
);
current.next.next.next = new Contribution4(
p4D[i + 11],
p4D[i + 12],
p4D[i + 13],
p4D[i + 14],
p4D[i + 15]
);
}
this.lookup4D = [];
for (let i = 0; i < lookupPairs4D.length; i += 2) {
this.lookup4D[lookupPairs4D[i]] = contributions4D[lookupPairs4D[i + 1]];
}
}
}