210 lines
5.6 KiB
Julia
210 lines
5.6 KiB
Julia
module Visualization
|
||
include("gray_scott_solver.jl")
|
||
using GLMakie, Observables, Makie
|
||
using Random
|
||
using .GrayScottSolver
|
||
|
||
"""
|
||
step_through_solution(sol::SolutionType, N::Int)
|
||
|
||
Function for visualization for the output of run_simulation
|
||
|
||
# Arguments
|
||
- sol: computed differential equation by run_simulation
|
||
- N: size of the N×N grid
|
||
|
||
# Returns
|
||
- Displays created figure
|
||
"""
|
||
function step_through_solution(sol, N::Int)
|
||
fig = Figure(resolution=(600, 600))
|
||
ax = Axis(fig[1, 1])
|
||
slider = Slider(fig[2, 1], range=1:length(sol), startvalue=1)
|
||
|
||
textbox = Textbox(fig[1, 2], placeholder="Feed Rate", validator=Float64)
|
||
F = Observable(0.060)
|
||
# Initialize heatmap with first time step
|
||
u0 = reshape(sol[1][1:N^2], N, N)
|
||
heat_obs = Observable(u0)
|
||
heatmap!(ax, heat_obs, colormap=:magma)
|
||
|
||
on(textbox.stored_string) do s
|
||
F[] = parse(Float64, s)
|
||
end
|
||
|
||
# Update heatmap on slider movement
|
||
on(slider.value) do i
|
||
u = reshape(sol[i][1:N^2], N, N)
|
||
heat_obs[] = u
|
||
end
|
||
|
||
display(fig)
|
||
end
|
||
|
||
function coord_to_index(x, y, N)
|
||
ix = clamp(round(Int, x), 1, N)
|
||
iy = clamp(round(Int, y), 1, N)
|
||
return ix, iy
|
||
end
|
||
|
||
function reset!(U, V, heat_obs)
|
||
U .= 1.0
|
||
V .= 0.0
|
||
# Default starting point for Run
|
||
default_start!(U, V)
|
||
heat_obs[] = copy(U)
|
||
end
|
||
|
||
function default_start!(U, V)
|
||
center = size(U, 1) ÷ 2
|
||
radius = 10
|
||
U[center-radius:center+radius, center-radius:center+radius] .= 0.50
|
||
V[center-radius:center+radius, center-radius:center+radius] .= 0.25
|
||
end
|
||
|
||
function starting_points!(U, V)
|
||
N = size(U, 1)
|
||
radius = 10
|
||
jitter = 5
|
||
margin = radius + 2
|
||
rels = [
|
||
(1 / 4, 1 / 4), (3 / 4, 1 / 4), (1 / 2, 1 / 2),
|
||
(1 / 4, 3 / 4), (3 / 4, 3 / 4), (1 / 2, 1 / 8)
|
||
]
|
||
for (px, py) in rels
|
||
cx = clamp(round(Int, px * N) + rand(-jitter:jitter), margin, N - margin)
|
||
cy = clamp(round(Int, py * N) + rand(-jitter:jitter), margin, N - margin)
|
||
for x in (cx-radius):(cx+radius), y in (cy-radius):(cy+radius)
|
||
if 1 ≤ x ≤ N && 1 ≤ y ≤ N && hypot(x - cx, y - cy) ≤ radius
|
||
U[x, y] = 0.50
|
||
V[x, y] = 0.25
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
function param_box!(grid, row, labeltxt, observable::Observable)
|
||
Label(grid[row, 1], labeltxt)
|
||
box = Textbox(grid[row, 2], validator=Float64, width=50,
|
||
placeholder=labeltxt, stored_string="$(observable[])")
|
||
on(box.stored_string) do s
|
||
try
|
||
observable[] = parse(Float64, s)
|
||
println("changed $labeltxt to $s")
|
||
catch
|
||
@warn "Invalid input for $labeltxt: $s"
|
||
end
|
||
end
|
||
end
|
||
|
||
function build_ui(U, V, Du, Dv, F, k, params_obs, heat_obs)
|
||
reset!(U, V, heat_obs)
|
||
fig = Figure(size=(800, 800))
|
||
gh = GridLayout(fig[1, 1])
|
||
ax = Axis(gh[1, 1])
|
||
heatmap!(ax, heat_obs, colormap=:viridis)
|
||
deactivate_interaction!(ax, :rectanglezoom)
|
||
ax.aspect = DataAspect()
|
||
|
||
run_label = Observable("Run")
|
||
stepsize = Observable(30)
|
||
spoint = select_point(ax.scene)
|
||
|
||
# # Controls
|
||
fig[2, 1] = buttongrid = GridLayout(ax.scene, tellwidth=false)
|
||
btn_step = Button(buttongrid[1, 1], width=60, label="Step")
|
||
btn_start = Button(buttongrid[1, 2], width=60, label=run_label)
|
||
btn_reset = Button(buttongrid[1, 3], width=60, label="Reset")
|
||
btn_Jaguar = Button(buttongrid[1, 4], width=60, label="Jaguar")
|
||
|
||
slidergrid = SliderGrid(fig[3, 1], (label="Speed", range=1:100,
|
||
format="{}x", width=350,
|
||
startvalue=stepsize[]))
|
||
speed_slider = slidergrid.sliders[1].value
|
||
|
||
gh[1, 2] = textboxgrid = GridLayout(ax.scene, tellwidth=false)
|
||
rowsize!(gh, 1, Relative(1.0))
|
||
param_box!(textboxgrid, 1, "Du", Du)
|
||
param_box!(textboxgrid, 2, "Dv", Dv)
|
||
param_box!(textboxgrid, 3, "Feed", F)
|
||
param_box!(textboxgrid, 4, "kill", k)
|
||
|
||
println("aufruf")
|
||
|
||
running = Observable(false)
|
||
on(running) do r
|
||
run_label[] = r ? "Pause" : "Run"
|
||
end
|
||
|
||
on(speed_slider) do s
|
||
stepsize[] = s
|
||
println("Changed stepsize to $s")
|
||
end
|
||
|
||
on(btn_step.clicks) do _
|
||
multi_step!((U, V), stepsize[], heat_obs, params_obs)
|
||
end
|
||
|
||
on(btn_start.clicks) do _
|
||
running[] = !running[]
|
||
if running[]
|
||
reset!(U, V, heat_obs)
|
||
@async while running[]
|
||
multi_step!((U, V), stepsize[], heat_obs, params_obs)
|
||
sleep(0.0015)
|
||
end
|
||
end
|
||
end
|
||
|
||
on(btn_Jaguar.clicks) do _
|
||
|
||
Du[] = 0.142
|
||
Dv[] = 0.078
|
||
F[] = 0.0617
|
||
k[] = 0.062
|
||
|
||
if !running[]
|
||
running[] = true
|
||
U .= 1.0
|
||
V .= 0.0
|
||
@async while running[]
|
||
multi_step!((U, V), stepsize[], heat_obs, params_obs)
|
||
sleep(0.0015)
|
||
end
|
||
end
|
||
|
||
starting_points!(U, V)
|
||
heat_obs[] = copy(U)
|
||
end
|
||
|
||
on(btn_reset.clicks) do _
|
||
running[] = false
|
||
reset!(U, V, heat_obs)
|
||
end
|
||
on(spoint) do pt
|
||
r = 5
|
||
if pt === nothing
|
||
return
|
||
end
|
||
x, y = pt
|
||
println("params_obs[].N, ", params_obs[].N)
|
||
i, j = coord_to_index(x, y, params_obs[].N)
|
||
|
||
# get corners of square that will get filled with concentration
|
||
imin = max(i - r, 1)
|
||
imax = min(i + r, params_obs[].N)
|
||
jmin = max(j - r, 1)
|
||
jmax = min(j + r, params_obs[].N)
|
||
|
||
# set disbalanced concentration of U and V
|
||
U[imin:imax, jmin:jmax] .= 0.5
|
||
V[imin:imax, jmin:jmax] .= 0.25
|
||
|
||
heat_obs[] = copy(U)
|
||
end
|
||
return fig
|
||
end
|
||
|
||
export step_through_solution, build_ui
|
||
end
|