196 lines
3.5 KiB
TypeScript
196 lines
3.5 KiB
TypeScript
|
// This is free and unencumbered software released into the public domain
|
||
|
|
||
|
import shuffleSeed from "./shuffle_seed";
|
||
|
|
||
|
const NORM_2D = 1.0 / 47.0;
|
||
|
const SQUISH_2D = (Math.sqrt(2 + 1) - 1) / 2;
|
||
|
const STRETCH_2D = (1 / Math.sqrt(2 + 1) - 1) / 2;
|
||
|
|
||
|
export type Noise2D = (x: number, y: number) => number;
|
||
|
|
||
|
interface Contribution2D {
|
||
|
dx: number;
|
||
|
dy: number;
|
||
|
next?: Contribution2D;
|
||
|
xsb: number;
|
||
|
ysb: number;
|
||
|
}
|
||
|
|
||
|
function contribution2D(
|
||
|
multiplier: number,
|
||
|
xsb: number,
|
||
|
ysb: number,
|
||
|
): Contribution2D {
|
||
|
return {
|
||
|
dx: -xsb - multiplier * SQUISH_2D,
|
||
|
dy: -ysb - multiplier * SQUISH_2D,
|
||
|
xsb,
|
||
|
ysb,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
export function makeNoise2D(clientSeed: number): Noise2D {
|
||
|
const contributions: Contribution2D[] = [];
|
||
|
for (let i = 0; i < p2D.length; i += 4) {
|
||
|
const baseSet = base2D[p2D[i]];
|
||
|
let previous: Contribution2D | null = null;
|
||
|
let current: Contribution2D | null = null;
|
||
|
for (let k = 0; k < baseSet.length; k += 3) {
|
||
|
current = contribution2D(baseSet[k], baseSet[k + 1], baseSet[k + 2]);
|
||
|
if (previous === null) contributions[i / 4] = current;
|
||
|
else previous.next = current;
|
||
|
previous = current;
|
||
|
}
|
||
|
current!.next = contribution2D(p2D[i + 1], p2D[i + 2], p2D[i + 3]);
|
||
|
}
|
||
|
const lookup: Contribution2D[] = [];
|
||
|
for (let i = 0; i < lookupPairs2D.length; i += 2) {
|
||
|
lookup[lookupPairs2D[i]] = contributions[lookupPairs2D[i + 1]];
|
||
|
}
|
||
|
|
||
|
const perm = new Uint8Array(256);
|
||
|
const perm2D = 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;
|
||
|
perm[i] = source[r[0]];
|
||
|
perm2D[i] = perm[i] & 0x0e;
|
||
|
source[r[0]] = source[i];
|
||
|
}
|
||
|
|
||
|
return (x: number, y: number): number => {
|
||
|
const stretchOffset = (x + y) * STRETCH_2D;
|
||
|
|
||
|
const xs = x + stretchOffset;
|
||
|
const ys = y + stretchOffset;
|
||
|
|
||
|
const xsb = Math.floor(xs);
|
||
|
const ysb = Math.floor(ys);
|
||
|
|
||
|
const squishOffset = (xsb + ysb) * SQUISH_2D;
|
||
|
|
||
|
const dx0 = x - (xsb + squishOffset);
|
||
|
const dy0 = y - (ysb + squishOffset);
|
||
|
|
||
|
const xins = xs - xsb;
|
||
|
const yins = ys - ysb;
|
||
|
|
||
|
const inSum = xins + yins;
|
||
|
const hash = (xins - yins + 1) |
|
||
|
(inSum << 1) |
|
||
|
((inSum + yins) << 2) |
|
||
|
((inSum + xins) << 4);
|
||
|
|
||
|
let value = 0;
|
||
|
|
||
|
for (
|
||
|
let c: Contribution2D | undefined = lookup[hash];
|
||
|
c !== undefined;
|
||
|
c = c.next
|
||
|
) {
|
||
|
const dx = dx0 + c.dx;
|
||
|
const dy = dy0 + c.dy;
|
||
|
|
||
|
const attn = 2 - dx * dx - dy * dy;
|
||
|
if (attn > 0) {
|
||
|
const px = xsb + c.xsb;
|
||
|
const py = ysb + c.ysb;
|
||
|
|
||
|
const indexPartA = perm[px & 0xff];
|
||
|
const index = perm2D[(indexPartA + py) & 0xff];
|
||
|
|
||
|
const valuePart = gradients2D[index] * dx + gradients2D[index + 1] * dy;
|
||
|
|
||
|
value += attn * attn * attn * attn * valuePart;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return value * NORM_2D;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const base2D = [
|
||
|
[1, 1, 0, 1, 0, 1, 0, 0, 0],
|
||
|
[1, 1, 0, 1, 0, 1, 2, 1, 1],
|
||
|
];
|
||
|
|
||
|
const gradients2D = [
|
||
|
5,
|
||
|
2,
|
||
|
2,
|
||
|
5,
|
||
|
-5,
|
||
|
2,
|
||
|
-2,
|
||
|
5,
|
||
|
5,
|
||
|
-2,
|
||
|
2,
|
||
|
-5,
|
||
|
-5,
|
||
|
-2,
|
||
|
-2,
|
||
|
-5,
|
||
|
];
|
||
|
|
||
|
const lookupPairs2D = [
|
||
|
0,
|
||
|
1,
|
||
|
1,
|
||
|
0,
|
||
|
4,
|
||
|
1,
|
||
|
17,
|
||
|
0,
|
||
|
20,
|
||
|
2,
|
||
|
21,
|
||
|
2,
|
||
|
22,
|
||
|
5,
|
||
|
23,
|
||
|
5,
|
||
|
26,
|
||
|
4,
|
||
|
39,
|
||
|
3,
|
||
|
42,
|
||
|
4,
|
||
|
43,
|
||
|
3,
|
||
|
];
|
||
|
|
||
|
const p2D = [
|
||
|
0,
|
||
|
0,
|
||
|
1,
|
||
|
-1,
|
||
|
0,
|
||
|
0,
|
||
|
-1,
|
||
|
1,
|
||
|
0,
|
||
|
2,
|
||
|
1,
|
||
|
1,
|
||
|
1,
|
||
|
2,
|
||
|
2,
|
||
|
0,
|
||
|
1,
|
||
|
2,
|
||
|
0,
|
||
|
2,
|
||
|
1,
|
||
|
0,
|
||
|
0,
|
||
|
0,
|
||
|
];
|