From be586dce9fdc5cf4868714244f95e59065e45aa3 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Fri, 12 Jul 2024 15:45:43 +0200 Subject: [PATCH 01/14] removing points from FableLayer Construction. --- .../{fractal_layer.jl => fable_layer.jl} | 81 +++++++++---------- 1 file changed, 40 insertions(+), 41 deletions(-) rename src/structs/layers/{fractal_layer.jl => fable_layer.jl} (77%) diff --git a/src/structs/layers/fractal_layer.jl b/src/structs/layers/fable_layer.jl similarity index 77% rename from src/structs/layers/fractal_layer.jl rename to src/structs/layers/fable_layer.jl index 755b8fb..44129dc 100644 --- a/src/structs/layers/fractal_layer.jl +++ b/src/structs/layers/fable_layer.jl @@ -5,7 +5,6 @@ export FableLayer, default_params, params, CopyToCanvas, to_canvas! mutable struct FableLayer <: AbstractLayer H::Union{Nothing, Hutchinson} H_post::Union{Nothing, Hutchinson} - particles::APT where APT <: AbstractArray{AP} where AP <: AbstractPoint values::AIT where AIT <: AbstractArray{Int} reds::AFT where AFT <: AbstractArray{FT} where FT <: AbstractFloat greens::AFT where AFT <: AbstractArray{FT} where FT <: AbstractFloat @@ -14,6 +13,7 @@ mutable struct FableLayer <: AbstractLayer priorities::AFT where AFT <: Union{AbstractArray{UI}, Nothing} where UI <: Unsigned canvas::ACT where ACT <: AbstractArray{CT} where CT <: RGBA + generator::AbstractGenerator position::Tuple world_size::Tuple ppu::Number @@ -64,10 +64,10 @@ end # Creating a default call -function FableLayer(p, v, r, g, b, a, pr, c, position, world_size, ppu; - postprocessing_steps = AbstractPostProcess[], - config = standard, - H = Hutchinson(), H_post = nothing) +function FableLayer(v, r, g, b, a, pr, c, gen, position, world_size, ppu; + postprocessing_steps = AbstractPostProcess[], + config = standard, + H = Hutchinson(), H_post = nothing) if isa(H, FableOperator) H = Hutchinson(H) end @@ -87,13 +87,13 @@ function FableLayer(p, v, r, g, b, a, pr, c, position, world_size, ppu; end postprocessing_steps = vcat([CopyToCanvas()], postprocessing_steps) - return FableLayer(H, H_post, p, v, r, g, b, a, pr, c, - position, world_size, ppu, - default_params(FableLayer, - config = config, - ArrayType = typeof(v), - FloatType = eltype(v)), - postprocessing_steps) + return FableLayer(H, H_post, p, v, r, g, b, a, pr, c, gen, + position, world_size, ppu, + default_params(FableLayer, + config = config, + ArrayType = typeof(v), + FloatType = eltype(v)), + postprocessing_steps) end # Create a blank, black image of size s @@ -105,7 +105,8 @@ function FableLayer(; config = :meh, ArrayType=Array, FloatType = Float32, numthreads = 256, num_particles = 1000, num_iterations = 1000, dims = 2, H = Hutchinson(), H_post = nothing, - solver_type = :semi_random, overlay = false) + solver_type = :semi_random, overlay = false, + generator = RandGenerator()) if isa(H, FableOperator) H = Hutchinson(H) end @@ -123,8 +124,6 @@ function FableLayer(; config = :meh, ArrayType=Array, FloatType = Float32, num_objects = length(H) res = (ceil(Int, world_size[1]*ppu), ceil(Int, world_size[2]*ppu)) - p = generate_points(num_particles; dims = dims, ArrayType = ArrayType, - num_objects = num_objects) v = ArrayType(zeros(Int,res)) r = ArrayType(zeros(FloatType,res)) g = ArrayType(zeros(FloatType,res)) @@ -137,33 +136,33 @@ function FableLayer(; config = :meh, ArrayType=Array, FloatType = Float32, end c = ArrayType(fill(RGBA(FloatType(0),0,0,0), res)) if config == :standard || config == :fractal_flame - return FableLayer(H, H_post, p, v, r, g, b, a, pr, c, - position, world_size, ppu, - default_params(FableLayer; - ArrayType = ArrayType, - FloatType = FloatType, - config = config, - num_particles = num_particles, - num_iterations = num_iterations, - dims = dims), - postprocessing_steps) + return FableLayer(H, H_post, v, r, g, b, a, pr, c, generator, + position, world_size, ppu, + default_params(FableLayer; + ArrayType = ArrayType, + FloatType = FloatType, + config = config, + num_particles = num_particles, + num_iterations = num_iterations, + dims = dims), + postprocessing_steps) else - return FableLayer(H, H_post, p, v, r, g, b, a, pr, c, - position, world_size, ppu, - params(FableLayer; - ArrayType=ArrayType, - FloatType = FloatType, - gamma = gamma, - logscale = logscale, - calc_max_value = calc_max_value, - max_value = max_value, - numthreads = numthreads, - num_particles = num_particles, - num_iterations = num_iterations, - dims = dims, - solver_type = solver_type, - overlay = overlay), - postprocessing_steps) + return FableLayer(H, H_post, v, r, g, b, a, pr, c, generator, + position, world_size, ppu, + params(FableLayer; + ArrayType=ArrayType, + FloatType = FloatType, + gamma = gamma, + logscale = logscale, + calc_max_value = calc_max_value, + max_value = max_value, + numthreads = numthreads, + num_particles = num_particles, + num_iterations = num_iterations, + dims = dims, + solver_type = solver_type, + overlay = overlay), + postprocessing_steps) end end From 447688995db03afb4cb3b722460614da374c22a7 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Mon, 15 Jul 2024 11:04:23 +0200 Subject: [PATCH 02/14] define -> create --- docs/src/examples/smear.md | 4 ++-- docs/src/examples/swirled_square.md | 4 ++-- docs/src/layering.md | 2 +- docs/src/postprocessing.md | 10 +++++----- examples/barnsley.jl | 2 +- examples/layering.jl | 2 +- examples/logscale.jl | 2 +- examples/multi-object.jl | 6 +++--- examples/postprocessing.jl | 12 ++++++------ examples/sierpinski.jl | 4 ++-- examples/smear.jl | 2 +- examples/square.jl | 2 +- src/fums/shaders.jl | 4 ++-- src/objects/barnsley.jl | 10 +++++----- src/objects/circle.jl | 10 +++++----- src/objects/rectangle.jl | 14 +++++++------- src/objects/triangle.jl | 10 +++++----- src/structs/fable_user_methods.jl | 6 +++--- src/structs/generators.jl | 19 +++++++++++++++++++ test/chaos_tests.jl | 4 ++-- test/layering_tests.jl | 6 +++--- test/object_tests.jl | 4 ++-- 22 files changed, 79 insertions(+), 60 deletions(-) create mode 100644 src/structs/generators.jl diff --git a/docs/src/examples/smear.md b/docs/src/examples/smear.md index 4fbdb44..695970c 100644 --- a/docs/src/examples/smear.md +++ b/docs/src/examples/smear.md @@ -83,7 +83,7 @@ Now we define the ball: ``` # define ball parameters position = [-2.0, -2.0] - ball = define_circle(; position = position, + ball = create_circle(; position = position, radius = 1.0, color = (1,1,1)) ``` @@ -212,7 +212,7 @@ function smear_example(num_particles, num_iterations, total_frames; # define ball parameters object_position = fi("object_position", [-2.0, -2.0]) - ball = define_circle(; position = object_position, + ball = create_circle(; position = object_position, radius = 1.0, color = (1,1,1)) diff --git a/docs/src/examples/swirled_square.md b/docs/src/examples/swirled_square.md index 52979bc..c1a8ba8 100644 --- a/docs/src/examples/swirled_square.md +++ b/docs/src/examples/swirled_square.md @@ -37,7 +37,7 @@ In this case, each row of the array will define the color of a different quadran Now we can define our fractal executable... ``` -H = define_square(; position = [0.0, 0.0], rotation = pi/4, color = colors) +H = create_square(; position = [0.0, 0.0], rotation = pi/4, color = colors) ``` Here, `ArrayType` can be either an `Array` or `CuArray` depending whether you would like to run the code on the CPU or (CUDA / AMD) GPU. @@ -159,7 +159,7 @@ function square_example(num_particles, num_iterations; [0.25, 0.25, 1.0, 1], [1.0, 0.25, 1.0, 1]] - H = define_square(; position = [0.0, 0.0], rotation = pi/4, color = colors) + H = create_square(; position = [0.0, 0.0], rotation = pi/4, color = colors) swirl_operator = fo(Flames.swirl) H_post = nothing if transform_type == :outer_swirl diff --git a/docs/src/layering.md b/docs/src/layering.md index 4293889..4837523 100644 --- a/docs/src/layering.md +++ b/docs/src/layering.md @@ -114,7 +114,7 @@ function layering_example(num_particles, num_iterations; ArrayType = Array) world_size = (9*0.15, 16*0.15) ppu = 1920/world_size[2] - square = define_rectangle(position = [0.0,0.0], + square = create_rectangle(position = [0.0,0.0], rotation = pi/4, color = RGBA(1,0,1)) flayer = FableLayer(; ArrayType = ArrayType, H1 = square, diff --git a/docs/src/postprocessing.md b/docs/src/postprocessing.md index 211b2b5..38defe0 100644 --- a/docs/src/postprocessing.md +++ b/docs/src/postprocessing.md @@ -28,7 +28,7 @@ For the following examples, we will be performing post processing on a simple ci ``` function quick_circle(num_particles, num_iterations; ArrayType = Array, filename = "out.png") - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) fl = FableLayer(; H1 = circle, ArrayType = ArrayType) @@ -75,7 +75,7 @@ Here is a quick example: function clip_example(num_particles, num_iterations; ArrayType = Array, filename = "clip_out.png") - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) clip = Clip(; threshold = 0.5, color = RGBA(1, 1, 0, 1)) @@ -165,7 +165,7 @@ Finally, here is a quick example using the Gaussian `Blur` post process (note yo ``` function blur_example(num_particles, num_iterations; ArrayType = Array, filter_size = 3, filename = "blur_out.png") - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) blur = Blur(; filter_size = filter_size) @@ -197,7 +197,7 @@ Here is a quick example using the Sobel operator: ``` function sobel_example(num_particles, num_iterations; ArrayType = Array, filename = "sobel_out.png") - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) sobel = Sobel() @@ -244,7 +244,7 @@ Here is a quick example using the Outline post process: function outline_example(num_particles, num_iterations; ArrayType = Array, filename = "outline_out.png", linewidth = 1, object_outline = false) - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) outline = Outline(; linewidth = linewidth, object_outline = object_outline) diff --git a/examples/barnsley.jl b/examples/barnsley.jl index f4d3493..95d720c 100644 --- a/examples/barnsley.jl +++ b/examples/barnsley.jl @@ -24,7 +24,7 @@ function barnsley_example(num_particles, num_iterations; color_3 = [0.,1,0,1] color_4 = [0.,0,1,1] - H = define_barnsley(; color = [color_1, color_2, color_3, color_4]) + H = create_barnsley(; color = [color_1, color_2, color_3, color_4]) fo_1 = fo(Flames.identity, Shaders.previous, 1) fo_2 = fo(scale_and_translate(translation = (0.5, 0.5), scale = 0.5), diff --git a/examples/layering.jl b/examples/layering.jl index 323d53c..1299326 100644 --- a/examples/layering.jl +++ b/examples/layering.jl @@ -5,7 +5,7 @@ function layering_example(num_particles, num_iterations; ArrayType = Array) world_size = (9*0.15, 16*0.15) ppu = 1920/world_size[2] - square = define_rectangle(position = [0.0,0.0], + square = create_rectangle(position = [0.0,0.0], rotation = pi/4, color = RGBA(1,0,1)) flayer = FableLayer(; ArrayType = ArrayType, H = square, diff --git a/examples/logscale.jl b/examples/logscale.jl index 6fd3dbe..dbe0e6d 100644 --- a/examples/logscale.jl +++ b/examples/logscale.jl @@ -5,7 +5,7 @@ function logscale_example(num_particles, num_iterations; ArrayType = Array) world_size = (9*0.15, 16*0.15) ppu = 1920/world_size[2] - circle = define_circle(chosen_fx = :naive_disk, color = Shaders.white) + circle = create_circle(chosen_fx = :naive_disk, color = Shaders.white) flayer = FableLayer(; ArrayType = ArrayType, H = circle, world_size = world_size, ppu = ppu, diff --git a/examples/multi-object.jl b/examples/multi-object.jl index 3332681..e4fd7c6 100644 --- a/examples/multi-object.jl +++ b/examples/multi-object.jl @@ -7,11 +7,11 @@ function multi_example(num_particles, num_iterations; ppu = 1920/world_size[2] - square = define_square(; position = [-0.25, -0.25], scale = 0.25, + square = create_square(; position = [-0.25, -0.25], scale = 0.25, color = Shaders.blue) - square_2 = define_square(; position = [-0.25, 0.25], scale = 0.25, + square_2 = create_square(; position = [-0.25, 0.25], scale = 0.25, color = Shaders.magenta) - circle = define_circle(; position = [0.25, 0.0], radius = 0.5, + circle = create_circle(; position = [0.25, 0.0], radius = 0.5, color = Shaders.red) final_H = Hutchinson([square_2, circle, square]) diff --git a/examples/postprocessing.jl b/examples/postprocessing.jl index 791dda2..bcbb6ce 100644 --- a/examples/postprocessing.jl +++ b/examples/postprocessing.jl @@ -2,7 +2,7 @@ using Fable, Colors function quick_circle(num_particles, num_iterations; ArrayType = Array, filename = "out.png") - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) fl = FableLayer(; H = circle, ArrayType = ArrayType) @@ -13,7 +13,7 @@ end function clip_example(num_particles, num_iterations; ArrayType = Array, filename = "clip_out.png") - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) clip = Clip(; threshold = 0.5, color = RGBA(1, 1, 0, 1)) @@ -26,7 +26,7 @@ end function identity_example(num_particles, num_iterations; ArrayType = Array, filename = "identity_out.png") - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) identity = Identity() @@ -39,7 +39,7 @@ end function blur_example(num_particles, num_iterations; ArrayType = Array, filter_size = 3, filename = "blur_out.png") - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) blur = Blur(; filter_size = filter_size) @@ -52,7 +52,7 @@ end function sobel_example(num_particles, num_iterations; ArrayType = Array, filename = "sobel_out.png") - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) sobel = Sobel() @@ -66,7 +66,7 @@ end function outline_example(num_particles, num_iterations; ArrayType = Array, filename = "outline_out.png", linewidth = 1, object_outline = false) - circle = define_circle(; radius = 0.1, color = [1, 0, 1, 1]) + circle = create_circle(; radius = 0.1, color = [1, 0, 1, 1]) outline = Outline(; linewidth = linewidth, object_outline = object_outline) diff --git a/examples/sierpinski.jl b/examples/sierpinski.jl index 63eb8e6..6c80122 100644 --- a/examples/sierpinski.jl +++ b/examples/sierpinski.jl @@ -18,12 +18,12 @@ function sierpinski_example(num_particles, num_iterations, num_frames; B_2 = fi(:B_2, [r*cos(-theta + 2*pi/3), r*sin(-theta + 2*pi/3)]) C_2 = fi(:C_2, [r*cos(-theta + 4*pi/3), r*sin(-theta + 4*pi/3)]) - H = define_triangle(; A = A_1, B = B_1, C = C_1, + H = create_triangle(; A = A_1, B = B_1, C = C_1, color = [[1.0, 0.0, 0.0, 1.0], [0.0, 1.0, 0.0, 1.0], [0.0, 0.0, 1.0, 1.0]], chosen_fx = :sierpinski) - H_2 = define_triangle(A = A_2, B = B_2, C = C_2, + H_2 = create_triangle(A = A_2, B = B_2, C = C_2, color = [[0.0, 1.0, 1.0, 1.0], [1.0, 0.0, 1.0, 1.0], [1.0, 1.0, 0.0, 1.0]], diff --git a/examples/smear.jl b/examples/smear.jl index 1efdee5..b423715 100644 --- a/examples/smear.jl +++ b/examples/smear.jl @@ -16,7 +16,7 @@ function smear_example(num_particles, num_iterations, total_frames; # define ball parameters object_position = fi("object_position", [-2.0, -2.0]) - ball = define_circle(; position = object_position, + ball = create_circle(; position = object_position, radius = 1.0, color = (1,1,1)) diff --git a/examples/square.jl b/examples/square.jl index 76d33c7..f834b32 100644 --- a/examples/square.jl +++ b/examples/square.jl @@ -25,7 +25,7 @@ function square_example(num_particles, num_iterations; [1.0, 0.25, 1.0, 1]] rot_fi = fi("rotation", pi/4) - square = define_square(; position = [0.0, 0.0], rotation = rot_fi, + square = create_square(; position = [0.0, 0.0], rotation = rot_fi, color = colors) swirl_operator = fo(Flames.swirl) H_post = nothing diff --git a/src/fums/shaders.jl b/src/fums/shaders.jl index 5b3d510..b01b86c 100644 --- a/src/fums/shaders.jl +++ b/src/fums/shaders.jl @@ -42,13 +42,13 @@ function create_color(a::RGBA) return Shaders.custom(r = a.r, g = a.g, b = a.b, a = a.alpha) end -function define_color_operators(color::Union{RGBA, RGB, FableUserMethod}; +function create_color_operators(color::Union{RGBA, RGB, FableUserMethod}; fnum = 4) color = create_color(color) return [color for i = 1:fnum] end -function define_color_operators(t_color::Union{Tuple, Vector}; fnum = 4) +function create_color_operators(t_color::Union{Tuple, Vector}; fnum = 4) if eltype(t_color) <: FableUserMethod return [t_color for i = 1:fnum] end diff --git a/src/objects/barnsley.jl b/src/objects/barnsley.jl index 2a882f8..3403007 100644 --- a/src/objects/barnsley.jl +++ b/src/objects/barnsley.jl @@ -1,13 +1,13 @@ -export define_barnsley -function define_barnsley(; color = Shaders.grey, tilt = 0) - fums = define_barnsley_operators(tilt = tilt) - color_set = define_color_operators(color; fnum = 4) +export create_barnsley +function create_barnsley(; color = Shaders.grey, tilt = 0) + fums = create_barnsley_operators(tilt = tilt) + color_set = create_color_operators(color; fnum = 4) prob_set = (0.01, 0.85, 0.07, 0.07) return fo(fums, color_set, prob_set) end # This specifically returns the fums for a barnsley fern -function define_barnsley_operators(; tilt::Union{Number, FableInput} = 0) +function create_barnsley_operators(; tilt::Union{Number, FableInput} = 0) s_1 = @fum function s_1() return point(0.16*y, 0.0) diff --git a/src/objects/circle.jl b/src/objects/circle.jl index d8a2e1f..c8a93a5 100644 --- a/src/objects/circle.jl +++ b/src/objects/circle.jl @@ -1,4 +1,4 @@ -export define_circle +export create_circle # Code examples modified from: https://www.math.uwaterloo.ca/~wgilbert/FableGallery/IFS/IFS.html @@ -49,18 +49,18 @@ constant_disk = @fum function constant_disk(x, y; radius = 1, end # Returns back H, colors, and probs for a circle -function define_circle(; position::Union{Tuple, Vector, FableInput} = (0, 0), +function create_circle(; position::Union{Tuple, Vector, FableInput} = (0, 0), radius::Union{Number, FableInput} = 1.0, color = Shaders.gray, chosen_fx = :constant_disk) - fums = define_circle_operators(position, radius; chosen_fx = chosen_fx) - color_set = define_color_operators(color; fnum = 2) + fums = create_circle_operators(position, radius; chosen_fx = chosen_fx) + color_set = create_color_operators(color; fnum = 2) return fo(fums, color_set, (0.5, 0.5)) end # This specifically returns the fums for a circle -function define_circle_operators(position::Union{Vector, Tuple, FableInput}, +function create_circle_operators(position::Union{Vector, Tuple, FableInput}, radius::Union{Number, FableInput}; chosen_fx = :constant_disk) diff --git a/src/objects/rectangle.jl b/src/objects/rectangle.jl index 22bdbca..f17e341 100644 --- a/src/objects/rectangle.jl +++ b/src/objects/rectangle.jl @@ -1,4 +1,4 @@ -export define_rectangle, define_square +export create_rectangle, create_square # Returns back H, colors, and probs for a square rectangle_object = @fum function rectangle_object(y,x; @@ -33,30 +33,30 @@ rectangle_object = @fum function rectangle_object(y,x; return point(0.5*(p_y + y), 0.5*(p_x + x)) end -function define_rectangle(; position::Union{Vector, Tuple, FableInput}=(0,0), +function create_rectangle(; position::Union{Vector, Tuple, FableInput}=(0,0), rotation::Union{Number, FableInput} = 0.0, scale_x::Union{Number, FableInput} = 1.0, scale_y::Union{Number, FableInput} = 1.0, color = Shaders.grey) - fums = define_rectangle_operators(position, rotation, scale_x, scale_y) - color_set = define_color_operators(color; fnum = 4) + fums = create_rectangle_operators(position, rotation, scale_x, scale_y) + color_set = create_color_operators(color; fnum = 4) return fo(fums, color_set, (0.25 for i = 1:4)) end # Returns back H, colors, and probs for a square -function define_square(; position::Union{Vector, Tuple, FableInput}=(0,0), +function create_square(; position::Union{Vector, Tuple, FableInput}=(0,0), rotation::Union{Number, FableInput} = 0.0, scale::Union{Number, FableInput} = 1.0, color = Shaders.grey) - return define_rectangle(; position = position, rotation = rotation, + return create_rectangle(; position = position, rotation = rotation, scale_x = scale, scale_y = scale, color = color) end # This specifically returns the fums for a square -function define_rectangle_operators(position::Union{Vector,Tuple,FableInput}, +function create_rectangle_operators(position::Union{Vector,Tuple,FableInput}, rotation::Union{Number, FableInput}, scale_x::Union{Number, FableInput}, scale_y::Union{Number, FableInput}) diff --git a/src/objects/triangle.jl b/src/objects/triangle.jl index d8ebf36..463ea1c 100644 --- a/src/objects/triangle.jl +++ b/src/objects/triangle.jl @@ -1,4 +1,4 @@ -export define_triangle +export create_triangle triangle_fill = @fum function triangle_fill(x,y; A = (0,0), @@ -12,25 +12,25 @@ triangle_fill = @fum function triangle_fill(x,y; return point(y,x) end -function define_triangle(; A::Union{Vector,Tuple,FableInput}=(sqrt(3)/4,-0.5), +function create_triangle(; A::Union{Vector,Tuple,FableInput}=(sqrt(3)/4,-0.5), B::Union{Vector,Tuple,FableInput}=(-sqrt(3)/4,0), C::Union{Vector,Tuple,FableInput}=(sqrt(3)/4,0.5), color = Shaders.gray, chosen_fx = :fill) - fums = define_triangle_operators(A, B, C; chosen_fx = chosen_fx) + fums = create_triangle_operators(A, B, C; chosen_fx = chosen_fx) fnum = 3 if chosen_fx == :fill fnum = 4 end - color_set = define_color_operators(color; fnum = fnum) + color_set = create_color_operators(color; fnum = fnum) @inbounds return fo(fums, color_set, Tuple([1/fnum for i = 1:fnum])) end # This specifically returns the fums for a triangle triangle -function define_triangle_operators(A::Union{Vector, Tuple, FableInput}, +function create_triangle_operators(A::Union{Vector, Tuple, FableInput}, B::Union{Vector, Tuple, FableInput}, C::Union{Vector, Tuple, FableInput}; chosen_fx = :fill) diff --git a/src/structs/fable_user_methods.jl b/src/structs/fable_user_methods.jl index 051526d..0e56e15 100644 --- a/src/structs/fable_user_methods.jl +++ b/src/structs/fable_user_methods.jl @@ -32,7 +32,7 @@ end # NamedTuple{Tuple(args.(kwargs[:],1))}(Tuple(args.(kwargs[:],2))) #end -function __define_fum_stuff(expr, config, mod, force_inbounds) +function __create_fum_stuff(expr, config, mod, force_inbounds) def = MacroTools.splitdef(expr) def[:name] = name = Symbol(def[:name], :_fum) used_args = def[:args] @@ -81,14 +81,14 @@ macro fum(ex...) elseif expr.head == :(=) # inline function definitions if isa(expr.args[1], Expr) - kwargs, fum_fx = __define_fum_stuff(expr, config, __module__, + kwargs, fum_fx = __create_fum_stuff(expr, config, __module__, force_inbounds) else error("Cannot create FableUserMethod.\n"* "Input is not a valid function definition!") end elseif expr.head == :function - kwargs, fum_fx = __define_fum_stuff(expr, config, __module__, + kwargs, fum_fx = __create_fum_stuff(expr, config, __module__, force_inbounds) else error("Cannot convert expr to Fable User Method!") diff --git a/src/structs/generators.jl b/src/structs/generators.jl new file mode 100644 index 0000000..7ff5b64 --- /dev/null +++ b/src/structs/generators.jl @@ -0,0 +1,19 @@ +export AbstractGenerator, ChaosGenerator + +# All AbstractGenerators should also have `args` and `iterations` +abstract type AbstractGenerator end; + +struct ChaosGenerator{A, I} + args::A + iterations::I +end + +run(gen::AbstractGenerator, tid) = simple_rand(quick_seed(ttid)) + +function chaos_game(tid, bounds, random_function, f_set, dims, n) + pt = randon_function(seed(tid), bounds, dims...) + + # now do Chaos Game for n iterations +end + +gen = ChaosGenerator(ChaosGame, (bounds, random_function, f_set...), 1000) diff --git a/test/chaos_tests.jl b/test/chaos_tests.jl index b4f25e9..3cc0601 100644 --- a/test/chaos_tests.jl +++ b/test/chaos_tests.jl @@ -19,7 +19,7 @@ end #------------------------------------------------------------------------------# function chaos_tests(ArrayType::Type{AT}) where AT <: AbstractArray - H = Fable.define_circle(; position = [0.0,0.0], radius = 2.0, + H = Fable.create_circle(; position = [0.0,0.0], radius = 2.0, color = [1.0, 1.0, 1.0, 1.0], chosen_fx = :naive_disk) @@ -36,7 +36,7 @@ function chaos_tests(ArrayType::Type{AT}) where AT <: AbstractArray GC.gc() - H_post = Fable.define_circle(; position = [0.0,0.0], radius = 2.0, + H_post = Fable.create_circle(; position = [0.0,0.0], radius = 2.0, color = [1.0, 1.0, 1.0, 1.0], chosen_fx = :constant_disk) fl = FableLayer(; ArrayType = ArrayType, config = :simple, H = H_post, diff --git a/test/layering_tests.jl b/test/layering_tests.jl index c70c677..39fbfe4 100644 --- a/test/layering_tests.jl +++ b/test/layering_tests.jl @@ -22,7 +22,7 @@ function layering_tests(ArrayType::Type{AT}) where AT <: AbstractArray ArrayType = ArrayType) sl = ShaderLayer(test_fum; world_size = (4, 4), ppu = 11/4, ArrayType = ArrayType) - circle = Fable.define_circle(; position = [0.0,0.0], radius = 2.0, + circle = Fable.create_circle(; position = [0.0,0.0], radius = 2.0, color = [0.0, 0.0, 1.0, 1.0]) fl = FableLayer(; world_size = (4,4), ppu = 11/4, H = circle, ArrayType = ArrayType) @@ -102,9 +102,9 @@ function layer_mixing_tests(ArrayType::Type{AT}) where AT <: AbstractArray @test img[2] == RGBA(1,1,1,1) clayer = ColorLayer(RGB(0.5, 0.5, 0.5); world_size = (1, 2), ppu = 1) - square_1 = define_square(; color = Shaders.black, + square_1 = create_square(; color = Shaders.black, position = (0, -0.5), scale = 1) - square_2 = define_square(; color = Shaders.white, + square_2 = create_square(; color = Shaders.white, position = (0, 0.5), scale = 1) flayer_1 = FableLayer(; H = square_1, world_size = (1, 2), ppu = 1, diff --git a/test/object_tests.jl b/test/object_tests.jl index 4acd4d7..c430803 100644 --- a/test/object_tests.jl +++ b/test/object_tests.jl @@ -9,7 +9,7 @@ function square_tests(ArrayType::Type{AT}) where AT <: AbstractArray [0.0, 0, 1, 1], [1.0, 0, 1, 1]] - square = Fable.define_square(; position = [0.0,0.0], scale = 4.0, + square = Fable.create_square(; position = [0.0,0.0], scale = 4.0, color = color_array) fl = FableLayer(; H = square, ArrayType = ArrayType, world_size = (4,4), ppu = 2.5) @@ -31,7 +31,7 @@ function triangle_tests(ArrayType::Type{AT}) where AT <: AbstractArray [0.0, 0, 1, 1], [1.0, 0, 1, 1]] - triangle = Fable.define_triangle(; color = color_array) + triangle = Fable.create_triangle(; color = color_array) fl = FableLayer(; H = triangle, ArrayType = ArrayType, world_size = (1,1), ppu = 11) From 79d75b41a4110cfd875faa845d4d3e033ebe41c3 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Tue, 16 Jul 2024 11:27:00 +0200 Subject: [PATCH 03/14] mucking around with generators --- src/Fable.jl | 1 + src/structs/fable_operators.jl | 10 +++++----- src/structs/generators.jl | 25 ++++++++++++++----------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/Fable.jl b/src/Fable.jl index 51344ef..27c0c13 100644 --- a/src/Fable.jl +++ b/src/Fable.jl @@ -30,6 +30,7 @@ include("utils/geometries.jl") include("utils/histogram.jl") # Fable flame structures +include("structs/generators.jl") include("structs/fable_input.jl") include("structs/fable_user_methods.jl") include("fums/shaders.jl") diff --git a/src/structs/fable_operators.jl b/src/structs/fable_operators.jl index 58e9f2d..c59d960 100644 --- a/src/structs/fable_operators.jl +++ b/src/structs/fable_operators.jl @@ -12,11 +12,11 @@ function prob_check(probs::T) where T <: Tuple end end -struct FableOperator - ops::Tuple - colors::Tuple - probs::Tuple - fnums::Union{Int, Tuple} +struct FableOperator{O, C, G} where {O <: Tuple, C <: Tuple, + g <: AbstractGenerator + ops::O + colors::C + gen::G end fo(args...; kwargs...) = FableOperator(args...; kwargs...) diff --git a/src/structs/generators.jl b/src/structs/generators.jl index 7ff5b64..91ee3b6 100644 --- a/src/structs/generators.jl +++ b/src/structs/generators.jl @@ -1,19 +1,22 @@ -export AbstractGenerator, ChaosGenerator +#-------------generators.jl----------------------------------------------------# +# +# Purpose: Generators are types used to determine how the final `run` function +# is `@generated` at the end. To see how this works, look at the +# files in the `run/` directory +# +#------------------------------------------------------------------------------# +export AbstractGenerator, ChaosGenerator, StandardGenerator -# All AbstractGenerators should also have `args` and `iterations` abstract type AbstractGenerator end; -struct ChaosGenerator{A, I} +struct ChaosGenerator{A, I, P, F} where {I <: Integer, P <: Tuple, F <: Tuple} args::A iterations::I + prob_set::P + fnums::F end -run(gen::AbstractGenerator, tid) = simple_rand(quick_seed(ttid)) - -function chaos_game(tid, bounds, random_function, f_set, dims, n) - pt = randon_function(seed(tid), bounds, dims...) - - # now do Chaos Game for n iterations +struct StandardGenerator{A, ND} + args::A + ndrange::ND end - -gen = ChaosGenerator(ChaosGame, (bounds, random_function, f_set...), 1000) From 6a993834afc32c293ceac1d799580d0e3e17cd55 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Tue, 16 Jul 2024 13:27:08 +0200 Subject: [PATCH 04/14] major refactoring to fable inputs and user methods --- src/Fable.jl | 56 ++++++------ src/structs/fable_input.jl | 70 +++++---------- src/structs/fable_user_methods.jl | 141 ++++++++++++++---------------- src/structs/generators.jl | 4 +- 4 files changed, 120 insertions(+), 151 deletions(-) diff --git a/src/Fable.jl b/src/Fable.jl index 27c0c13..df552cc 100644 --- a/src/Fable.jl +++ b/src/Fable.jl @@ -33,43 +33,43 @@ include("utils/histogram.jl") include("structs/generators.jl") include("structs/fable_input.jl") include("structs/fable_user_methods.jl") -include("fums/shaders.jl") -include("structs/fable_operators.jl") -include("structs/fable_executable/fable_executable.jl") -include("structs/fable_executable/hutchinson.jl") -include("structs/fable_executable/shader.jl") +#include("fums/shaders.jl") +#include("structs/fable_operators.jl") +#include("structs/fable_executable/fable_executable.jl") +#include("structs/fable_executable/hutchinson.jl") +#include("structs/fable_executable/shader.jl") # Operations -include("utils/simple_rng.jl") -include("fums/flames.jl") -include("fums/smears.jl") +#include("utils/simple_rng.jl") +#include("fums/flames.jl") +#include("fums/smears.jl") # Shapes -include("objects/rectangle.jl") -include("objects/circle.jl") -include("objects/triangle.jl") -include("objects/barnsley.jl") +#include("objects/rectangle.jl") +#include("objects/circle.jl") +#include("objects/triangle.jl") +#include("objects/barnsley.jl") # IO -include("structs/layers/layers.jl") -include("io/postprocess/postprocess.jl") -include("structs/layers/fractal_layer.jl") -include("structs/layers/color_layer.jl") -include("structs/layers/image_layer.jl") -include("structs/layers/shader_layer.jl") -include("structs/video_params.jl") -include("io/io_tools.jl") +#include("structs/layers/layers.jl") +#include("io/postprocess/postprocess.jl") +#include("structs/layers/fractal_layer.jl") +#include("structs/layers/color_layer.jl") +#include("structs/layers/image_layer.jl") +#include("structs/layers/shader_layer.jl") +#include("structs/video_params.jl") +#include("io/io_tools.jl") # PostProcessing -include("io/postprocess/clip.jl") -include("io/postprocess/filter.jl") -include("io/postprocess/sobel.jl") -include("io/postprocess/outline.jl") +#include("io/postprocess/clip.jl") +#include("io/postprocess/filter.jl") +#include("io/postprocess/sobel.jl") +#include("io/postprocess/outline.jl") # Main file -include("run/run.jl") -include("run/fractal_flame.jl") -include("run/shader.jl") -include("run/color.jl") +#include("run/run.jl") +#include("run/fractal_flame.jl") +#include("run/shader.jl") +#include("run/color.jl") end # module diff --git a/src/structs/fable_input.jl b/src/structs/fable_input.jl index 5ee9a40..8bdc562 100644 --- a/src/structs/fable_input.jl +++ b/src/structs/fable_input.jl @@ -1,53 +1,29 @@ -export FableInput, fi, @fi, set!, combine, to_args, find_fi_index, value - -struct FableInput - s::Union{Symbol, String} - x::Ref{Union{Number, Tuple, Vector}} +#-------------fable_input------------------------------------------------------# +# +# Purpose: This file is meant to create fable inputs for Fable +# Fable Inputs are any values meant to be modified by the user +# within a Fable executable to avoid recompilation. +# +# Notes: Maybe save as pointers to expressions for usability? +# +#------------------------------------------------------------------------------# +export FableInput, @fi + +struct FableInput{E} + expr::E end -fi(args...) = FableInput(args...) - -function set!(fi::FableInput, val) - fi.x.x = val -end +""" + x = @fi 7 -function combine(nt::Tuple, fis::Tuple) - return Tuple(combine(nt[i], fis[i]) for i = 1:length(fis)) -end + or -combine(fis::Vector{FableInput}, nt::NamedTuple) = combine(nt, fis) + x = @fi 7*y+5 -function combine(nt::NamedTuple, fis::Vector{FableInput}) - if length(fis) == 0 - return nt - end - - fi_vals = (remove_vectors(fis[i].x.x) for i = 1:length(fis)) - fi_keys = (Symbol(fis[i].s) for i = 1:length(fis)) - - return NamedTuple{(keys(nt)..., fi_keys...)}((values(nt)..., fi_vals...)) +Marks the variable `x` as a FableInput -- a dynamic variable passed in to the +final executable at runtime. +This avoids recompilation of code for variables that are dynamically changing. +""" +macro fi(ex) + esc(FableInput(ex)) end - -function to_args(nt::Tuple, fis::Tuple) - return Tuple(combine(fis[i], nt[i]) for i = 1:length(fis)) -end - -function to_args(nt::NamedTuple, fis::Vector{FableInput}) - if length(fis) == 0 - return values(nt) - end - fi_vals = (remove_vectors(fis[i].x.x) for i = 1:length(fis)) - return (values(nt)..., fi_vals...) -end - -function find_fi_index(s, fis::FT) where FT <: Union{Vector{FableInput}, - Tuple} - for i = 1:length(fis) - if Symbol(fis[i].s) == Symbol(s) - return i - end - end -end - -value(fi::FableInput) = fi.x.x -value(a) = a diff --git a/src/structs/fable_user_methods.jl b/src/structs/fable_user_methods.jl index 0e56e15..2580d51 100644 --- a/src/structs/fable_user_methods.jl +++ b/src/structs/fable_user_methods.jl @@ -1,102 +1,94 @@ +#-------------fable-user_methods-----------------------------------------------# +# +# Purpose: This file is meant to define how users interact with fable user +# methods. +# fums are essentially function fragments that will be compiled at +# a later stage of the Fable pipeline +# +# Notes: I think the best way to carry fis with the user methods is +# to define a constructor function that reads in the expr block and +# kwargs, and then parses out the fis from that list. Then at the +# end of the @fum macro, I can call into that constructor. +# We could get fanc with our own @generated macro, but... +# +#------------------------------------------------------------------------------# export FableUserMethod, @fum -struct FableUserMethod{NT <: NamedTuple, - V <: Vector{FableInput}, - F <: Function} - kwargs::NT - fis::V - fx::F +struct FableUserMethod{E, F} + body::E + fis::F end + args(a,n) = a.args[n] -function __set_args(args, config) - if config == :fractal - correct_args = [:y, :x, :frame] - elseif config == :shader - correct_args = [:y, :x, :color, :frame] +function __warn_args(args, config) + correct_args = (:y, :x, :frame, :color) + max_arg_idx = 3 + if config == :color + max_arg_idx = 4 end - if !issubset(args, correct_args) + if !issubset(args, correct_args[1:max_arg_idx]) error("Function arguments must be one of the following:\n"* - string(correct_args)*"\n"* + string(correct_args[1:max_arg_idx])*"\n"* "Please use key-word arguments for any additional arguments!") end - return correct_args end -# this function can create a NamedTuple from kwargs in a macro, ie: -# kwargs = __to_NamedTuple(def[:kwargs]) -# It is not currently used, but took a while to find out, so I'm leaving it -# here for debugging purposes -#function __to_NamedTuple(kwargs) -# NamedTuple{Tuple(args.(kwargs[:],1))}(Tuple(args.(kwargs[:],2))) -#end - -function __create_fum_stuff(expr, config, mod, force_inbounds) - def = MacroTools.splitdef(expr) - def[:name] = name = Symbol(def[:name], :_fum) - used_args = def[:args] - args = __set_args(used_args, config) - def[:args] = args - if force_inbounds - body_qt = quote - @inbounds $(def[:body]) - end - def[:body] = body_qt - end - kwargs = NamedTuple() - fum_fx = combinedef(def) - return kwargs, Core.eval(mod, fum_fx) -end +""" + f = @fum f(x) = x+1 + +Defines a Fable User Method (f) that can be used along with other fums to +build a Fable Executable. +Note that these are not compiled until the Fable Executable is built! +This means you will not have error checking until later in the process! + +You may use `:color` to specify that the fum is meant for coloring. +""" +macro fum(exprs...) -# Note: this operator currently works like this: -# f = @fum function config f(x) x+1 end -# There should be a way to define f in this macro, so we don't need to -# say `f = @fum function ...`, but instead just `@fum function ...` -macro fum(ex...) - - config = :fractal - force_inbounds = false - - if length(ex) == 1 - else - for i = 1:length(ex)-1 - if ex[i] == :color || ex[i] == :shader || - ex[i] == :(:shader) || ex[i] == :(:color) - config = :shader - elseif ex[i] isa Expr && ex[i].head == :(=) && - ex[i].args[1] == :inbounds && ex[i].args[2] isa Bool - force_inbounds = ex[i].args[2] + config = :default + + if length(exprs) != 1 + for i = 1:length(exprs)-1 + if exprs[i] == :color || exprs[i] == :shader || + exprs[i] == :(:shader) || exprs[i] == :(:color) + config = :color else error("Incorrect config argument ", ex[i], "!") end end end - expr = ex[end] - kwargs = nothing + ex = exprs[end] - if isa(expr, Symbol) - error("Cannot convert Symbol to Fable User Method!") - elseif expr.head == :(=) - # inline function definitions - if isa(expr.args[1], Expr) - kwargs, fum_fx = __create_fum_stuff(expr, config, __module__, - force_inbounds) - else - error("Cannot create FableUserMethod.\n"* - "Input is not a valid function definition!") + if !isa(ex, Expr) + error("Cannot convert single inputs to Fable User Method!") + elseif ex.head == :(=) && !(isa(ex.args[1], Expr)) + error("Invalid function definition!") + end + + def = MacroTools.splitdef(ex) + __warn_args(def[:args], config) + kwargs = def[:kwargs] + fis = Tuple(FableInput[]) + for i = 1:length(kwargs) + if !isa(kwargs[i], Expr) + error("fum key word arguments must be an expression, like "* + "`q = 5`") + end + kwargs[i].head = :(=) + if isa(kwargs[i].args[2], FableInput) + fis = (fis..., kwargs[i].args[2]) end - elseif expr.head == :function - kwargs, fum_fx = __create_fum_stuff(expr, config, __module__, - force_inbounds) - else - error("Cannot convert expr to Fable User Method!") end - return FableUserMethod(kwargs, FableInput[], fum_fx) + final_ex = Expr(:block, kwargs..., def[:body]) + return esc(FableUserMethod(Expr(:block, def[:kwargs]..., def[:body]), + fis)) end +#= function (a::FableUserMethod)(args...; kwargs...) @@ -151,3 +143,4 @@ function (a::FableUserMethod)(args...; kwargs...) final_kwargs = NamedTuple{ks}(vals) return FableUserMethod(final_kwargs, fis, a.fx) end +=# diff --git a/src/structs/generators.jl b/src/structs/generators.jl index 91ee3b6..47aaabb 100644 --- a/src/structs/generators.jl +++ b/src/structs/generators.jl @@ -9,14 +9,14 @@ export AbstractGenerator, ChaosGenerator, StandardGenerator abstract type AbstractGenerator end; -struct ChaosGenerator{A, I, P, F} where {I <: Integer, P <: Tuple, F <: Tuple} +struct ChaosGenerator{A, I, P, F} <: AbstractGenerator args::A iterations::I prob_set::P fnums::F end -struct StandardGenerator{A, ND} +struct StandardGenerator{A, ND} <: AbstractGenerator args::A ndrange::ND end From 81e02558ffd87da57dfa38a8c1930eb9fd3424ca Mon Sep 17 00:00:00 2001 From: James Schloss Date: Wed, 17 Jul 2024 10:49:54 +0200 Subject: [PATCH 05/14] Revert "major refactoring to fable inputs and user methods" This reverts commit 6a993834afc32c293ceac1d799580d0e3e17cd55. --- src/Fable.jl | 56 ++++++------ src/structs/fable_input.jl | 70 ++++++++++----- src/structs/fable_user_methods.jl | 141 ++++++++++++++++-------------- src/structs/generators.jl | 4 +- 4 files changed, 151 insertions(+), 120 deletions(-) diff --git a/src/Fable.jl b/src/Fable.jl index df552cc..27c0c13 100644 --- a/src/Fable.jl +++ b/src/Fable.jl @@ -33,43 +33,43 @@ include("utils/histogram.jl") include("structs/generators.jl") include("structs/fable_input.jl") include("structs/fable_user_methods.jl") -#include("fums/shaders.jl") -#include("structs/fable_operators.jl") -#include("structs/fable_executable/fable_executable.jl") -#include("structs/fable_executable/hutchinson.jl") -#include("structs/fable_executable/shader.jl") +include("fums/shaders.jl") +include("structs/fable_operators.jl") +include("structs/fable_executable/fable_executable.jl") +include("structs/fable_executable/hutchinson.jl") +include("structs/fable_executable/shader.jl") # Operations -#include("utils/simple_rng.jl") -#include("fums/flames.jl") -#include("fums/smears.jl") +include("utils/simple_rng.jl") +include("fums/flames.jl") +include("fums/smears.jl") # Shapes -#include("objects/rectangle.jl") -#include("objects/circle.jl") -#include("objects/triangle.jl") -#include("objects/barnsley.jl") +include("objects/rectangle.jl") +include("objects/circle.jl") +include("objects/triangle.jl") +include("objects/barnsley.jl") # IO -#include("structs/layers/layers.jl") -#include("io/postprocess/postprocess.jl") -#include("structs/layers/fractal_layer.jl") -#include("structs/layers/color_layer.jl") -#include("structs/layers/image_layer.jl") -#include("structs/layers/shader_layer.jl") -#include("structs/video_params.jl") -#include("io/io_tools.jl") +include("structs/layers/layers.jl") +include("io/postprocess/postprocess.jl") +include("structs/layers/fractal_layer.jl") +include("structs/layers/color_layer.jl") +include("structs/layers/image_layer.jl") +include("structs/layers/shader_layer.jl") +include("structs/video_params.jl") +include("io/io_tools.jl") # PostProcessing -#include("io/postprocess/clip.jl") -#include("io/postprocess/filter.jl") -#include("io/postprocess/sobel.jl") -#include("io/postprocess/outline.jl") +include("io/postprocess/clip.jl") +include("io/postprocess/filter.jl") +include("io/postprocess/sobel.jl") +include("io/postprocess/outline.jl") # Main file -#include("run/run.jl") -#include("run/fractal_flame.jl") -#include("run/shader.jl") -#include("run/color.jl") +include("run/run.jl") +include("run/fractal_flame.jl") +include("run/shader.jl") +include("run/color.jl") end # module diff --git a/src/structs/fable_input.jl b/src/structs/fable_input.jl index 8bdc562..5ee9a40 100644 --- a/src/structs/fable_input.jl +++ b/src/structs/fable_input.jl @@ -1,29 +1,53 @@ -#-------------fable_input------------------------------------------------------# -# -# Purpose: This file is meant to create fable inputs for Fable -# Fable Inputs are any values meant to be modified by the user -# within a Fable executable to avoid recompilation. -# -# Notes: Maybe save as pointers to expressions for usability? -# -#------------------------------------------------------------------------------# -export FableInput, @fi - -struct FableInput{E} - expr::E +export FableInput, fi, @fi, set!, combine, to_args, find_fi_index, value + +struct FableInput + s::Union{Symbol, String} + x::Ref{Union{Number, Tuple, Vector}} end -""" - x = @fi 7 +fi(args...) = FableInput(args...) + +function set!(fi::FableInput, val) + fi.x.x = val +end - or +function combine(nt::Tuple, fis::Tuple) + return Tuple(combine(nt[i], fis[i]) for i = 1:length(fis)) +end - x = @fi 7*y+5 +combine(fis::Vector{FableInput}, nt::NamedTuple) = combine(nt, fis) -Marks the variable `x` as a FableInput -- a dynamic variable passed in to the -final executable at runtime. -This avoids recompilation of code for variables that are dynamically changing. -""" -macro fi(ex) - esc(FableInput(ex)) +function combine(nt::NamedTuple, fis::Vector{FableInput}) + if length(fis) == 0 + return nt + end + + fi_vals = (remove_vectors(fis[i].x.x) for i = 1:length(fis)) + fi_keys = (Symbol(fis[i].s) for i = 1:length(fis)) + + return NamedTuple{(keys(nt)..., fi_keys...)}((values(nt)..., fi_vals...)) end + +function to_args(nt::Tuple, fis::Tuple) + return Tuple(combine(fis[i], nt[i]) for i = 1:length(fis)) +end + +function to_args(nt::NamedTuple, fis::Vector{FableInput}) + if length(fis) == 0 + return values(nt) + end + fi_vals = (remove_vectors(fis[i].x.x) for i = 1:length(fis)) + return (values(nt)..., fi_vals...) +end + +function find_fi_index(s, fis::FT) where FT <: Union{Vector{FableInput}, + Tuple} + for i = 1:length(fis) + if Symbol(fis[i].s) == Symbol(s) + return i + end + end +end + +value(fi::FableInput) = fi.x.x +value(a) = a diff --git a/src/structs/fable_user_methods.jl b/src/structs/fable_user_methods.jl index 2580d51..0e56e15 100644 --- a/src/structs/fable_user_methods.jl +++ b/src/structs/fable_user_methods.jl @@ -1,94 +1,102 @@ -#-------------fable-user_methods-----------------------------------------------# -# -# Purpose: This file is meant to define how users interact with fable user -# methods. -# fums are essentially function fragments that will be compiled at -# a later stage of the Fable pipeline -# -# Notes: I think the best way to carry fis with the user methods is -# to define a constructor function that reads in the expr block and -# kwargs, and then parses out the fis from that list. Then at the -# end of the @fum macro, I can call into that constructor. -# We could get fanc with our own @generated macro, but... -# -#------------------------------------------------------------------------------# export FableUserMethod, @fum -struct FableUserMethod{E, F} - body::E - fis::F +struct FableUserMethod{NT <: NamedTuple, + V <: Vector{FableInput}, + F <: Function} + kwargs::NT + fis::V + fx::F end - args(a,n) = a.args[n] -function __warn_args(args, config) - correct_args = (:y, :x, :frame, :color) - max_arg_idx = 3 - if config == :color - max_arg_idx = 4 +function __set_args(args, config) + if config == :fractal + correct_args = [:y, :x, :frame] + elseif config == :shader + correct_args = [:y, :x, :color, :frame] end - if !issubset(args, correct_args[1:max_arg_idx]) + if !issubset(args, correct_args) error("Function arguments must be one of the following:\n"* - string(correct_args[1:max_arg_idx])*"\n"* + string(correct_args)*"\n"* "Please use key-word arguments for any additional arguments!") end + return correct_args end -""" - f = @fum f(x) = x+1 - -Defines a Fable User Method (f) that can be used along with other fums to -build a Fable Executable. -Note that these are not compiled until the Fable Executable is built! -This means you will not have error checking until later in the process! - -You may use `:color` to specify that the fum is meant for coloring. -""" -macro fum(exprs...) - - config = :default +# this function can create a NamedTuple from kwargs in a macro, ie: +# kwargs = __to_NamedTuple(def[:kwargs]) +# It is not currently used, but took a while to find out, so I'm leaving it +# here for debugging purposes +#function __to_NamedTuple(kwargs) +# NamedTuple{Tuple(args.(kwargs[:],1))}(Tuple(args.(kwargs[:],2))) +#end + +function __create_fum_stuff(expr, config, mod, force_inbounds) + def = MacroTools.splitdef(expr) + def[:name] = name = Symbol(def[:name], :_fum) + used_args = def[:args] + args = __set_args(used_args, config) + def[:args] = args + if force_inbounds + body_qt = quote + @inbounds $(def[:body]) + end + def[:body] = body_qt + end + kwargs = NamedTuple() + fum_fx = combinedef(def) + return kwargs, Core.eval(mod, fum_fx) +end - if length(exprs) != 1 - for i = 1:length(exprs)-1 - if exprs[i] == :color || exprs[i] == :shader || - exprs[i] == :(:shader) || exprs[i] == :(:color) - config = :color +# Note: this operator currently works like this: +# f = @fum function config f(x) x+1 end +# There should be a way to define f in this macro, so we don't need to +# say `f = @fum function ...`, but instead just `@fum function ...` +macro fum(ex...) + + config = :fractal + force_inbounds = false + + if length(ex) == 1 + else + for i = 1:length(ex)-1 + if ex[i] == :color || ex[i] == :shader || + ex[i] == :(:shader) || ex[i] == :(:color) + config = :shader + elseif ex[i] isa Expr && ex[i].head == :(=) && + ex[i].args[1] == :inbounds && ex[i].args[2] isa Bool + force_inbounds = ex[i].args[2] else error("Incorrect config argument ", ex[i], "!") end end end - ex = exprs[end] - - if !isa(ex, Expr) - error("Cannot convert single inputs to Fable User Method!") - elseif ex.head == :(=) && !(isa(ex.args[1], Expr)) - error("Invalid function definition!") - end + expr = ex[end] + kwargs = nothing - def = MacroTools.splitdef(ex) - __warn_args(def[:args], config) - kwargs = def[:kwargs] - fis = Tuple(FableInput[]) - for i = 1:length(kwargs) - if !isa(kwargs[i], Expr) - error("fum key word arguments must be an expression, like "* - "`q = 5`") - end - kwargs[i].head = :(=) - if isa(kwargs[i].args[2], FableInput) - fis = (fis..., kwargs[i].args[2]) + if isa(expr, Symbol) + error("Cannot convert Symbol to Fable User Method!") + elseif expr.head == :(=) + # inline function definitions + if isa(expr.args[1], Expr) + kwargs, fum_fx = __create_fum_stuff(expr, config, __module__, + force_inbounds) + else + error("Cannot create FableUserMethod.\n"* + "Input is not a valid function definition!") end + elseif expr.head == :function + kwargs, fum_fx = __create_fum_stuff(expr, config, __module__, + force_inbounds) + else + error("Cannot convert expr to Fable User Method!") end - final_ex = Expr(:block, kwargs..., def[:body]) - return esc(FableUserMethod(Expr(:block, def[:kwargs]..., def[:body]), - fis)) + return FableUserMethod(kwargs, FableInput[], fum_fx) end -#= function (a::FableUserMethod)(args...; kwargs...) @@ -143,4 +151,3 @@ function (a::FableUserMethod)(args...; kwargs...) final_kwargs = NamedTuple{ks}(vals) return FableUserMethod(final_kwargs, fis, a.fx) end -=# diff --git a/src/structs/generators.jl b/src/structs/generators.jl index 47aaabb..91ee3b6 100644 --- a/src/structs/generators.jl +++ b/src/structs/generators.jl @@ -9,14 +9,14 @@ export AbstractGenerator, ChaosGenerator, StandardGenerator abstract type AbstractGenerator end; -struct ChaosGenerator{A, I, P, F} <: AbstractGenerator +struct ChaosGenerator{A, I, P, F} where {I <: Integer, P <: Tuple, F <: Tuple} args::A iterations::I prob_set::P fnums::F end -struct StandardGenerator{A, ND} <: AbstractGenerator +struct StandardGenerator{A, ND} args::A ndrange::ND end From 565f3a91462abecc25c05a708ef11c1de5a4b9d6 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Wed, 17 Jul 2024 12:57:55 +0200 Subject: [PATCH 06/14] small changes --- src/Fable.jl | 56 +++++++++++++++++++-------------------- src/structs/generators.jl | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/Fable.jl b/src/Fable.jl index 27c0c13..c1d6696 100644 --- a/src/Fable.jl +++ b/src/Fable.jl @@ -34,42 +34,42 @@ include("structs/generators.jl") include("structs/fable_input.jl") include("structs/fable_user_methods.jl") include("fums/shaders.jl") -include("structs/fable_operators.jl") -include("structs/fable_executable/fable_executable.jl") -include("structs/fable_executable/hutchinson.jl") -include("structs/fable_executable/shader.jl") +#include("structs/fable_operators.jl") +#include("structs/fable_executable/fable_executable.jl") +#include("structs/fable_executable/hutchinson.jl") +#include("structs/fable_executable/shader.jl") # Operations -include("utils/simple_rng.jl") -include("fums/flames.jl") -include("fums/smears.jl") +#include("utils/simple_rng.jl") +#include("fums/flames.jl") +#include("fums/smears.jl") # Shapes -include("objects/rectangle.jl") -include("objects/circle.jl") -include("objects/triangle.jl") -include("objects/barnsley.jl") +#include("objects/rectangle.jl") +#include("objects/circle.jl") +#include("objects/triangle.jl") +#include("objects/barnsley.jl") -# IO -include("structs/layers/layers.jl") -include("io/postprocess/postprocess.jl") -include("structs/layers/fractal_layer.jl") -include("structs/layers/color_layer.jl") -include("structs/layers/image_layer.jl") -include("structs/layers/shader_layer.jl") -include("structs/video_params.jl") -include("io/io_tools.jl") +## IO +#include("structs/layers/layers.jl") +#include("io/postprocess/postprocess.jl") +#include("structs/layers/fractal_layer.jl") +#include("structs/layers/color_layer.jl") +#include("structs/layers/image_layer.jl") +#include("structs/layers/shader_layer.jl") +#include("structs/video_params.jl") +#include("io/io_tools.jl") # PostProcessing -include("io/postprocess/clip.jl") -include("io/postprocess/filter.jl") -include("io/postprocess/sobel.jl") -include("io/postprocess/outline.jl") +#include("io/postprocess/clip.jl") +#include("io/postprocess/filter.jl") +#include("io/postprocess/sobel.jl") +#include("io/postprocess/outline.jl") # Main file -include("run/run.jl") -include("run/fractal_flame.jl") -include("run/shader.jl") -include("run/color.jl") +#include("run/run.jl") +#include("run/fractal_flame.jl") +#include("run/shader.jl") +#include("run/color.jl") end # module diff --git a/src/structs/generators.jl b/src/structs/generators.jl index 91ee3b6..5c9299e 100644 --- a/src/structs/generators.jl +++ b/src/structs/generators.jl @@ -9,7 +9,7 @@ export AbstractGenerator, ChaosGenerator, StandardGenerator abstract type AbstractGenerator end; -struct ChaosGenerator{A, I, P, F} where {I <: Integer, P <: Tuple, F <: Tuple} +struct ChaosGenerator{A, I <: Integer, P <: Tuple, F <: Tuple} args::A iterations::I prob_set::P From fbff3e1cd11973a45bf7026a7046a6dec48aa206 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Fri, 19 Jul 2024 12:58:09 +0200 Subject: [PATCH 07/14] updating FableInputs and FableBuffers --- src/Fable.jl | 9 ++-- src/structs/fable_buffer.jl | 59 ++++++++++++++++++++++++ src/structs/fable_input.jl | 92 ++++++++++++++++++++----------------- 3 files changed, 113 insertions(+), 47 deletions(-) create mode 100644 src/structs/fable_buffer.jl diff --git a/src/Fable.jl b/src/Fable.jl index c1d6696..49f8ecf 100644 --- a/src/Fable.jl +++ b/src/Fable.jl @@ -30,11 +30,12 @@ include("utils/geometries.jl") include("utils/histogram.jl") # Fable flame structures -include("structs/generators.jl") +include("structs/fable_buffer.jl") include("structs/fable_input.jl") -include("structs/fable_user_methods.jl") -include("fums/shaders.jl") -#include("structs/fable_operators.jl") +#include("structs/fable_user_methods.jl") +#include("fums/shaders.jl") +#include("structs/fable_operators/generators.jl") +#include("structs/fable_operators/fable_operators.jl") #include("structs/fable_executable/fable_executable.jl") #include("structs/fable_executable/hutchinson.jl") #include("structs/fable_executable/shader.jl") diff --git a/src/structs/fable_buffer.jl b/src/structs/fable_buffer.jl new file mode 100644 index 0000000..325dc5e --- /dev/null +++ b/src/structs/fable_buffer.jl @@ -0,0 +1,59 @@ +#-------------fable_buffer.jl--------------------------------------------------# +# +# Purpose: FableBuffers are Array / GPUArray pairs that are mewant for passing +# variables from the CPU to GPU. +# +# Notes: Improvements: +# 1. Make the buffer always UInt and then carry the types around +# so you can recast the fis in the kernel to the appropriate type. +# Right now, we are just defaulting to floats +# 2. Think about textures! +# +#------------------------------------------------------------------------------# + +export FableInput, fi, set!, value +export FableBuffer, create_falbe_buffer, to_cpu!, to_gpu! + +""" +A FableBuffer is an Array / GPUArray pair for storing FableInputs. +FableInputs are variables that are meant to be modified on-the-fly by the user +""" +struct FableBuffer{AT <: AbstractArray, GPUAT <: AbstractArray} + cpu::AT + gpu::GPUAT +end + +""" + create_falbe_buffer(zeros(10); ArrayType = Array) + +Will create a FableBuffer of zeros of size 10 for storing FableInputs. +This will create a buffer on the CPU and an additional buffer stored as an +ArrayType (meant for passing variables to the GPU) +""" +function create_falbe_buffer(A::AT; ArrayType = Array) where AT <: AbstractArray + return FableBuffer(A, ArrayType(A)) +end + +""" + create_fable_buffer(10; ArrayType = Array, ElementType = Float64) + +Will create a FableBuffer of zeros (Float64) of size 10 for storing FableInputs. +""" +function create_falbe_buffer(i::N; ArrayType = Array, + ElementType = Float64) where N <: Number + create_falbe_buffer(zeros(ElementType, 10); ArrayType) +end + +""" +This function copies the GPU FableBuffer back to the CPU +""" +function to_cpu!(fb::FableBuffer) + fb.cpu .= fb.gpu +end + +""" +This function copies the CPU FableBuffer to the GPU. This is particularly useful because all FableInputs modify the CPU array by default. +""" +function to_gpu!(fb::FableBuffer) + fb.gpu .= fb.cpu +end diff --git a/src/structs/fable_input.jl b/src/structs/fable_input.jl index 5ee9a40..fe14cbb 100644 --- a/src/structs/fable_input.jl +++ b/src/structs/fable_input.jl @@ -1,53 +1,59 @@ -export FableInput, fi, @fi, set!, combine, to_args, find_fi_index, value - -struct FableInput - s::Union{Symbol, String} - x::Ref{Union{Number, Tuple, Vector}} +#-------------fable_input.jl---------------------------------------------------# +# +# Purpose: Recompilation is a costly process in Julia (and for GPU programming +# in general). FableInputs are meant to allow users to store specific +# variables that are meant to be modified dynamically without +# triggering recompilation. +# +# Notes: Improvements: +# 1. Make the buffer object less noticeable when creating fis (for +# example, it might be nice not to specify index) +# +#------------------------------------------------------------------------------# + +export FableInput, fi, set!, value + +""" + x = FableInput(buffer, 5) + +Will create a FableInput at index 5 of the FableBuffer. +FableInputs (fis) are variables meant to be dynamically modified by the user. +This way we do not unnecessarily triger a costly recompilation +""" +struct FableInput{FB <: FableBuffer, I <: Integer} + buffer::FB + index::I end -fi(args...) = FableInput(args...) - -function set!(fi::FableInput, val) - fi.x.x = val -end - -function combine(nt::Tuple, fis::Tuple) - return Tuple(combine(nt[i], fis[i]) for i = 1:length(fis)) -end +""" + x = fi(buffer, 5) -combine(fis::Vector{FableInput}, nt::NamedTuple) = combine(nt, fis) - -function combine(nt::NamedTuple, fis::Vector{FableInput}) - if length(fis) == 0 - return nt - end - - fi_vals = (remove_vectors(fis[i].x.x) for i = 1:length(fis)) - fi_keys = (Symbol(fis[i].s) for i = 1:length(fis)) +This is a shorthand for FableInput construction +""" +fi(args...) = FableInput(args...) - return NamedTuple{(keys(nt)..., fi_keys...)}((values(nt)..., fi_vals...)) -end +""" + x = fi(buffer, index, value) -function to_args(nt::Tuple, fis::Tuple) - return Tuple(combine(fis[i], nt[i]) for i = 1:length(fis)) +Will create a FableInput that points to `index` in `buffer` + and then sets it to `value` +""" +function fi(fb::FableBuffer, idx::Integer, val::N) where N <: Number + x = FableInput(buffer, idx) + set!(x, val) + return x end -function to_args(nt::NamedTuple, fis::Vector{FableInput}) - if length(fis) == 0 - return values(nt) - end - fi_vals = (remove_vectors(fis[i].x.x) for i = 1:length(fis)) - return (values(nt)..., fi_vals...) -end +""" + set!(fi, 5) -function find_fi_index(s, fis::FT) where FT <: Union{Vector{FableInput}, - Tuple} - for i = 1:length(fis) - if Symbol(fis[i].s) == Symbol(s) - return i - end - end +Will set the associated CPU buffer element for your FableInput to 5 +""" +function set!(fi::FableInput, val) + fi.buffer.cpu[fi.index] = val end -value(fi::FableInput) = fi.x.x -value(a) = a +""" +This just returns the stored value of your FablInput +""" +value(fi::FableInput) = fi.buffer.cpu[fi.index] From d3b4683e3e32aaa556bf74b1989de49f0b21b34b Mon Sep 17 00:00:00 2001 From: James Schloss Date: Wed, 24 Jul 2024 16:42:03 +0200 Subject: [PATCH 08/14] updating fums, still need to reason about configuration --- src/Fable.jl | 2 +- .../chaos_operators.jl} | 0 .../fable_operators/fable_operators.jl | 25 +++ .../{ => fable_operators}/generators.jl | 18 +- src/structs/fable_user_methods.jl | 173 ++++++++++++------ 5 files changed, 160 insertions(+), 58 deletions(-) rename src/structs/{fable_operators.jl => fable_operators/chaos_operators.jl} (100%) create mode 100644 src/structs/fable_operators/fable_operators.jl rename src/structs/{ => fable_operators}/generators.jl (54%) diff --git a/src/Fable.jl b/src/Fable.jl index 49f8ecf..0070424 100644 --- a/src/Fable.jl +++ b/src/Fable.jl @@ -32,7 +32,7 @@ include("utils/histogram.jl") # Fable flame structures include("structs/fable_buffer.jl") include("structs/fable_input.jl") -#include("structs/fable_user_methods.jl") +include("structs/fable_user_methods.jl") #include("fums/shaders.jl") #include("structs/fable_operators/generators.jl") #include("structs/fable_operators/fable_operators.jl") diff --git a/src/structs/fable_operators.jl b/src/structs/fable_operators/chaos_operators.jl similarity index 100% rename from src/structs/fable_operators.jl rename to src/structs/fable_operators/chaos_operators.jl diff --git a/src/structs/fable_operators/fable_operators.jl b/src/structs/fable_operators/fable_operators.jl new file mode 100644 index 0000000..c56ab2e --- /dev/null +++ b/src/structs/fable_operators/fable_operators.jl @@ -0,0 +1,25 @@ +export FableOperator, fo + +struct FableOperator{G, F, KW} where {G <: AbstractGenerator, + F <: Function, KW <: Tuple, + gen::G + fx::F + kwargs::KW +end + +""" + @fo generator fxs = (fum_1, fum_2, fum_3) colors = (clr_1, clr_2, clr_3) + +Will create a Fable Operator with a function that combines all the Fable User +Methods and associated colors. +""" +macro fo(gen, args...) + if gen == :RandomGenerator + expr = generate_random_fo(args...) + else + error("Unknown generator: ", gen, "!") + end +end + +@generated function fo(generator::G, fums, colors) where G <: AbstractGenerator +end diff --git a/src/structs/generators.jl b/src/structs/fable_operators/generators.jl similarity index 54% rename from src/structs/generators.jl rename to src/structs/fable_operators/generators.jl index 5c9299e..cb90472 100644 --- a/src/structs/generators.jl +++ b/src/structs/fable_operators/generators.jl @@ -2,21 +2,33 @@ # # Purpose: Generators are types used to determine how the final `run` function # is `@generated` at the end. To see how this works, look at the -# files in the `run/` directory +# files in the `run/` directory as well as in `fable_operators.jl` # #------------------------------------------------------------------------------# export AbstractGenerator, ChaosGenerator, StandardGenerator abstract type AbstractGenerator end; -struct ChaosGenerator{A, I <: Integer, P <: Tuple, F <: Tuple} +struct ChaosGenerator{A, I <: Integer, + P <: Tuple, F <: Tuple} <: AbstractGenerator args::A iterations::I prob_set::P fnums::F end -struct StandardGenerator{A, ND} +struct StandardGenerator{A, ND} <: AbstractGenerator + args::A + ndrange::ND +end + +# For post transformations +struct SequentialGenerator{A, ND} <: AbstractGenerator + args::A + ndrange::ND +end + +struct RandomGenerator{A, ND} <: AbstractGenerator args::A ndrange::ND end diff --git a/src/structs/fable_user_methods.jl b/src/structs/fable_user_methods.jl index 0e56e15..2656115 100644 --- a/src/structs/fable_user_methods.jl +++ b/src/structs/fable_user_methods.jl @@ -1,61 +1,123 @@ +#-------------fable_user_methods-----------------------------------------------# +# +# Purpose: FableUserMethods are user-defined functions that later get compiled +# into compute shaders / kernels for Fable +# +# Notes: FableUserMethods do not define functions precisely, but instead +# pass along the necessary function bodies to be `eval`ed later at +# the FableOperator and FableExecutable level. +# +#------------------------------------------------------------------------------# export FableUserMethod, @fum -struct FableUserMethod{NT <: NamedTuple, - V <: Vector{FableInput}, - F <: Function} - kwargs::NT - fis::V - fx::F +abstract type AbstractFUMKind end; + +struct FUMColor <: AbstractFUMKind end +struct FUMTransform <: AbstractFUMKind end + + +""" +A FableUserFragment is a piece of code created with the `@fum` macro that is +later configured into a FableUserMethod by calling it as a function +""" +struct FableUserFragment{A <: AbstractFUMKind} + body::Expr + kwargs::Vector + kind::A end -args(a,n) = a.args[n] +""" +This is a struct for holding all expressions related to the construction of a +FableUserMethod. It is not meant to be diretly created by the user, but instead +it is created by calling a FableUserFragment as a function to configure its +keyword arguments +""" +struct FableUserMethod + name::Expr + body::Expr +end -function __set_args(args, config) - if config == :fractal - correct_args = [:y, :x, :frame] - elseif config == :shader - correct_args = [:y, :x, :color, :frame] +#------------------------------------------------------------------------------# +# Macro @fum +#------------------------------------------------------------------------------# + +function __find_kind(s) + if s == :color + return FUMColor() + elseif s == :transform + return FUMTransform() end - if !issubset(args, correct_args) - error("Function arguments must be one of the following:\n"* +end + +function __check_args(args, kwargs, config) + correct_args = (:y, :x, :z, :frame, :color) + + # checking the kwargs + for kwarg in kwargs + if in(kwarg.args[1], correct_args) + error("Fable User Method key word arguments cannot be:\n"* string(correct_args)*"\n"* + "These are defined for all fums. Please redefine "* + string(kwarg.args[1])*"!") + end + end + + # checking the args + final_idx = length(correct_args) - 1 + if config == :color + final_idx = final_idx + 1 + end + if !issubset(args, correct_args[1:final_idx]) + error("Fable User Method arguments must be one of the following:\n"* + string(correct_args[1:final_idx])*"\n"* "Please use key-word arguments for any additional arguments!") end - return correct_args end -# this function can create a NamedTuple from kwargs in a macro, ie: -# kwargs = __to_NamedTuple(def[:kwargs]) -# It is not currently used, but took a while to find out, so I'm leaving it -# here for debugging purposes -#function __to_NamedTuple(kwargs) -# NamedTuple{Tuple(args.(kwargs[:],1))}(Tuple(args.(kwargs[:],2))) -#end - -function __create_fum_stuff(expr, config, mod, force_inbounds) +function __create_fum_stuff(expr, config, force_inbounds) def = MacroTools.splitdef(expr) - def[:name] = name = Symbol(def[:name], :_fum) - used_args = def[:args] - args = __set_args(used_args, config) - def[:args] = args + + kwargs = def[:kwargs] + __check_args(def[:args], kwargs, config) + + body_qt = def[:body] if force_inbounds body_qt = quote @inbounds $(def[:body]) end - def[:body] = body_qt end - kwargs = NamedTuple() - fum_fx = combinedef(def) - return kwargs, Core.eval(mod, fum_fx) + + return (body_qt, def[:kwargs]) end -# Note: this operator currently works like this: -# f = @fum function config f(x) x+1 end -# There should be a way to define f in this macro, so we don't need to -# say `f = @fum function ...`, but instead just `@fum function ...` +""" + f = @fum inbounds = true color f(x; q = 7) = x*q + +Will create a FableUserFragment `f` with the function body `x*q`, the +key word argument `q = 7`, default settings for coloring, and `@inbounds` +propagated throughout the fum. + +Note that you may also use a full function definition like so: + + f = @fum inbounds = true color function f(x; q = 7) + x*q + end + +Also note that you can configure the function as a `shader`, which will also +set default settings for coloring. +If you do not configure the fum or otherwise use the `transform` configuration, +it will default to the settings for transformations of points. + +You can then configure the FableUserFragment into a FableUserMethod with +specific key words by calling it as a normal function: + + configured_f = f(q = 6) + +Which will return a FableUserMethod `f` with the correct configuration. +""" macro fum(ex...) - config = :fractal + config = :transform force_inbounds = false if length(ex) == 1 @@ -63,7 +125,7 @@ macro fum(ex...) for i = 1:length(ex)-1 if ex[i] == :color || ex[i] == :shader || ex[i] == :(:shader) || ex[i] == :(:color) - config = :shader + config = :color elseif ex[i] isa Expr && ex[i].head == :(=) && ex[i].args[1] == :inbounds && ex[i].args[2] isa Bool force_inbounds = ex[i].args[2] @@ -77,28 +139,31 @@ macro fum(ex...) kwargs = nothing if isa(expr, Symbol) - error("Cannot convert Symbol to Fable User Method!") - elseif expr.head == :(=) - # inline function definitions - if isa(expr.args[1], Expr) - kwargs, fum_fx = __create_fum_stuff(expr, config, __module__, - force_inbounds) - else - error("Cannot create FableUserMethod.\n"* - "Input is not a valid function definition!") - end - elseif expr.head == :function - kwargs, fum_fx = __create_fum_stuff(expr, config, __module__, - force_inbounds) + error("Fable User Methods must be Functions") + elseif expr.head == :(=) && !isa(expr.args[1], Expr) + error("Cannot create FableUserMethod.\n"* + "Input is not a valid function definition!") else - error("Cannot convert expr to Fable User Method!") + return FableUserFragment(__create_fum_stuff(expr, + config, + force_inbounds)..., + __find_kind(config)) end - return FableUserMethod(kwargs, FableInput[], fum_fx) end +#------------------------------------------------------------------------------# +# Configuration +#------------------------------------------------------------------------------# + +""" + @fum f(; q = 5) = q + f(q=2) -function (a::FableUserMethod)(args...; kwargs...) +Will create a FableUserMethod `f` and thenc onfigure it's keyword argument +`q = 5` -> `q = 2`. +""" +function (a::FableUserFragment)(args...; kwargs...) # we do not reason about standard args right now, although this could # be considered in the future if we ensure users always configure their From f19c17cc04c9cfff29a06ca575ab2368c19db46f Mon Sep 17 00:00:00 2001 From: James Schloss Date: Wed, 24 Jul 2024 23:24:58 +0200 Subject: [PATCH 09/14] adding fum configuration --- src/structs/fable_user_methods.jl | 70 ++++++++++++++++++------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/structs/fable_user_methods.jl b/src/structs/fable_user_methods.jl index 2656115..d0b5cf6 100644 --- a/src/structs/fable_user_methods.jl +++ b/src/structs/fable_user_methods.jl @@ -33,8 +33,8 @@ it is created by calling a FableUserFragment as a function to configure its keyword arguments """ struct FableUserMethod - name::Expr body::Expr + fi_num::Int end #------------------------------------------------------------------------------# @@ -49,17 +49,29 @@ function __find_kind(s) end end +function __find_kwarg_keys(kwargs) + ks = [:x for i = 1:length(kwargs)] + for i = 1:length(ks) + ks[i] = kwargs[i].args[1] + end + return ks +end + function __check_args(args, kwargs, config) - correct_args = (:y, :x, :z, :frame, :color) + correct_args = (:y, :x, :z, :frame, :fi_buffer, :color) + input_kwargs = __find_kwarg_keys(kwargs) # checking the kwargs - for kwarg in kwargs - if in(kwarg.args[1], correct_args) + for i = 1:length(kwargs) + if in(input_kwargs[i], correct_args) error("Fable User Method key word arguments cannot be:\n"* string(correct_args)*"\n"* "These are defined for all fums. Please redefine "* string(kwarg.args[1])*"!") end + if i > 1 && in(input_kwargs[i], input_kwargs[1:i-1]) + error(string(input_kwargs[i])*" key word already defined!") + end end # checking the args @@ -156,6 +168,13 @@ end # Configuration #------------------------------------------------------------------------------# +function __extract_keys(v) + final_v = [:x for i = 1:length(v)] + for i = 1:length(v) + final_v[i] = v[i].args[1] + end + return final_v +end """ @fum f(; q = 5) = q f(q=2) @@ -178,28 +197,26 @@ function (a::FableUserFragment)(args...; kwargs...) # checking to make sure the symbols are assignable in the first place error_flag = false - # Note: grabs all keywords from the a.fx. If multiple definitions exist, - # it takes the first one. It might mistakenly grab the wrong function. - # If this happens, we need to reason about how to pick the right function - # when two exist with the same name, but different kwargs... - known_kwargs = Base.kwarg_decl.(methods(a.fx))[1] - - final_kwarg_idxs = Int[] - final_fi_idxs = Int[] + final_kwargs = copy(a.kwargs) ks = keys(kwargs) vals = values(NamedTuple(kwargs)) - for i = 1:length(ks) - if !in(ks[i], known_kwargs) - @warn(string(key)*" not found in set of fum key-word args!") - error_flag = true - end - - if isa(vals[i], FableInput) - push!(final_fi_idxs, i) - else - push!(final_kwarg_idxs, i) + known_kwargs = __extract_keys(final_kwargs) + fi_num = 0 + + for i = 1:length(final_kwargs) + for j = 1:length(ks) + if known_kwargs[i] == ks[j] + s = known_kwargs[i] + if isa(vals[j], FableInput) + final_kwargs[i] = :($s = fi_buffer[$(vals[j].index)]) + fi_num += 1 + else + final_kwargs[i] = :($s = $(vals[j])) + end + end end + final_kwargs[i].head = :(=) end if error_flag @@ -208,11 +225,6 @@ function (a::FableUserFragment)(args...; kwargs...) "key-word arguments?") end - fis = [FableInput(ks[i], vals[i].x) for i in final_fi_idxs] - - ks = Tuple(ks[final_kwarg_idxs]) - vals = Tuple(remove_vectors.(vals[final_kwarg_idxs])) - - final_kwargs = NamedTuple{ks}(vals) - return FableUserMethod(final_kwargs, fis, a.fx) + # combining it all together + return FableUserMethod(Expr(:block, final_kwargs..., a.body), fi_num) end From 7e6288949ec427a18249cc156f26686496f2a461 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Thu, 25 Jul 2024 14:47:41 +0200 Subject: [PATCH 10/14] revamping histogram output --- src/Fable.jl | 6 +-- src/io/splat.jl | 111 +++++++++++++++++++++++++++++++++++++++++ src/utils/histogram.jl | 110 ---------------------------------------- 3 files changed, 113 insertions(+), 114 deletions(-) create mode 100644 src/io/splat.jl delete mode 100644 src/utils/histogram.jl diff --git a/src/Fable.jl b/src/Fable.jl index 0070424..1391a4e 100644 --- a/src/Fable.jl +++ b/src/Fable.jl @@ -26,14 +26,12 @@ include("utils/extras.jl") # Geometries include("utils/geometries.jl") -# KA kernels -include("utils/histogram.jl") - # Fable flame structures include("structs/fable_buffer.jl") include("structs/fable_input.jl") include("structs/fable_user_methods.jl") -#include("fums/shaders.jl") +include("fums/shaders.jl") +include("io/splat.jl") #include("structs/fable_operators/generators.jl") #include("structs/fable_operators/fable_operators.jl") #include("structs/fable_executable/fable_executable.jl") diff --git a/src/io/splat.jl b/src/io/splat.jl new file mode 100644 index 0000000..2eb7dc7 --- /dev/null +++ b/src/io/splat.jl @@ -0,0 +1,111 @@ +#-------------splat.jl---------------------------------------------------------# +# +# Purpose: This implements the `@splat` output macro for use within fees, fos, +# and fums. +# +# +# Notes: I need to create a FableColor type that has RGBA, values, and +# priority. I als need to create Base.:(+) for it. +# I can create new on_image fucntions if the pt is in 3D +# +#------------------------------------------------------------------------------# + +export splat + +""" + @splat method = simple atomic = true buffer = output_buffer + +Will splat (output) a given point to screen. If no configuration is given, it +will default to a simple atomic operation where each point corresponds to a +single pixel in the output buffer +""" +macro splat(ex...) + # default configurations + buffer = :output_buffer + atomic = true + method = __simple_splat + + # setting user configuration + if length(ex) > 0 + for i = 1:length(ex) + if ex[i].args[1] == :method + println(ex[i].args[2]) + if ex[i].args[2] != :simple + error("Splat method ", ex[i].args[2], " not found!") + end + elseif ex[i].args[1] == :atomic + if ex[i].args[2] == true || ex[i].args[2] == false + atomic = ex[i].args[2] + else + error("atomic splat config argument must be boolean!") + end + elseif ex[i].args[1] == :buffer + if isa(ex[i].args[2], Symbol) + buffer = ex[i].args[2] + else + error("buffer splat config must point to a buffer!") + end + else + error("Incorrect splat config argument ", ex[i], "!") + end + end + end + + return method(atomic, buffer) + +end + +#------------------------------------------------------------------------------# +# Splat methods +#------------------------------------------------------------------------------# + +function __simple_splat(atomic, buffer) + if atomic + return quote + if on_image(pt, bounds) + bin = find_bin(pt, $buffer, bounds, bin_widths) + @inbounds @atomic $buffer[bin] += color + end + end + else + return quote + if on_image(pt, bounds) + bin = find_bin(pt, $buffer, bounds, bin_widths) + @inbounds $buffer[bin] += color + end + end + end +end + +#------------------------------------------------------------------------------# +# Aux methods +#------------------------------------------------------------------------------# + +unsafe_ceil(T, x) = Base.unsafe_trunc(T, round(x, RoundUp)) + +@inline function find_bin(input::Point2D, histogram_output, bounds, bin_widths) + + @inbounds begin + bin_y = unsafe_ceil(Int, (input.y - bounds[1]) / bin_widths[1]) + bin_x = unsafe_ceil(Int, (input.x - bounds[3]) / bin_widths[2]) + end + + return CartesianIndex(bin_y, bin_x) +end + + +# couldn't figure out how to get an n-dim version working for GPU +@inline function on_image(p::Point2D, bounds) + flag = true + if p.y <= bounds.ymin || p.y > bounds.ymax || + p.y == NaN || p.y == Inf + flag = false + end + + if p.x <= bounds.xmin || p.x > bounds.xmax || + p.x == NaN || p.x == Inf + flag = false + end + return flag +end + diff --git a/src/utils/histogram.jl b/src/utils/histogram.jl deleted file mode 100644 index 05e855c..0000000 --- a/src/utils/histogram.jl +++ /dev/null @@ -1,110 +0,0 @@ -unsafe_ceil(T, x) = Base.unsafe_trunc(T, round(x, RoundUp)) - -@inline function find_bin(histogram_output, input_y, input_x, - bounds, bin_widths) - - @inbounds begin - bin = unsafe_ceil(Int, (input_y - bounds[1]) / bin_widths[1]) - - slab = size(histogram_output)[1] - bin += unsafe_ceil(Int, ((input_x - bounds[3]) / bin_widths[2])-1)*slab - end - - return bin - -end - -@inline function find_bin(histogram_output, input, - tid, dims, bounds, bin_widths) - @inbounds begin - b_index = 1 - bin = unsafe_ceil(Int, (input[tid, 1] - bounds[1]) / bin_widths[1]) - slab = 1 - - for i = 2:dims - slab *= size(histogram_output)[i-1] - b_index += 2 - bin += unsafe_ceil(Int,(((input[tid, i]) - bounds[b_index]) / - bin_widths[i])-1)*slab - end - end - - return bin - -end - -# This is a naive histogramming method that is preferred for CPU calculations -@kernel function naive_histogram_kernel!(histogram_output, input, - bounds, bin_widths, dims) - tid = @index(Global, Linear) - - # I want to turn this in to an @uniform, but that fails on CPU (#274) - bin = find_bin(histogram_output, input, tid, dims, bounds, bin_widths) - @inbounds @atomic histogram_output[bin] += 1 - -end - -# This a 1D histogram kernel where the histogramming happens on shmem -@kernel function histogram_kernel!(histogram_output, input, - bounds, bin_widths, dims) - tid = @index(Global, Linear) - lid = @index(Local, Linear) - - @uniform warpsize = Int(32) - - @inbounds @uniform gs = @groupsize()[1] - @uniform N = length(histogram_output) - - shared_histogram = @localmem Int (gs) - - # This will go through all input elements and assign them to a location in - # shmem. Note that if there is not enough shem, we create different shmem - # blocks to write to. For example, if shmem is of size 256, but it's - # possible to get a value of 312, then we will have 2 separate shmem blocks, - # one from 1->256, and another from 256->512 - @uniform max_element = 1 - @uniform min_element = 1 - while max_element <= N - - # Setting shared_histogram to 0 - @inbounds shared_histogram[lid] = 0 - @synchronize() - - # I want to turn this in to an @uniform, but that fails on CPU (#274) - bin = find_bin(histogram_output, input, tid, dims, bounds, bin_widths) - - max_element = min_element + gs - if max_element > N + 1 - max_element = N+1 - end - - # Defining bin on shared memory and writing to it if possible - if bin >= min_element && bin < max_element - bin -= min_element-1 - @inbounds @atomic shared_histogram[bin] += 1 - max_element = N - end - - @synchronize() - - if ((lid+min_element-1) <= N) - @inbounds @atomic histogram_output[lid+min_element-1] += shared_histogram[lid] - end - - min_element += gs - - end - -end - - -function histogram!(histogram_output, input; dims = ndims(histogram_output), - bounds = zeros(dims,2), - bin_widths = [1 for i = 1:dims], - numcores = 4, numthreads = 256, ArrayType = Array) - - backend = get_backend(histogram_output) - kernel! = naive_histogram_kernel!(backend, numthreads) - kernel!(histogram_output, input, ArrayType(bounds), ArrayType(bin_widths), - dims, ndrange=size(input)[1]) -end From 02ae1c2de9316d72abe9c524884c91b0e646e297 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Thu, 25 Jul 2024 21:15:11 +0200 Subject: [PATCH 11/14] adding FableColors --- src/Fable.jl | 1 + src/structs/colors.jl | 88 +++++++++++++++++++++++ src/structs/fable_operators/generators.jl | 15 ++-- src/structs/fable_user_methods.jl | 8 +-- test/extra_tests.jl | 14 ++++ test/runtests.jl | 3 + 6 files changed, 118 insertions(+), 11 deletions(-) create mode 100644 src/structs/colors.jl create mode 100644 test/extra_tests.jl diff --git a/src/Fable.jl b/src/Fable.jl index 1391a4e..5b71ac1 100644 --- a/src/Fable.jl +++ b/src/Fable.jl @@ -19,6 +19,7 @@ set_output(tf) = (global OUTPUT = tf) # Interfaces include("structs/time.jl") include("structs/point.jl") +include("structs/colors.jl") # Utilities include("utils/extras.jl") diff --git a/src/structs/colors.jl b/src/structs/colors.jl new file mode 100644 index 0000000..a28f389 --- /dev/null +++ b/src/structs/colors.jl @@ -0,0 +1,88 @@ +#-------------colors.jl--------------------------------------------------------# +# +# Purpose: This file defines the AbstractFableColor type and corresponding +# structs. The purpose is to allow for better construction of the +# output pixel layer for various purposes. +# +# Notes: Each FableColor must define an addition operator +# These types will be used to determine how to postprocess each layer +# We might need to specify that C <: RGBA but not N0f8 for GPU +# +#------------------------------------------------------------------------------# + +export StandardColor, PriorityColor, DepthColor +abstract type AbstractFableColor end; + +function Base.:(+)(a::AbstractFableColor, b::AbstractFableColor) + typeof(a)(a.color + b.color) +end + +#------------------------------------------------------------------------------# +# Standard Color +#------------------------------------------------------------------------------# + +""" + c = StandardColor{RGBA(0,0,0,0)} + +Will create a Standard Fable Color that is transparent white. +It is just a wrapper used on RGBA for dispatch +""" +struct StandardColor{C <: RGBA} <: AbstractFableColor + color::C +end + +function Base.:(+)(a::StandardColor, b::StandardColor) + StandardColor(a.color + b.color) +end + +#------------------------------------------------------------------------------# +# Priority Color +#------------------------------------------------------------------------------# +# Not sure if priority should be an int (for layer num) or float (for position) + +""" + pc = PriorityColor(RGBA{0,0,0,0}, 0) + +Will create a transparent white PriorityColor with a priority of 0. +This is useful for when you want to blend objects together in the same kernel +so you can tell which object is meant to be on top of one another. +It is particularly useful when the objects are moving in space and need more +depth information. +""" +struct PriorityColor{C <: RGBA, P <: Number} <: AbstractFableColor + color::C + priority::P +end + +PriorityColor(c::C) where C <: RGBA = PriorityColor(c, 0) + +function Base.:(+)(a::PriorityColor, b::PriorityColor) + if a.priority > b.priority + return a + else + return b + end +end + +#------------------------------------------------------------------------------# +# Depth Color +#------------------------------------------------------------------------------# +# val will just += 1 in the kernel, but then be postprocessed at the end +""" + dc = DepthColor(RGBA{0,0,0,0}, 0) + +Will create a transparent white DepthColor value with a total value of 0. +This color is useful for when you want to do shading based on the number of +points that fall into each pixel bin such as for fractal flame methods. +The depth information will be encoded in the postprocessing steps +""" +struct DepthColor{C <: RGBA, V <: Number} + color::C + val::V +end + +DepthColor(c::C) where C <: RGBA = DepthColor(c, 0) + +function Base.:(+)(a::DepthColor, b::DepthColor) + return DepthColor(a.color + b.color, a.val + b.val) +end diff --git a/src/structs/fable_operators/generators.jl b/src/structs/fable_operators/generators.jl index cb90472..a8b5a74 100644 --- a/src/structs/fable_operators/generators.jl +++ b/src/structs/fable_operators/generators.jl @@ -9,6 +9,15 @@ export AbstractGenerator, ChaosGenerator, StandardGenerator abstract type AbstractGenerator end; +struct RandomGenerator{A, ND} <: AbstractGenerator + args::A + ndrange::ND +end + +#------------------------------------------------------------------------------# +# Planned generators +#------------------------------------------------------------------------------# +#= struct ChaosGenerator{A, I <: Integer, P <: Tuple, F <: Tuple} <: AbstractGenerator args::A @@ -27,8 +36,4 @@ struct SequentialGenerator{A, ND} <: AbstractGenerator args::A ndrange::ND end - -struct RandomGenerator{A, ND} <: AbstractGenerator - args::A - ndrange::ND -end +=# diff --git a/src/structs/fable_user_methods.jl b/src/structs/fable_user_methods.jl index d0b5cf6..1f77c7e 100644 --- a/src/structs/fable_user_methods.jl +++ b/src/structs/fable_user_methods.jl @@ -34,7 +34,6 @@ keyword arguments """ struct FableUserMethod body::Expr - fi_num::Int end #------------------------------------------------------------------------------# @@ -132,8 +131,7 @@ macro fum(ex...) config = :transform force_inbounds = false - if length(ex) == 1 - else + if length(ex) > 1 for i = 1:length(ex)-1 if ex[i] == :color || ex[i] == :shader || ex[i] == :(:shader) || ex[i] == :(:color) @@ -202,7 +200,6 @@ function (a::FableUserFragment)(args...; kwargs...) ks = keys(kwargs) vals = values(NamedTuple(kwargs)) known_kwargs = __extract_keys(final_kwargs) - fi_num = 0 for i = 1:length(final_kwargs) for j = 1:length(ks) @@ -210,7 +207,6 @@ function (a::FableUserFragment)(args...; kwargs...) s = known_kwargs[i] if isa(vals[j], FableInput) final_kwargs[i] = :($s = fi_buffer[$(vals[j].index)]) - fi_num += 1 else final_kwargs[i] = :($s = $(vals[j])) end @@ -226,5 +222,5 @@ function (a::FableUserFragment)(args...; kwargs...) end # combining it all together - return FableUserMethod(Expr(:block, final_kwargs..., a.body), fi_num) + return FableUserMethod(Expr(:block, final_kwargs..., a.body)) end diff --git a/test/extra_tests.jl b/test/extra_tests.jl new file mode 100644 index 0000000..36dc953 --- /dev/null +++ b/test/extra_tests.jl @@ -0,0 +1,14 @@ +@testset "color tests" begin + color = RGBA(rand(), rand(), rand(), rand()) + + sc = StandardColor(color) + dc = DepthColor(color) + + pc = PriorityColor(color) + + @test (sc+sc).color == color + color + @test (dc+dc).color == color + color + + # priority colors replace instead of directly adding + @test (pc+pc).color == color +end diff --git a/test/runtests.jl b/test/runtests.jl index 577c85f..895995d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -25,6 +25,9 @@ include("object_tests.jl") # Example testsuite is flaky, but can be run locally. #include("example_tests.jl") +# Non Array-based tests +include("extra_tests.jl") + function run_tests(ArrayTypes) for ArrayType in ArrayTypes histogram_testsuite(ArrayType) From 06bc7f8ee2ec8858a54c586b57089af1cdf06cb3 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Thu, 25 Jul 2024 21:36:54 +0200 Subject: [PATCH 12/14] checking if Fable Fragments return values --- src/structs/fable_user_methods.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/structs/fable_user_methods.jl b/src/structs/fable_user_methods.jl index 1f77c7e..86ec695 100644 --- a/src/structs/fable_user_methods.jl +++ b/src/structs/fable_user_methods.jl @@ -40,6 +40,18 @@ end # Macro @fum #------------------------------------------------------------------------------# +# Check whether there are `return` statements +function contains_return(expr) + result = false + MacroTools.postwalk(expr) do ex + if @capture(ex, return x_) + result = true + end + expr + end + result +end + function __find_kind(s) if s == :color return FUMColor() @@ -98,6 +110,9 @@ function __create_fum_stuff(expr, config, force_inbounds) end end + if contains_return(body_qt) + error("Fable User Methods must not return values!") + end return (body_qt, def[:kwargs]) end From be5053866aba7d390630d47f47ad76078511d1a7 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Fri, 26 Jul 2024 14:21:24 +0200 Subject: [PATCH 13/14] adding fum fusion --- src/structs/fable_user_methods.jl | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/structs/fable_user_methods.jl b/src/structs/fable_user_methods.jl index 86ec695..c90da93 100644 --- a/src/structs/fable_user_methods.jl +++ b/src/structs/fable_user_methods.jl @@ -8,7 +8,7 @@ # the FableOperator and FableExecutable level. # #------------------------------------------------------------------------------# -export FableUserMethod, @fum +export FableUserMethod, @fum, fuse_fums abstract type AbstractFUMKind end; @@ -239,3 +239,29 @@ function (a::FableUserFragment)(args...; kwargs...) # combining it all together return FableUserMethod(Expr(:block, final_kwargs..., a.body)) end + +#------------------------------------------------------------------------------# +# Fusion / Aux utils +#------------------------------------------------------------------------------# + +function fuse_fum_expr(a::FableUserMethod, b::FableUserMethod) + return Expr(:block, a.body, b.body) +end + +function fuse_fum_expr(a::FableUserMethod) + return a.body +end + +function fuse_fum_expr(t::T) where T <: Tuple + Expr(:block, fuse_fum_expr.(t)...) +end + +function fuse_fum_expr(a::Any) + error("Fable can only fuse objects of type FableUserMethod, not "* + string(typeof(a)) *"!") +end + +function fuse_fums(args...) + FableUserMethod(Expr(:block, fuse_fum_expr.(args)...)) +end + From 5a2cdfab21954ed6063b4d1340d5dd3855c51e69 Mon Sep 17 00:00:00 2001 From: James Schloss Date: Fri, 2 Aug 2024 01:51:02 +0200 Subject: [PATCH 14/14] a few more changes --- .../fable_operators/fable_operators.jl | 28 ++++++++----------- src/structs/fable_user_methods.jl | 7 +++++ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/structs/fable_operators/fable_operators.jl b/src/structs/fable_operators/fable_operators.jl index c56ab2e..3dc4ef1 100644 --- a/src/structs/fable_operators/fable_operators.jl +++ b/src/structs/fable_operators/fable_operators.jl @@ -1,25 +1,19 @@ +#-------------fable_operators.jl-----------------------------------------------# +# +# Purpose: This file defines the FableOperator, a method to combine +# Fable User Methods +# +#------------------------------------------------------------------------------# export FableOperator, fo -struct FableOperator{G, F, KW} where {G <: AbstractGenerator, - F <: Function, KW <: Tuple, - gen::G - fx::F - kwargs::KW -end +abstract type FableOperator end; """ - @fo generator fxs = (fum_1, fum_2, fum_3) colors = (clr_1, clr_2, clr_3) + fo(operator_type, args..., kwargs...) -Will create a Fable Operator with a function that combines all the Fable User -Methods and associated colors. +Will create a FableOperator of `operator_type` with necessary args and kwargs """ -macro fo(gen, args...) - if gen == :RandomGenerator - expr = generate_random_fo(args...) - else - error("Unknown generator: ", gen, "!") - end +function fo(fo_t::Type{FO}, args; kwargs) where FO <: FableOperator + fo_t(args...; kwargs...) end -@generated function fo(generator::G, fums, colors) where G <: AbstractGenerator -end diff --git a/src/structs/fable_user_methods.jl b/src/structs/fable_user_methods.jl index c90da93..7ec7c2e 100644 --- a/src/structs/fable_user_methods.jl +++ b/src/structs/fable_user_methods.jl @@ -261,6 +261,13 @@ function fuse_fum_expr(a::Any) string(typeof(a)) *"!") end +function fuse_fum_expr(a::FableUserFragment) + error("FableUserMethod not congifured!\n"* + "Please configure the fum first by calling it "* + "with key word arguments like:\n"* + " fum = f(q = 7)") +end + function fuse_fums(args...) FableUserMethod(Expr(:block, fuse_fum_expr.(args)...)) end