-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path_ff_cgpt_perlin.noise4d_revision_notes.lua
277 lines (244 loc) · 10.2 KB
/
_ff_cgpt_perlin.noise4d_revision_notes.lua
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
-- <?> 3.10.2023»19:07
Here's the current state of my notes and code, after revision:
-- permutation table
-- defines
perlin = {}, perlin.p = {}
-- `p` is a lookup table that stores the permutation of 256 integers used in the calculation of the `noise4d` function
-- initializes
math.randomseed(get_intslider_input(SEED))
for i = 1, 256 do
perlin.p[i] = math.random(255)
perlin.p[256 + i] = perlin.p[i] -- verify this to rule out `nil`,`oob`
end;
-- the input parameters for the `perlin.noise4d` function and their respective data types:
-- `x`: float
-- `y`: float
-- `z`: float (optional)
-- `w`: float (optional)
-- t: float (optional)
-- `lacunarity`: float
-- `octaves`: int
-- `frequency`: float
-- `amplitude`: float
-- `persistence`: float
-- • `z`, `w` and `t` are optional parameters
-- optionally, if they are not provided, maybe the function could generate 3d or 2d noise?
function perlin.octaves4d(x, y, z, w, lacunarity, octaves, frequency, amplitude, persistence, t)
-- scales the input coordinates based on the `frequency`
-- computes the maximum value
-- for i = 1, octaves do
-- calculates the value (`noise4d`) of each octave
-- scales the value by the `amplitude` and adds to the total
-- updates the maximum value and reduce the amplitude for the next octave
-- scales the input coordinates by `lacunarity` for the next octave
-- if t ~= nil then t = t * lacunarity
-- returns octaves of noise
function perlin.noise4d(x, y, z, w)
-- calculates the "unit hypercube"
-- calculates the relative positions of the input point within the unit hypercube
-- calculates the fractional part of the coordinates of the point within the unit hypercube
-- applies fade function
-- calculates the dot product of each corner vector and the distance vector from the point to that corner
-- uses the fade curve to blend the contributions of each corner vector together
-- identifies the corner points of the unit hypercube surrounding the input point are identified using a hash function
-- declares local variables to store the values of the corner points.
-- retrieves values from the permutation table
-- adds adjustments from the initial hypercube coordinate positions
-- stores the pseudorandom gradient vectors corresponding to the unit hypercube vertices surrounding the input point
-- calculates the noise value for the input coordinates by
-- taking the weighted average between all unit hypercube coordinates surrounding the input coordinate
-- nested lerp functions are used to calculate the weighted average between all unit hypercube coordinates
-- gradient vectors are calculated using the grad function
function perlin.grad4d(hash, x, y, z, w)
return (gradient)
end;
-- takes a hash value (int [1, 256]) and the input coordinates, and returns a gradient vector
-- (finds the dot product between a pseudorandom gradient vector and the vector from an input coordinate to a unit hypercube vertex)
-- (returns the computed dot product)
function perlin.dot4d(x, y, z, w)
-- generates table of 32 possible bit patterns [1, 32](x, y, z, w)
-- each entry contains a function
-- takes x, y, z, w arguments
-- returns the direction of the vector
function perlin.lerp4d(t, a, b)
return a + t * (b - a)
end;
-- takes two values `a` and `b`, and a weight `w` and returns the linearly interpolated value between `a` and `b` based on the weight `w`
-- used to combine the weighted average of the dot products of gradient vectors and the input coordinates
function perlin.fade4d(t)
return t * t * t * (t * (t * 6 - 15) + 10)
end;
-- is a 6th-degree polynomial that interpolates smoothly between 0 and 1
-- takes a single parameter `t`, which represents the input value to be smoothed
-- used to smooth the final output of the `perlin:noise4d` function
function test(..., min, max, kind)
takes an arbitrary number of variable name-value pairs, min, max, kind
creates a table to store the pairs of variable names and values, with a field for errors
iterates over the arguments by incrementing the index by two, and uses each odd-numbered argument as a variable name and each even-numbered argument as a value, adding them to the table as key-value pairs
tests if the value is within [min, max], adds a result field to the table for its pair with "true" if it is, "oob" if it is out-of-bounds, or "nil" if it is nil
also, if it is out-of-bounds or nil then errors = 1, else errors = 0
after the test loop, it tallies the number or errors
if sum_errors > 0, it will
generate a report string consisting of: (sum_errors .. ": " .. the list of "names .. ", " .. values .. ", " .. results (groups separated by "; " .. kind .. ".")
throw error using ("<!> " .. report) for the message
returns sum_errors, min, max, kind
```
-- permutation table
perlin = {}
perlin.p = {}
-- initialize `perlin.p`
math.randomseed(get_intslider_input(SEED))
for i = 1, 256 do
perlin.p[i] = math.random(255)
perlin.p[256 + i] = perlin.p[i]
end;
function perlin.grad4d(hash, x, y, z, w)
local h = hash % 32 + 1
local u = h < 8 and x or y
local v = h < 4 and y or (h == 12 or h == 14) and x or z
local r = (h % 2 == 0) and u or v
local h2 = hash % 16
local u2 = h2 < 8 and y or z
local v2 = h2 < 4 and x or (h2 == 12 or h2 == 14) and y or w
local r2 = (h2 % 2 == 0) and u2 or v2
return ((hash % 2 == 0) and r or -r) + ((h2 % 2 == 0) and r2 or -r2)
end;
function perlin.dot4d(hash, x, y, z, w)
local h = hash % 32 + 1
local gx = perlin.grad4d(h, x, y, z, w)
local gy = perlin.grad4d(h + 1, x - 1, y, z, w)
local gz = perlin.grad4d(h + 2, x - 1, y - 1, z, w)
local gw = perlin.grad4d(h + 3, x - 1, y - 1, z - 1, w)
return gx * x + gy * y + gz * z + gw * w
end;
function perlin.fade(t)
return t * t * t * (t * (t * 6 - 15) + 10)
end;
function perlin.lerp(a, b, t)
return a + t * (b - a)
end;
function perlin.noise4d(x, y, z, w)
local ix = bit32.band(math.floor(x), 255)
local iy = bit32.band(math.floor(y), 255)
local iz = bit32.band(math.floor(z), 255)
local iw = bit32.band(math.floor(w), 255)
local fx = x - ix
local fy = y - iy
local fz = z - iz
local fw = w - iw
local ux = perlin.fade(fx)
local uy = perlin.fade(fy)
local uz = perlin.fade(fz)
local uw = perlin.fade(fw)
local a = perlin.p[ix & 255 +1] + iy
local aa = perlin.p[a & 255 +1] + iz
local aaa = perlin.p[aa & 255 +1] + iw
local ab = perlin.p[(a + 1) & 255 +1] + iz
local aba = perlin.p[ab & 255 +1] + iw
local b = perlin.p[(ix + 1) & 255 +1] + iy
local ba = perlin.p[b & 255 +1] + iz
local baa = perlin.p[ba & 255 +1] + iw
local bb = perlin.p[(b + 1) & 255 +1] + iz
local bba = perlin.p[bb & 255 +1] + iw
return
perlin.lerp(
perlin.lerp(
perlin.lerp(
perlin.dot4d(perlin.p[aaa & 255 +1], fx, fy, fz, fw),
perlin.dot4d(perlin.p[baa & 255 +1], fx - 1, fy, fz, fw),
ux),
perlin.lerp(
perlin.dot4d(perlin.p[aba & 255 +1], fx, fy - 1, fz, fw),
perlin.dot4d(perlin.p[bba & 255 +1], fx - 1, fy - 1, fz, fw),
ux),
uy),
perlin.lerp(
perlin.lerp(
perlin.dot4d(perlin.p[aaa + 1 & 255 +1], fx, fy, fz - 1, fw),
perlin.dot4d(perlin.p[baa + 1 & 255 +1], fx - 1, fy, fz - 1, fw),
ux),
perlin.lerp(
perlin.dot4d(perlin.p[aba + 1 & 255 +1], fx, fy - 1, fz - 1, fw),
perlin.dot4d(perlin.p[bba + 1 & 255 +1], fx - 1, fy - 1, fz - 1, fw),
ux),
uy),
uz)
end;
function perlin.octaves4d(x, y, z, w, lacunarity, octaves, frequency, amplitude, persistence, t)
-- scale the input coordinates based on the `frequency`
x = x * frequency
y = y * frequency
z = z or x+1 * frequency
w = w or y+1 * frequency
t = t or 0.0
local fractal = 0
local max_noise = (1 - persistence ^ octaves) / (1 - persistence)
for i = 1, octaves do
-- calculate the noise of each octave
local noise = perlin.noise4d(x, y, z, w)
-- scale the noise by the `amplitude` and add to the fractal
fractal = fractal + noise * amplitude
-- update the maximum noise and reduce the amplitude for the next octave
max_noise = max_noise + amplitude
amplitude = amplitude * persistence
-- scale the input coordinates for the next octave
x = x * lacunarity
y = y * lacunarity
z = z * lacunarity
w = w * lacunarity
if t ~= nil then
t = t * lacunarity
end;
end;
return fractal
end;
Here's an example call to the test function using named arguments:
sum_errors, min, max, kind = test(ix=128, iy=192, iz=64, iw=210, min=1, max=256, kind="p{i}")
And here's the updated implementation of the test function that uses named arguments:
function test(...)
-- parse arguments
local args = {...}
local n = #args
local options = {min=1, max=256, kind="indexed value"}
local values = {}
local errors = {}
for i = 1, n, 2 do
local name = args[i]
local value = args[i+1]
if type(name) ~= "string" then
error("Variable name must be a string")
end
values[name] = value
end
for name, value in pairs(values) do
local result = nil
if value == nil then
result = "nil"
errors[name] = true
elseif value < options.min or value > options.max then
result = "oob"
errors[name] = true
else
result = "true"
end
values[name] = {value=value, result=result}
end
local num_errors = 0
for name, error in pairs(errors) do
if error then
num_errors = num_errors + 1
end
end
if num_errors > 0 then
local report = ""
for name, data in pairs(values) do
report = report .. name .. "=" .. data.value .. "," .. data.result .. "; "
end
report = report .. "kind=" .. options.kind
error("<!> " .. num_errors .. ": " .. report)
end
return num_errors, options.min, options.max, options.kind
end
-- The function now expects named arguments, but provides default values for min, max, and kind if they are not provided. If the user specifies min, max, or kind as arguments to the test function, those values will overwrite the default values specified in the options table. The values table now stores both the variable name and its corresponding value and result. The error reporting now includes the names, values, and results of all variables tested.
-- For example, if the user calls test("ix", 128, "iy", 192, "iz", 64, "iw", 210, min=10, kind="p{j}"), then options.min will be set to 10, options.max will remain 256, and options.kind will be set to "p{j}".
```