Basically, you want to use the exact same scheme as in my first post, but this needs some getting used to. Below is a minimal recipe to approach the problem.
- Firstly, always make a plot without interactivity first to make sure everything else besides the interactive part is already working. For your example this could look something like
using GLMakie
GLMakie.activate!()
position = 0.0
velocity = 0.1
time_step = 0.1
m = 1
num_steps = 100
# these are supposed to be varied later on
k = 0.0
c = 0.0
positions = [position]
times = [0.0]
# Simulation function
function simulate_step(position, velocity, k, c)
acceleration = ((-k) * position - c * velocity) / m
new_velocity = velocity + acceleration * time_step
new_position = position + new_velocity * time_step
return new_position, new_velocity
end
# Create the plot
fig = Figure(resolution = (800, 600))
ax = Axis(fig[1,1])
for i in 1:num_steps
p, v = simulate_step(position, velocity, k, c)
push!(positions, p)
push!(times, i*time_step)
end
lines!(times, positions)
display(fig)
- Then you step through your script and insert the necessary widgets and listeners (
on, onany
) calls.
using GLMakie
GLMakie.activate!()
position = 0.0
velocity = 0.1
time_step = 0.1
m = 1
num_steps = 100
positions = [position]
times = [0.0]
k = Observable(0.0)
c = Observable(0.0)
positions = Observable([position])
times = Observable([0.0])
# Simulation function
function simulate_step(position, velocity, k, c)
acceleration = ((-k) * position - c * velocity) / m
new_velocity = velocity + acceleration * time_step
new_position = position + new_velocity * time_step
return new_position, new_velocity
end
# Create the plot
fig = Figure(resolution = (800, 600))
ax = Axis(fig[1,1])
# Create a slider for stiffness
stiffness_slider = Slider(fig[2, 1], value = k[], range = 0.1:0.1:20.0, startvalue = 0)
# Create a slider for damping
damping_slider = Slider(fig[3, 1], value = c[], range = 0.1:0.1:5.0, startvalue = 0)
on(stiffness_slider.value) do value
k[] = value
end
on(damping_slider.value) do value
c[] = value
end
onany(k, c) do k, c
# need to reset storage for next plot update
# use .val on Observable objects to avoid triggering premature plotting updates
# this is needed to avoid length-mismatch errors and similar problems
resize!(positions.val, 1)
resize!(times.val, 1)
positions.val[1] = position
times.val[1] = 0.0
for i in 1:num_steps
p, v = simulate_step(position, velocity, k, c)
push!(positions.val, p)
push!(times.val, i*time_step)
end
# when done updating data notify everyone listening to our data
notify(positions)
end
# trigger the pipeline once so that we have something to display on first plot
notify(stiffness_slider.value)
on(positions) do _
reset_limits!(ax)
end
lines!(times, positions)
display(fig)
- [Optional] Optimize the pipeline if needed. In your case one could think about whether it is a good idea to empty the vectors
positions, times
on every plot update. A more performant version would use a preallocated vector of length num_steps
for both positions, times
.