Calling multiple user plot recipes from single recipe

I am building a suite of Plots.jl recipes for my common visualizations, which has been very helpful–this functionality is awesome.

I’ve arrived at a point where I would like to compose multiple of my user recipes in a single plot axis. A MWE as follows:

I have two user recipes as follows (which in the real case do some more styling, etc., but this is the gist), and a type which I use for other purposes but would like to provide a recipe for.

@userplot MultiSeries
@recipe function f(ms::MultiSeries)
    time, ys... = ms.args
    for (i, y) in enumerate(ys)
        @series begin
            return time, y
        end
    end
end

@userplot endPlot
@recipe function f(tp::endPlot)
    t_end = tp.args[1]
    @series begin
        seriestype := :vline
        label --> "\$t_\\text{end}\$"
        return [t_end]
    end
end

struct SeveralData
    t
    ys
    t_end
end

s = SeveralData(1:10, [rand(10) for _ in 1:3],  12)

What I am hoping to do is provide a recipe as follows:

@recipe function f(s::SeveralData)
    return [MultiSeries((s.t, s.Ts...)), tendPlot(s.t_end)]
end

so that instead of

multiseries(s.t, s.ys...)
tendplot!(s.t_end)

I can simply do

plot(s)

As written, I get ERROR: Cannot convert MultiSeries to series data for plotting. Is there a change to my syntax that fixes this? Or am I going about this the wrong way?

If all you want to do is to give the last series different styling you can just do

@userplot MultiSeries
@recipe function f(ms::MultiSeries)
    time, ys... = ms.args
    for (i, y) in enumerate(ys)
        if i == length(ys)
              # do it different
        else
            @series begin
                 return time, y
            end
        end
    end
end

It is possible to return objects to call a different recipe, but I don’t think that works with inhomogeneous vectors like you have here.

With this example, this worked:

@recipe function f(s::SeveralData)
           @series begin
               return MultiSeries((s.t, s.ys...))
           end
           @series begin
               return endPlot(s.t_end)
           end
       end

That worked a treat, thanks! I spent a good half hour thinking that it was causing a mess with my styling, but it turned out I just forgot which of my recipes was which.

And for any future readers: my use case has if branches where it checks for missing data, in which case it only applies some of these series, and the recipe still handles everything great.

1 Like