1 | 'use strict';
|
2 |
|
3 | Math.$random = function(min = 0, max = 10) {
|
4 | return Math.floor(Math.random() * (max - min + 1)) + min;
|
5 | };
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | function MersenneTwister(seed) {
|
11 | seed = seed || Date.now();
|
12 | this.N = 624;
|
13 | this.M = 397;
|
14 | this.MATRIX_A = 0x9908b0df;
|
15 | this.UPPER_MASK = 0x80000000;
|
16 | this.LOWER_MASK = 0x7fffffff;
|
17 | this.I = Math.pow(2, 32);
|
18 |
|
19 | this.mt = new Array(this.N);
|
20 | this.mti = this.N + 1;
|
21 |
|
22 | this.mt[0] = seed >>> 0;
|
23 | for (this.mti = 1; this.mti < this.N; this.mti++) {
|
24 | const s = this.mt[this.mti - 1] ^ this.mt[this.mti - 1] >>> 30;
|
25 | this.mt[this.mti] = (((s & 0xffff0000) >>> 16) * 1812433253 << 16) +
|
26 | (s & 0x0000ffff) * 1812433253 + this.mti;
|
27 | this.mt[this.mti] >>>= 0;
|
28 | }
|
29 | }
|
30 |
|
31 | MersenneTwister.prototype.random = function(min, max) {
|
32 | if (min !== undefined && max !== undefined) {
|
33 | return Math.floor(this.random() * (max - min + 1)) + min;
|
34 | } else if (min !== undefined) {
|
35 | return this.random(0, min);
|
36 | }
|
37 |
|
38 | const mag01 = new Array(0x0, this.MATRIX_A);
|
39 | let y;
|
40 |
|
41 | if (this.mti >= this.N) {
|
42 | let kk;
|
43 |
|
44 | for (kk = 0; kk < this.N - this.M; kk++) {
|
45 | y = this.mt[kk] & this.UPPER_MASK | this.mt[kk + 1] & this.LOWER_MASK;
|
46 | this.mt[kk] = this.mt[kk + this.M] ^ y >>> 1 ^ mag01[y & 0x1];
|
47 | }
|
48 |
|
49 | for (;kk < this.N - 1; kk++) {
|
50 | y = this.mt[kk] & this.UPPER_MASK | this.mt[kk + 1] & this.LOWER_MASK;
|
51 | this.mt[kk] = this.mt[kk + (this.M - this.N)] ^ y >>> 1 ^ mag01[y & 0x1];
|
52 | }
|
53 |
|
54 | y = this.mt[this.N - 1] & this.UPPER_MASK | this.mt[0] & this.LOWER_MASK;
|
55 |
|
56 | this.mt[this.N - 1] = this.mt[this.M - 1] ^ y >>> 1 ^ mag01[y & 0x1];
|
57 |
|
58 | this.mti = 0;
|
59 | }
|
60 |
|
61 | y = this.mt[this.mti++];
|
62 |
|
63 | y ^= y >>> 11;
|
64 | y ^= y << 7 & 0x9d2c5680;
|
65 | y ^= y << 15 & 0xefc60000;
|
66 | y ^= y >>> 18;
|
67 | y = y >>> 0;
|
68 |
|
69 | return (y + 0.5) / this.I;
|
70 | };
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | function interpolate(x0, x1, alpha) {
|
76 | return x0 * (1 - alpha) + alpha * x1;
|
77 | }
|
78 |
|
79 | function generateWhiteNoise(width, height, prng) {
|
80 | const noise = new Array(width * height);
|
81 | for (let i = 0; i < noise.length; ++i) {
|
82 | if (prng) {
|
83 | noise[i] = prng.random();
|
84 | } else {
|
85 | noise[i] = Math.random();
|
86 | }
|
87 | }
|
88 | return noise;
|
89 | }
|
90 |
|
91 | function generatePerlinNoise(width, height, options) {
|
92 | options = options || {};
|
93 |
|
94 | let prng = null;
|
95 | if (options.prng) {
|
96 | prng = options.prng;
|
97 | } else if (options.seed) {
|
98 | prng = new MersenneTwister(options.seed);
|
99 | }
|
100 |
|
101 | const octaveCount = options.octaveCount || 4;
|
102 | const persistence = options.persistence || 0.2;
|
103 | const whiteNoise = generateWhiteNoise(width, height, prng);
|
104 |
|
105 | let amplitude = options.amplitude || 0.1;
|
106 |
|
107 | function generateSmoothNoise(octave) {
|
108 | const noise = new Array(width * height);
|
109 | const samplePeriod = Math.pow(2, octave);
|
110 | const sampleFrequency = 1 / samplePeriod;
|
111 | let noiseIndex = 0;
|
112 | for (let y = 0; y < height; ++y) {
|
113 | const sampleY0 = Math.floor(y / samplePeriod) * samplePeriod;
|
114 | const sampleY1 = (sampleY0 + samplePeriod) % height;
|
115 | const vertBlend = (y - sampleY0) * sampleFrequency;
|
116 | for (let x = 0; x < width; ++x) {
|
117 | const sampleX0 = Math.floor(x / samplePeriod) * samplePeriod;
|
118 | const sampleX1 = (sampleX0 + samplePeriod) % width;
|
119 | const horizBlend = (x - sampleX0) * sampleFrequency;
|
120 |
|
121 | const top = interpolate(whiteNoise[sampleY0 * width + sampleX0],
|
122 | whiteNoise[sampleY1 * width + sampleX0],
|
123 | vertBlend);
|
124 |
|
125 | const bottom = interpolate(whiteNoise[sampleY0 * width + sampleX1],
|
126 | whiteNoise[sampleY1 * width + sampleX1],
|
127 | vertBlend);
|
128 |
|
129 | noise[noiseIndex] = interpolate(top, bottom, horizBlend);
|
130 | noiseIndex += 1;
|
131 | }
|
132 | }
|
133 | return noise;
|
134 | }
|
135 |
|
136 | const smoothNoiseList = new Array(octaveCount);
|
137 | for (let i = 0; i < octaveCount; ++i) {
|
138 | smoothNoiseList[i] = generateSmoothNoise(i);
|
139 | }
|
140 | const perlinNoise = new Array(width * height);
|
141 | let totalAmplitude = 0;
|
142 |
|
143 | for (let i = octaveCount - 1; i >= 0; --i) {
|
144 | amplitude *= persistence;
|
145 | totalAmplitude += amplitude;
|
146 |
|
147 | for (let j = 0; j < perlinNoise.length; ++j) {
|
148 | perlinNoise[j] = perlinNoise[j] || 0;
|
149 | perlinNoise[j] += smoothNoiseList[i][j] * amplitude;
|
150 | }
|
151 | }
|
152 |
|
153 | for (let i = 0; i < perlinNoise.length; ++i) {
|
154 | perlinNoise[i] /= totalAmplitude;
|
155 | }
|
156 |
|
157 | return perlinNoise;
|
158 | }
|
159 |
|
160 |
|
161 |
|
162 | module.exports = {
|
163 | MersenneTwister,
|
164 | generatePerlinNoise
|
165 | };
|