-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path_ff_cgpt_catch_functions.lua
683 lines (549 loc) · 25 KB
/
_ff_cgpt_catch_functions.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
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
It can be difficult to predict what assumptions the original author made about the environment in which the script would be run, or how the output should be formatted. But by breaking down the code and understanding what each piece is doing, you can usually figure out how to adapt it to work in Filter Forge.
the map script requires this format:
function prepare()
-- called once, before the rendering begins.
-- used for non-mapped inputs and precalculation.
end;
function get_sample(x, y)
-- main function returning image output.
-- used for mapped inputs and mage generation.
local r, g, b, a = 0, 0, 0, 1 -- initialize when not input
local , g, b = x, y, (x + y) / 2 -- default gradient output
return r, g, b, a
end;
-function add_vector_gradients(x, y, num_dimensions, resolution)
local gradients = {{1, 0}, {0, 1}} -- x and y gradients
for i = 1, num_dimensions do
table.insert(gradients, {1 / resolution, -1 / resolution})
end
return unpack(gradients)
end
-- x, y, z, w = add_vector_gradients(x, y, 2, 600)
function get_sample_color(x, y, r, g, b, a)
local r, g, b, a = x * y * r, x * y * g, x * y * b, x * y * a
return r, g, b, a
end
-- r_, g_, b_, a_ = get_sample_color(x, y, 1, 1, 1, 1)
-- r_atm, g_atm, b_atm, a_atm = get_sample_color(x, y, 0.055, 0.5775, 1.1, 1)
-[[
local sqrt, min, max, rad, pi = math.sqrt, math.min, math.max, math.rad, math.pi
local sin, cos, tan, asin, acos, atan2 = math.sin, math.cos, math.tan, math.asin, math.acos, math.atan2
-- prepare
-- inputs and precalculation
ang_x = rad(get_angle_input(ANGLE_X))
ang_y = rad(get_angle_input(ANGLE_Y))
ang_z = rad(get_angle_input(ANGLE_Z))
cos_ax, sin_ax = cos(ang_x), sin(ang_x)
cos_ay, sin_ay = cos(ang_y), sin(ang_y)
cos_az, sin_az = cos(ang_z), sin(ang_z)
]]
function get_map_rotations_xyz(x, y)
local x = x - 0.5
local y = y * pi
x = x * aspect * pi
local nx = cos(x) * sin(y)
local ny = sin(x) * sin(y)
local nz = cos(y)
local ax1, ax2 = (nx * cos_ax) + (ny * sin_ax), (nx * sin_ax) - (ny * cos_ax)
local ay1, ay2 = (ax1 * cos_ay) - (cos(y) * sin_ay), (ax1 * sin_ay) + (cos(y) * cos_ay)
local az1, az2 = (ax2 * cos_az) + (ay2 * sin_az), (ax2 * sin_az) - (ay2 * cos_az)
nx = atan2(az1, ay1) * 0.159155 + 0.5
ny = acos(az2) * 0.16 * 2
nx = xratio(nx) -- aspect auto-conversion
return nx, ny
end;
function xratio(x)
if OUTPUT_WIDTH / OUTPUT_HEIGHT == 2 then x = x * 2 - 1 end;
return x
end;
-- x = xratio(x) -- in get_sample or function
local function cos_sin(angle)
local cos_angle, sin_angle = cos(angle), sin(angle)
return cos_angle, sin_angle
end;
-- c_, s_ = cos_sin(angle) -- in prepare or function
function get_cos_sin(rotation, tilt, roll)
local rotation, tilt, roll = rotation, tilt, roll
cy, sy = cos_sin(rotation) -- y-axis aka yaw
cx, sx = cos_sin(tilt) -- x-axis aka pitch
cz, sz = cos_sin(roll) -- z-axis
return cy, sy, cx, sx, cz, sz
end;
-- cy_, sy_, cx_, sx_, cz_, sz_ = get_cos_sin(rotation_, tilt_, roll_) -- in prepare
function get_sphere(x, y)
local x, y = x * 2 - 1, y * 2 - 1
x, y = x * radius, y * radius
local rho = sqrt((x * x)+(y * y))
local tha, phi = atan2(y, x), 0
if per > 0 then
phi = min(1, rho) * per
local h, cph = sin(phi), cos(phi)
rho = h * (cph* _div - sqrt((cph * cph - cp * cp)* _div* _div))
end;
px, py = rho * cos(tha), rho * sin(tha)
local z = -sqrt(1.0 - ((x * x)+(y * y)))
return x, y, z, rho, phi, tha
end;
-- x_, y_, z_, rho_, phi_, tha_ = get_sphere(x, y) -- in get_sample
function rotate_sphere(x, y, z, cy, sy, cx, sx, cz, sz)
x, y = rotate_z(x, y, z) -- roll
z, y = rotate_x(x, y, z) -- tilt
x, z = rotate_y(x, y, z) -- rotation
return x, y, z
end;
function rotate_z(x, y, z) -- z-axis -- roll
local tx, ty = (cz * x) - (sz * y), (sz * x) + (cz * y)
x, y = tx, ty
return x, y
end;
function rotate_x(x, y, z) -- x-axis -- tilt
local tz, ty = (cx * z) - (sx * y), (sx * z) + (cx * y)
z, y = tz, ty
return z, y
end;
function rotate_y(x, y, z) -- y-axis -- rotation
local tx, tz= (cy * x) - (sy * z), (sy * x) + (cy * z)
x, z = tx, tz
return x, z
end;
function rotate_xy(x, y, rotation) -- tha, phi
x, y = min(max(x, - 1), 1), - min(max(y, - 1), 1)
local phi, tha = asin(y), 0
local cph = cos(phi)
x = min(max(x, - cph), cph)
if cph~=0 then tha = asin(x / cph) end;
phi = .5 * pi - phi
local nx, ny, nz = cos(tha) * sin(phi), sin(tha) * sin(phi), cos(phi)
nx, nz = nx * cx_1 - nz * sx_1, nx * sx_1 + nz * cx_1 -- tilt
tha, phi = atan2(ny, nx) + rotation, acos(nz) -- rotation
tha, phi = (tha * pi_div + 1) * .5, phi * pi_div
tha = xratio(tha) -- aspect auto-conversion
return tha, phi -- relative x, y
end;
function get_atmos(x, y, alpha, rho)
local x, y, alpha, rho = x, y, alpha, rho
local atmos, phi = 1, min(1, rho)
local s_atmos, c_atmos = sin(atmos), cos(atmos)
local s_phi, c_phi = sin(phi), cos(phi)
rho = s_phi * (c_phi / pi - sqrt((c_phi * c_phi - c_atmos * c_atmos) / (pi ^ 2))) * 10
atmos = -sqrt(max(1.0 - ((x * x)+(y * y)), 0.0000001))
atmos = (atmos * 0.5 + 0.5) * 2
return atmos
end;
-- atmos = get_slider_input(ATMOS) * 0.25 * pi; s_atmos, c_atmos = sin(atmos), cos(atmos); divp = max(1 / s_atmos)
-- if atmos > radius then alpha = 0 end; return alpha * atmos, atmos, alpha
a lua map script for cos and sin of an angle:
local function cos_sin(angle)
local cos_angle, sin_angle = cos(angle), sin(angle)
return cos_angle, sin_angle
end;
-- _ac, _as = cos_sin(angle)
a lua map script for cartesian to bi-polar remapping:
function prepare()
aspect = OUTPUT_HEIGHT / OUTPUT_WIDTH * 2
mode = get_checkbox_input(MODE) and true or false
end;
function get_sample(x, y)
local z = (1 - y) * 2 - 1
local nz = 1 - x
local x = x * aspect * math.pi
local y = y * math.pi
local nx = math.cos(x) * math.sin(y)
local ny = (math.sin(x) * math.sin(y))
-- nx = nx * 0.5 + 0.5
-- ny = ny * 0.5 + 0.5
-- poles to equator
-- local nnx = (math.atan(z, nx) / math.pi) * 0.5 + 0.5
-- local nny = ny * 0.5 + 0.5
-- poles to poles
local nnx = (math.atan(z, nx) / math.pi)5
local nny = ny
r = nnx
g = nny
b = 0.8235294118 -- nz
if mode then
return r, g, b, 1
else
r, g, b, a = get_sample_map(nnx, nny, SOURCE)
return r, g, b, a
end
end;
a lua map script for equirectangular remapping:
function prepare()
-- inputs and precalculation
aspect = OUTPUT_HEIGHT / OUTPUT_WIDTH * 2
end;
function get_sample(x, y)
-- image generation
local nx, ny, nz = map_equirectangular(x, y)
local r, g, b, a = nx, ny, nz, 1
return r, g, b, a
end;
function map_equirectangular(x, y)
-- equirectangular remapping
local x = x * aspect * math.pi
local y = y * math.pi
local nx = math.cos(x) * math.sin(y)
local ny = math.sin(x) * math.sin(y)
local nz = math.cos(y)
return nx, ny, nz
end;
-- local nx, ny, nz = map_equirectangular(x, y)
a lua function to convert radians to rotations
function rad2rot(x) return x * 0.159155 end -- or / 6.2832
a lua function to clamp a value to 0 to 1 range:
function truncate(value) -- truncate()
if value <= 0 then value = 0 end
if value >= 1 then value = 1 end
return value
end;
a lua function to modify any variables that must not be zero (epsilon):
function e(x)
if math.abs(x) < 1e-10 then
return 1e-10
else
return x
end
end
function reciprocal(x)
-- calculate the reciprocal of a variable, making sure it's not zero
return 1 / e(x)
end
a lua function that sums all the arguments passed to it:
function sum(...)
local result = 0
for i, v in ipairs({...}) do
result = result + v
end
return result
end
--[[
the ... notation in the function definition allows the sum function to accept any number of arguments. the ipairs({...}) expression inside the function body creates an iterator over the table of arguments passed to the function, allowing them to be accessed and summed.
]]--
a lua function to convert a value from one range of numbers to another:
function convert_range(old_min, old_max, new_min, new_max, value)
local old_range = old_max - old_min
local new_range = new_max - new_min
local normalized_value = (value - old_min) / old_range
return new_min + (normalized_value * new_range)
end
--[[
this function takes in the old minimum and maximum values (0 and 100, respectively), as well as the new minimum and maximum values (0 and 1, respectively), and the value to convert (50). it then calculates the normalized value of the input value, and returns the converted value in the new range (in the example: 0.5).
]]--
a lua function to convert one range of numbers to another that returns the conversion ratio and adjustment value for the new range or a converted value if provided:
function convert_range(old_min, old_max, new_min, new_max, value)
local old_range = old_max - old_min
local new_range = new_max - new_min
local ratio = new_range / old_range
local adjustment = new_min - old_min * ratio
if value == nil then
return ratio, adjustment
else
return value * ratio + adjustment
end
end
--[[
this function takes the old_min, old_max, new_min, new_max, value arguments and returns value in the new range, or the conversion ratio and adjustment value if value is not supplied.
if you supply -1, 1, 0, 1 as the arguments to the convert_range function, you will get a ratio of 0.5 and an adjustment value of 0.5, since:
• old_min is -1; old_max is 1; new_min is 0; new_max is 1
the ratio can be calculated as:
ratio = (new_max - new_min) / (old_max - old_min)
• = (1 - 0) / (1 - (-1))
• = 0.5
the adjustment value can be calculated as:
adjustment = new_min - (ratio * old_min)
• = 0 - (0.5 * (-1))
• = 0.5
so, if you call convert_range(-1, 1, 0, 1), you will get 0.5 as the ratio and 0.5 as the adjustment value.
]]--
a lua map script exploiting a function to convert a value from one range of numbers to another or return an rgb value,ratio and adjustment:
function prepare()
-- inputs and precalculation
mode = get_checkbox_input(MODE)
end;
function get_sample(x, y)
-- inputs and image generation
local r, g, b, a = get_sample_map(x, y, SOURCE)
old_min = get_sample_grayscale(x, y, FROM_MIN)
old_max = get_sample_grayscale(x, y, FROM_MAX)
new_min = get_sample_grayscale(x, y, TO_MIN)
new_max = get_sample_grayscale(x, y, TO_MAX)
if mode then
--- average channels
value = r +g +b / 3
-- fill channels with `value * ratio + adjustment, ratio, adjustment`
r, g, b = convert_range(old_min, old_max, new_min, new_max, value, mode)
else
r = convert_range(old_min, old_max, new_min, new_max, r, mode)
g = convert_range(old_min, old_max, new_min, new_max, g, mode)
b = convert_range(old_min, old_max, new_min, new_max, b, mode)
end;
return r, g, b, a
end;
function convert_range(old_min, old_max, new_min, new_max, value, mode)
local old_range = old_max - old_min
local new_range = new_max - new_min
local ratio = new_range / old_range
local adjustment = new_min - old_min * ratio
if mode then
return value * ratio + adjustment, ratio, adjustment
else
return value * ratio + adjustment
end;
end;
a function that calculates the angle offsets of three 2D offsets:
function calculate_angle(x, y)
return math.atan2(y, x) * 180 / math.pi
end
-- this code defines a function calculate_angle that takes an x and y offset and returns the angle offset in degrees. the main code then defines an array of three 2d offsets and uses a loop to calculate the angle offset for each one using the calculate_angle function. the results are printed to the console.
function offset_image(x, y, img)
-- Three 2D offsets
offsets = {
{0.02, 0},
{-0.015, 0},
{-0.015, -0.02}
}
-- Calculate the angle offset for each offset
for i, offset in ipairs(offsets) do
angle = calculate_angle(offset[1], offset[2])
-- print(string.format("Offset %d: %.2f degrees", i, angle))
end
-- establish lookup table
local lookup = {}
for x = 1, width do
lookup[x] = {}
for y = 1, height do
local i = math.floor((x-1)/(1/grid_size)) + 1
local j = math.floor((y-1)/(1/grid_size)) + 1
lookup[x][y] = grid[i][j]
end
end
-- set the pixel values of the output image based on the grid cells
for x = 1, width do
for y = 1, height do
local cell = lookup[x][y]
img:set_pixel(x-1, y-1, cell[1], cell[2], cell[3], cell[4]) -- set_pixel function may not exist, just here as an example
end
end
-- return the output image
return img
end
•• I would also like a general `lookup` function for use in a Filter Forge map script component that can use the images from two `get_sample_map` functions `x_lookup` and `y_lookup` and use them as gradients to remap a `source` `get_sample_map` image.
Here's an example of a general lookup function in Lua that uses two input images, x_lookup and y_lookup, as gradients to remap a source image:
function lookup(source, x_lookup, y_lookup) --<!> adapt to ff standards
-- get the dimensions of the input images
local width, height = source.width, source.height
local x_width, x_height = x_lookup.width, x_lookup.height
local y_width, y_height = y_lookup.width, y_lookup.height
-- create an output image with the same dimensions as the source; make_output_image() is merely an example
local output = make_output_image()
output.width, output.height = width, height
-- iterate over each pixel in the output image
for y = 0, height - 1 do
for x = 0, width - 1 do
-- get the color value of the current pixel in the source image
local r, g, b, a = source:get_pixel(x, y)
-- use the x_lookup and y_lookup images as gradients to remap the color values
local x_value = x_lookup:get_pixel(x % x_width, y % x_height).r
local y_value = y_lookup:get_pixel(x % y_width, y % y_height).r
r = r * x_value
g = g * y_value
b = b * (1 - x_value) * (1 - y_value)
-- set the color value of the current pixel in the output image
output:set_pixel(x, y, r, g, b, a)
end
end
return output
end
--[[
here's how to use this lookup function:
» define two input images, x_lookup and y_lookup, using the get_sample_map function.
» define a source image using the get_sample_map function.
» call the lookup function with the source, x_lookup, and y_lookup images as arguments.
» use the output of the lookup function as the result of the Filter Forge map script component.
» note: this is a general example, so the specific implementation may need to be adjusted to fit the specific use case.
]]--
A version of the `lookup` function, `lookup_in`, that can use `xin` and `yin (derivatives of the`x` and `y` gradients of the `get_sample` function which have undergone modifications) as substitutes for the `x_lookup, y_lookup` to remap `source`.
function lookup_in(xin, yin, source)
local sourceWidth, sourceHeight = get_source_image_size(source)
local x, y = xin * sourceWidth, yin * sourceHeight
local x1, y1 = math.floor(x), math.floor(y)
local x2, y2 = x1 + 1, y1 + 1
local wx2, wy2 = x - x1, y - y1
local wx1, wy1 = 1.0 - wx2, 1.0 - wy2
local s11 = get_sample(source, x1, y1)
local s21 = get_sample(source, x2, y1)
local s12 = get_sample(source, x1, y2)
local s22 = get_sample(source, x2, y2)
local w1 = wx1 * wy1
local w2 = wx2 * wy1
local w3 = wx1 * wy2
local w4 = wx2 * wy2
local x_gradient = (s21 - s11) + wx2 * (s22 - s21 - s12 + s11)
local y_gradient = (s12 - s11) + wy2 * (s22 - s21 - s12 + s11)
local r = w1 * get_sample(source, x1, y1) + w2 * get_sample(source, x2, y1) + w3 * get_sample(source, x1, y2) + w4 * get_sample(source, x2, y2)
local g = w1 * get_sample(xin, x1, y1) + w2 * get_sample(xin, x2, y1) + w3 * get_sample(xin, x1, y2) + w4 * get_sample(xin, x2, y2)
local b = w1 * get_sample(yin, x1, y1) + w2 * get_sample(yin, x2, y1) + w3 * get_sample(yin, x1, y2) + w4 * get_sample(yin, x2, y2)
local a = w1 * get_sample(source, x1, y1, CHANNEL_ALPHA) + w2 * get_sample(source, x2, y1, CHANNEL_ALPHA) + w3 * get_sample(source, x1, y2, CHANNEL_ALPHA) + w4 * get_sample(source, x2, y2, CHANNEL_ALPHA)
return r, g, b, a, x_gradient, y_gradient
end
-- here, `xin` and `yin` are used in place of `x_lookup` and `y_lookup` to sample the gradients. the rest of the function remains the same.
Modified `lookup_in` function with descriptive comments:
function lookup_in(source, xin, yin, x_size, y_size)
-- remaps the 'source' image based on the xin and yin derivatives of the gradients
-- (or lookup tables) specified in x_lookup and y_lookup.
-- x_size and y_size specify the dimensions of the source image and output image.
-- the resulting image is returned as an array of r,g,b,a values.
-- Initialize an array to store the output image.
local output = {}
-- loop through every pixel in the output image.
for y = 0, y_size - 1 do
for x = 0, x_size - 1 do
-- calculate the remapped coordinates for this pixel using the xin and yin gradients.
local u = xin[y * x_size + x + 1]
local v = yin[y * x_size + x + 1]
-- clamp the coordinates to the range [0, 1].
u = math.max(0, math.min(1, u))
v = math.max(0, math.min(1, v))
-- convert the clamped coordinates to pixel coordinates in the source image.
local sx = math.floor(u * x_size + 0.5)
local sy = math.floor(v * y_size + 0.5)
-- get the color of the corresponding pixel in the source image.
local color = source[sy * x_size + sx + 1]
-- add the color to the output array.
table.insert(output, color[1])
table.insert(output, color[2])
table.insert(output, color[3])
table.insert(output, color[4])
end
end
-- return the output array.
return output
end
•• A passing thought, would it be possible for you to write a Sobel edge-detect function in Lua for use in Filter Forge?
Yes, I can provide you with a Sobel edge-detection function written in Lua for use in Filter Forge. Here is an implementation of the Sobel edge detection algorithm that you can use:
function sobel(input)
local width, height = input.width, input.height
local output = {}
for y = 1, height do
for x = 1, width do
local gx = (-1 * input:get(x - 1, y - 1)) + (-2 * input:get(x - 1, y)) + (-1 * input:get(x - 1, y + 1)) +
(1 * input:get(x + 1, y - 1)) + (2 * input:get(x + 1, y)) + (1 * input:get(x + 1, y + 1))
local gy = (-1 * input:get(x - 1, y - 1)) + (-2 * input:get(x, y - 1)) + (-1 * input:get(x + 1, y - 1)) +
(1 * input:get(x - 1, y + 1)) + (2 * input:get(x, y + 1)) + (1 * input:get(x + 1, y + 1))
local magnitude = math.sqrt(gx ^ 2 + gy ^ 2)
output:set(x, y, magnitude)
end
end
return output
end
This function takes an input image as an argument, which is assumed to be a Filter Forge Buffer. The function calculates the gradient of the image using the Sobel operator, and returns an output image that represents the magnitude of the gradient at each pixel.
Note that this implementation assumes that the input image has a border of one pixel around its edges. If your input image does not have a border, you may need to modify the function accordingly.
•• It would be preferrable not to have one. If it creates too much difficulty, I can come up wit a work-around, like first scaling down the image by 1 pixel, doing the edge-detect, then scaling back up once it's done.
In that case, you can modify the sobel function to handle the case where the input image does not have a border. One way to do this is to calculate the gradient using only the pixels that are inside the image, and then set the border pixels of the output image to zero. Here's an updated implementation of the sobel function that works without assuming a border:
function sobel(input)
local width, height = input.width, input.height
local output = Buffer:new(width, height)
for y = 2, height - 1 do
for x = 2, width - 1 do
local gx = (-1 * input:get(x - 1, y - 1)) + (-2 * input:get(x - 1, y)) + (-1 * input:get(x - 1, y + 1)) +
(1 * input:get(x + 1, y - 1)) + (2 * input:get(x + 1, y)) + (1 * input:get(x + 1, y + 1))
local gy = (-1 * input:get(x - 1, y - 1)) + (-2 * input:get(x, y - 1)) + (-1 * input:get(x + 1, y - 1)) +
(1 * input:get(x - 1, y + 1)) + (2 * input:get(x, y + 1)) + (1 * input:get(x + 1, y + 1))
local magnitude = math.sqrt(gx ^ 2 + gy ^ 2)
output:set(x, y, magnitude)
end
end
-- Set border pixels to zero
for x = 1, width do
output:set(x, 1, 0)
output:set(x, height, 0)
end
for y = 1, height do
output:set(1, y, 0)
output:set(width, y, 0)
end
return output
end
This implementation skips the outermost row and column of pixels when calculating the gradient, and then sets the border pixels of the output image to zero using a separate loop. This ensures that the output image has the same dimensions as the input image, but with zeros in the border pixels where the gradient could not be calculated.
function tableToGrid(table)
local grid = {}
for y=0,1,0.01 do
for x=0,1,0.01 do
local r, g, b, a = 0, 0, 0, 0
for i=1,#table do
local dx, dy, dz, dw = x-table[i][1], y-table[i][2], 0, 0
if #table[i] >= 3 then dz = table[i][3] end
if #table[i] >= 4 then dw = table[i][4] end
local attn = 2 - dx^2 - dy^2 - dz^2 - dw^2
if attn > 0 then
attn = attn^4
r = r + attn * table[i][1]
g = g + attn * table[i][2]
b = b + attn * dz
a = a + attn * dw
end
end
grid[#grid+1] = r
grid[#grid+1] = g
grid[#grid+1] = b
grid[#grid+1] = a
end
end
return grid
end
--[[
a lua script function for use in a Filter Forge map script component that can turn a table with 2 to 4 coordinate values (x,y,z,w) into an x,y grid with cells showing `x,y,z,w` as r,g,b,a values. this function takes a table as an argument and returns a flattened grid of r,g,b,a values. you can use the returned grid to create an image.
note that `tableToGrid(table)` assumes that the values in the table are in the range of -1 to 1. If your values are in a different range, you may need to modify the function accordingly.
]]--
function process()
--[[
display the `grid` from `tableToGrid` in r,g,b,a showing the colored cells arranged in x,y = (0 to 1), (0 to 1) as an image in Filter Forge.
uses the following code in the process() function of a Filter Forge map script component:
get the width and height of the image
this code first gets the width and height of the input image using the get_size() function. it then creates a new image using the make_image() function with the same dimensions as the input image.
next, it creates a lookup table to map x,y coordinates to the corresponding grid cells. this is done by dividing the width and height of the image into equal-sized regions based on the size of the grid, and mapping each region to the corresponding grid cell.
finally, it sets the pixel values of the output image based on the corresponding grid cell using the set_pixel() function. the r,g,b,a values of the cell are used to set the pixel values.
the code then returns the output image, which will be displayed in Filter Forge.
]]--
local width, height = get_size()
-- create a new image with the same dimensions as the input image
local img = make_image(width, height)
-- create a lookup table to map x,y coordinates to the grid cells
local grid_size = #grid[1]
--[[
`local grid_size = #grid[1]` creates a local variable called `grid_size` and sets its value to the length of the first sub-table of the `grid` table. in lua, the `#` operator is used to get the length of a table, which is the number of elements in the table when the table has consecutive integer keys starting from 1. in this case, `grid[1]` is a sub-table of the `grid` table, and `#grid[1]` returns the number of elements in this sub-table, which is also the number of cells in a row or column of the `grid` table assuming it's a square grid. the `local` keyword means that the variable is only accessible within the scope of the block of code where it is defined.
]]--
Updated version of the `tableToGrid function` that automatically determines the size of the output image:
function tableToGrid(table)
local grid_size = #table[1]
local output_size = OUTPUT_HEIGHT * OUTPUT_WIDTH
local grid_step = 1 / grid_size
local output = {}
for i = 1, output_size do
local x = (i - 1) % OUTPUT_WIDTH
local y = math.floor((i - 1) / OUTPUT_WIDTH)
local x_idx = math.floor(x / grid_step) + 1
local y_idx = math.floor(y / grid_step) + 1
local r, g, b, a = unpack(table[y_idx][x_idx])
table.insert(output, r)
table.insert(output, g)
table.insert(output, b)
table.insert(output, a)
end
return output
end
-- this version of the function calculates the output size as the product of OUTPUT_HEIGHT and OUTPUT_WIDTH, and uses these values to determine the x and y coordinates of each pixel in the output image.
function get_noise(x,y,z) -- demo reliant on unknown library
local noise = 0
if noise_type == 1 then
noise = math.perlin_noise(x,y,z)
elseif noise_type == 2 then
noise = math.simplex_noise(x,y,z)
elseif noise_type == 3 then
noise = math.cellular_noise(x,y,z)
elseif noise_type == 4 then
noise = math.fractal_noise(x,y,z)
end
return noise
end