forked from eliasfloreteng/lively-audio-visualizer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
noise.js
157 lines (137 loc) · 3.65 KB
/
noise.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Borrowed from p5.js: https://github.com/processing/p5.js/blob/main/src/math/noise.js
const PERLIN_YWRAPB = 4
const PERLIN_YWRAP = 1 << PERLIN_YWRAPB
const PERLIN_ZWRAPB = 8
const PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB
const PERLIN_SIZE = 4095
let perlin_octaves = 4 // default to medium smooth
let perlin_amp_falloff = 0.5 // 50% reduction/octave
const scaled_cosine = (i) => 0.5 * (1.0 - Math.cos(i * Math.PI))
let perlin // will be initialized lazily by noise() or noiseSeed()
/**
* @method noise
* @param {Number} x x-coordinate in noise space
* @param {Number} [y] y-coordinate in noise space
* @param {Number} [z] z-coordinate in noise space
* @return {Number} Perlin noise value (between 0 and 1) at specified
* coordinates
*/
function noise(x, y = 0, z = 0) {
if (perlin == null) {
perlin = new Array(PERLIN_SIZE + 1)
for (let i = 0; i < PERLIN_SIZE + 1; i++) {
perlin[i] = Math.random()
}
}
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 < perlin_octaves; o++) {
let of = xi + (yi << PERLIN_YWRAPB) + (zi << PERLIN_ZWRAPB)
rxf = scaled_cosine(xf)
ryf = scaled_cosine(yf)
n1 = perlin[of & PERLIN_SIZE]
n1 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n1)
n2 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE]
n2 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n2)
n1 += ryf * (n2 - n1)
of += PERLIN_ZWRAP
n2 = perlin[of & PERLIN_SIZE]
n2 += rxf * (perlin[(of + 1) & PERLIN_SIZE] - n2)
n3 = perlin[(of + PERLIN_YWRAP) & PERLIN_SIZE]
n3 += rxf * (perlin[(of + PERLIN_YWRAP + 1) & PERLIN_SIZE] - n3)
n2 += ryf * (n3 - n2)
n1 += scaled_cosine(zf) * (n2 - n1)
r += n1 * ampl
ampl *= perlin_amp_falloff
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
}
/**
* @method noiseDetail
* @param {Number} lod number of octaves to be used by the noise
* @param {Number} falloff falloff factor for each octave
*/
function noiseDetail(lod, falloff) {
if (lod > 0) {
perlin_octaves = lod
}
if (falloff > 0) {
perlin_amp_falloff = falloff
}
}
/**
* @method noiseSeed
* @param {Number} seed the seed value
*/
function noiseSeed(seed) {
// Linear Congruential Generator
// Variant of a Lehman Generator
const lcg = (() => {
// Set to values from http://en.wikipedia.org/wiki/Numerical_Recipes
// m is basically chosen to be large (as it is the max period)
// and for its relationships to a and c
const m = 4294967296
// a - 1 should be divisible by m's prime factors
const a = 1664525
// c and m should be co-prime
const c = 1013904223
let seed, z
return {
setSeed(val) {
// pick a random seed if val is undefined or null
// the >>> 0 casts the seed to an unsigned 32-bit integer
z = seed = (val == null ? Math.random() * m : val) >>> 0
},
getSeed() {
return seed
},
rand() {
// define the recurrence relationship
z = (a * z + c) % m
// return a float in [0, 1)
// if z = m then z / m = 0 therefore (z % m) / m < 1 always
return z / m
},
}
})()
lcg.setSeed(seed)
perlin = new Array(PERLIN_SIZE + 1)
for (let i = 0; i < PERLIN_SIZE + 1; i++) {
perlin[i] = lcg.rand()
}
}