using GLMakie
import Makie
# define recipe with condition "showlines"
Makie.@recipe(MyPlot, x, y) do scene
Makie.Attributes(;
showlines=true
)
end
# toggle between scatterlines and scatter
function Makie.plot!(plot::MyPlot)
if plot.showlines[] # this is the root of the issue
Makie.scatterlines!(plot, plot.x, plot.y)
else
Makie.scatter!(plot, plot.x, plot.y)
end
end
# initialize plot
x = 1:10
y = rand(10)
showlines = Observable(true)
myplot(x, y; showlines)
# doesn't work
showlines[] = false
Setting the observable doesn’t trigger changes in the scene, and that is probably because the syntax plot.showlines[] is extracting the node value at that point in time (true) and the only branch that is registered in the compute graph is the first one.
I tried to wrap the condition with the new compute pipeline, but it gives an error:
function Makie.plot!(plot::MyPlot)
Makie.map!(plot, :showlines, :nothing) do showlines
if showlines
Makie.scatterlines!(plot, plot.x, plot.y)
else
Makie.scatter!(plot, plot.x, plot.y)
end
end
end
If there is a better idiom that plays well with compute pipelines, I’d be happy to learn.
I’d probably do a scatter and then a separate lines whose visible is connected to your showlines. scatterlines is just scatter plus lines anyway so that’d be the same result. In general, plot insertion is not something that is intended to be governed by the compute graph, it’s the attributes of the inserted plots that form the graph.
The exception here is plotlist! to which you can pass an observable of specs. You can use this, it’s just a bit less efficient to update because it has to do some diffing on the arguments I think to determine what can stay and what needs to be removed on updates.
I’ve been using plotlist for example to implement annotations, because when you change the annotation object the visuals can change a lot.
The MWE with scatterlines vs. scatter was just an example. I wonder what the solution would look like with plotlist!. I could also consider making x and y empty if the recipes themselves handled the empty case, but I think it is not a general solution.
Could you please share how the MWE above could be rewritten in terms of plotlist!?
using GLMakie
import Makie.SpecApi as S
@recipe LineOrScatter (xs, ys) begin
show_lines = false
color = :tomato
end
function Makie.plot!(p::LineOrScatter)
map!(p.attributes, [:xs, :ys, :show_lines, :color], :plotspecs) do xs, ys, show_lines, color
if show_lines
return [S.Lines(xs, ys; color = color, linewidth = 3)]
else
return [S.Scatter(xs, ys; color = color, markersize = 12)]
end
end
plotlist!(p, p.plotspecs)
return p
end
x = range(0, 4pi; length = 60)
y = sin.(x) .+ 0.15 .* randn(length(x))
fig = Figure(size = (700, 500))
ax = Axis(fig[2, 1]; title = "lineorscatter recipe")
p = lineorscatter!(ax, x, y)
btn = Button(fig[1, 1];
label = lift(b -> b ? "Show as scatter" : "Show as lines", p.show_lines),
tellwidth = false,
)
on(btn.clicks) do _
p.show_lines[] = !p.show_lines[]
end
fig