How to insert an interactive condition inside a Makie recipe?

The following MWE is self-explanatory:

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