Makie - passing keyword arguments through a @recipe

Hi,

I’m trying to define a new recipe in Makie that is essentially an enhanced contour plot, and I would like any kwargs that get passed in the call for my new recipe to automatically get passed-through to my call to contour!. But it’s not clear to me how to do this.

More information: I’m trying to make new plot recipe that will use a kernel density estimate to plot 2D contours of the density of some points. I want kdecontour(x, y; kwargs...) to become a contour plot that plots density contours, and passes the options in kwargs onward to contour. My recipe looks like

@recipe(KDEContour, x, y) do scene
    Theme(
        levels = [0.1, 0.5],
        xexpansionfactor = 0.1,
        yexpansionfactor = 0.1,
        xgridsize=128,
        ygridsize=129
    )
end
function Makie.plot!(kdecontour::KDEContour)
    x = kdecontour.x[]
    y = kdecontour.y[]

    pts = vcat(x', y')
    k = KDE(pts)

    p_kde_pts = [pdf(k, pts[:,i]) for i in axes(pts, 2)]
    p_levels = [quantile(p_kde_pts, l) for l in kdecontour.levels[]]

    dx = maximum(x)-minimum(x)
    dy = maximum(y)-minimum(y)

    fx = kdecontour.xexpansionfactor[]/2
    xgrid = range(minimum(x)-fx*dx, maximum(x)+fx*dx, length=kdecontour.xgridsize[])

    fy = kdecontour.yexpansionfactor[]/2
    ygrid = range(minimum(y)-fy*dy, maximum(y)+fy*dy, length=kdecontour.ygridsize[])

    z = [pdf(k, [x, y]) for x in xgrid, y in ygrid]

    # I want any additional kwargs to get passed along to this invocation of `contour!`
    contour!(kdecontour, xgrid, ygrid, z; levels=p_levels)
end

(Nevermind that I’m ignoring all the Observable machinery for now—I’ll put that in later.). How do I do this?

Will

There’s kdecontour.attributes, you could copy that and pop! the observables you need elsewhere from it and then pass the remaining thing to contour

Sweet! That’s what I didn’t know about—thanks!

In newer versions you can also do:

contour!(kdecontour, shared_attributes(kdecontour, Contour), xgrid, ygrid, z; levels=p_levels)

Hi @sdanisch, I’m wondering how this could be working for FooMakieExt extensions… In fact, I’ve used the following pattern.
In the package, define

function fooplot end
function fooplot! end

and in the extension

module FooMakieExt

import fooplot,fooplot!

Makie.@recipe(FooPlot, arg1) do scene
    ...
end

function Makie.plot!(plt::Fooplot{<:Tuple{Arg1Type}})
    ...
end
end

This way fooplot and fooplot! can be accessed from other packages if Makie is loaded, however FooPlot itself only lives in the extension.
What would be the proper way to make shared_attributes(someplot,Fooplot) possible ?