Makie - How to clear ax whenever slider value change?

Hello,

How can I clear the ‘ax’ whenever the slider value changes? I don’t want multiple lines at the same time on the scene. The code is

using GLMakie

# Parameters
local m = 0.0
local c = 0.0

# # Create the plot
fig = Figure(resolution = (800, 600))
ax = Axis(fig[1,1])

# Simulation loop
function test()
    
    x = 0:0.01:10
    f(x) = m * x + c
    lines!(ax, x, f.(x))

end

# Create a slider for m
m_slider = Slider(fig[2, 1], value = m, range = 0.0:0.5:10.0)

# Create a slider for c
c_slider = Slider(fig[3, 1], value = c, range = 0.0:1.0:10.0)

# Add a callback for the m slider
on(m_slider.value) do value
    global m = value  # Update m value
    test()
end

# Add a callback for the c slider
on(c_slider.value) do value
    global c = value  # Update c value
    test()
end

# Display the figure
display(fig)

Could anyone please help?

Using globals to make Makie’s plots interactive is not optimal. Instead you should look into how Observables work. See here for more infos: Observables & Interaction · Makie

Here is a reworked version of your example

using GLMakie

# Parameters
m = Observable(0.0)
c = Observable(0.0)
x = 0:0.01:10
y = Observable(Float64[])
f(x,m,c) = m*x+c


# Create a figure with an axis
fig = Figure(resolution = (800, 600))
ax = Axis(fig[1,1])

# Create a slider for m
m_slider = Slider(fig[2, 1], value = m[], range = 0.0:0.5:10.0)

# Create a slider for c
c_slider = Slider(fig[3, 1], value = c[], range = 0.0:1.0:10.0)

y = lift(m_slider.value, c_slider.value) do m, c
   # lift is similar to on (or onany), only that its return value will be wrapped into an Observable
   return f.(x,m,c)
end

on(y) do _
    # whenever y changes we update the axis limits
    reset_limits!(ax)
end

lines!(ax, x, y)

# Display the figure
display(fig)
1 Like

Thank you @fatteneder. Based on the code you provided I write another code which uses some general function. But, I am not getting anything on the figure. Could you please check and let me know what is the issue now? The code is-

using GLMakie

k = Observable(0.0)
c = Observable(0.0)
position = 0.0
velocity = 0.0
time_step = 0.1
m = 1
num_steps = 100
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])

# 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(k) do k
    for i in 1:num_steps
        position, velocity = simulate_step(position, velocity, k, c)
        push!(positions, position)
        push!(times, i*time_step)
    end
    fig(lines!(times, positions))
end

on(damping_slider.value) do value
    c[] = value
end

on(c) do c
    for i in 1:num_steps
        position, velocity = simulate_step(position, velocity, k, c)
        push!(positions, position)
        push!(times, i*time_step)
    end
    fig(lines!(times, positions))
end

display(fig)

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.

  1. 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)

  1. 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)
  1. [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.