I want to make an interactive plot with sliders, where the displayed data changes when my sliders get moved. That seems to work when the observable plotted is an array of positions.
But is it possible if the observable is something else, say a dataframe?
For example, this works nicely (adapted from an old Makie issue post):
using GLMakie
fig = Figure()
ax = Axis(fig[1,1:2])
s1 = Slider(fig[2,1], range = 0.1:0.1:10, startvalue = 3)
s2 = Slider(fig[2,2], range = LinRange(-2pi, 2pi, 100), startvalue = 0.0)
data = lift(s2.value) do v
map(LinRange(0, 2pi, 100)) do x
4f0 .* Point2f0(sin(x) + (sin(x * v) .* 0.1), cos(x) + (cos(x * v) .* 0.1))
end
end
p = scatter!(fig[1,1:2], data, markersize = s1.value)
But what would I do if the data Observable is not a nice positions array? Then updates don’t seem to work right:
data = lift(s2.value) do v
xs = [4f0 .* (sin(x) + (sin(x * v) .* 0.1)) for x in LinRange(0, 2pi, 100)]
ys = [4f0 .* (cos(x) + (cos(x * v) .* 0.1)) for x in LinRange(0, 2pi, 100)]
ys2 = [4f0 .* (cos(x) + (cos(x * v) .* 0.3)) for x in LinRange(0, 2pi, 100)]
[xs, ys, ys2]
end
p = scatter!(fig[1,1:2], data[][1], data[][2], markersize = s1.value)
There the first slider still changes the point size, and the second slider still changes the underlying data, but it does not get replotted. What could I do here?
I tried using
p = lift(data) do d
scatter!(d[1], d[2], markersize = s1.value)
end
but that adds entirely new plots to the graph, i.e. just overlays new points into the plot, whenever the slider is moved.
What’s the best way to get a plot that updates correctly, if say the Observable is an array, or maybe even a DataFrame?
The only way I could think about doing this is creating a new separate observable (linked to the data observable) that converts each data part I want to plot into a positions array.
data = lift(s2.value) do v
xs = [4f0 .* (sin(x) + (sin(x * v) .* 0.1)) for x in LinRange(0, 2pi, 100)]
ys = [4f0 .* (cos(x) + (cos(x * v) .* 0.1)) for x in LinRange(0, 2pi, 100)]
ys2 = [4f0 .* (cos(x) + (cos(x * v) .* 0.3)) for x in LinRange(0, 2pi, 100)]
[xs, ys, ys2]
end
data1 = lift(data) do v
map(i -> Point2f0(data[][1][i], data[][2][i]), 1:1:length(data[][1]))
end
data2 = lift(data) do v
map(i -> Point2f0(data[][1][i], data[][3][i]), 1:1:length(data[][1]))
end
p = scatter!(fig[1,1:2], data1, markersize = s1.value)
p = scatter!(fig[1,1:2], data2, markersize = s1.value)
That works but seems very kludgy. And the selection of which columns to plot is hard coded.
Is this really the only way to do this? Or is there a way to say just pass say the first and second column of the data array to scatter!(), and still have it update with a data change?
Side question, is there a way to autoscale the y axis to the data while the slider is being moved?