# Makie @recipe need understanding

I’m having no luck finding a working example of how to define a makie @recipe for a custom type, or in this case custom types.

I have a home grown quaternion and a 3D vector type which match better how legacy simulations function making conversion better. The Vector type presents an x, y, and z field. The quaternion can rotate those vectors.

In Plots.jl recipe it is fairly easy to specify a plot overload that throws up 3 lines representing the XYZ axis of a quaternion rotation into a plot where I have an optional origin for the axis and a scale factor for the length of the axis.

The Plot.jl recipe I’m trying to emulate starts out with a from like this.

``````quaternion_plot
@recipe function f(q::quaternion, v::xyzVecT = xyzVecT(); scalefactor = 1.0)
# Quaternion will plot the X, Y and Z axis at the location
seriestype := :path
linestyle --> :solid
arrow --> :True
seriesalpha --> 0.4
marker --> :none
xaxis = xVec(scalefactor) * q
yaxis = yVec(scalefactor) * q
zaxis = zVec(scalefactor) * q
quickFmt(a,b) = [a.x,b.x], [a.y, b.y], [a.z, b.z]
# Plot the X Axis in Blue with reduced alpha
@series begin
label --> :none
seriescolor --> :blue
marker := :none
quickFmt(v, v + xaxis)  # Remove the vector markers
end
@series begin
label --> :none
seriescolor --> :green
marker := :none
quickFmt(v, v + yaxis) # Remove the vector markers
end
@series begin
label --> :none
seriescolor --> :red
marker := :none
quickFmt(v, v + zaxis) # Remove the vector markers
end

# Direction Pointers
@series begin
label --> "X"
seriestype --> :scatter
seriescolor --> :blue
marker := :diamond
temp=v + xaxis
[temp.x], [temp.y], [temp.z]
end

@series begin
label --> "Y"
seriestype --> :scatter
seriescolor --> :green
marker := :circle
temp=v + yaxis
[temp.x], [temp.y], [temp.z]
end

@series begin
label --> "Z"
seriestype --> :scatter
seriescolor --> :red
marker := :square
temp=v + zaxis
[temp.x], [temp.y], [temp.z]
end

end
``````

I cannot figure out how to get the Makie.@recipe to do anything, even if I skip the origin shift and the scale factor and just take the quaternion. I find just the one documentation example and a dataframe example on the internet. Can someone help me understand this @recipe concept and figure out how to use it or is it just too far off and I need to forget a recipe.

``````@recipe(QPlot, q, origin, scale_factor ) do scene
Attributes(
color = :red,
style = :dashed
)
end

function plot!(qp::QPlot)
xaxis = xVec(qp[:scale_factor][]) * qp[:q][]
yaxis = yVec(qp[:scale_factor][]) * qp[:q][]
zaxis = zVec(qp[:scale_factor][]) * qp[:q][]
quickFmt(a,b) = [a.x,b.x], [a.y, b.y], [a.z, b.z]
lines!(qp,quickFmt(qp[:origin], qp[:origin] + xaxis),color=qp[:color][],linestyle=qp[:style][])
lines!(qp,quickFmt(qp[:origin], qp[:origin] + yaxis),color=qp[:color][],linestyle=qp[:style][])
lines!(qp,quickFmt(qp[:origin], qp[:origin] + zaxis),color=qp[:color][],linestyle=qp[:style][])
qp
end

``````

Note: I’m assuming the quickFmt works the same for lines! as it did for the plot recipe, but I haven’t even gotten into the function to debug any of that stuff.

Thanks for any help and understanding.
Best Regards,
Allan

It needs to be `Makie.plot!` if you want to extend that function, that might be the first thing to try? There are a couple things that could be improved, for example you unwrap all observables so the plot will not be interactively updateable. Might have time to write an example later.

yeah, forget the observable unwrapping. I was following the DataFrame example I found, but that makes sense.

Try this to get started:

``````julia> @recipe(QPlot, q, origin, scale_factor ) do scene
Attributes(
color = :red,
style = :dashed
)
end

julia> function Makie.plot!(p::QPlot)
axs = lift(p[:q], p[:scale_factor], p[:origin]) do q, scale, ori
segments = mapreduce(vcat, [Vec3f(1, 0, 0), Vec3f(0, 1, 0), Vec3f(0, 0, 1)]) do v
v_rot = q * (scale * v)
[ori, ori + v_rot]
end
end
linesegments!(p, axs, color = [:red, :green, :blue])
p
end

julia> qplot(Makie.to_rotation(1.0), Point3f(2, 2, 2), 3.0)
``````

Whoa. That’s fancy. Thank you! I’ll give it a shot!

I think I have this part working now… in a package extension no left.
Do you by any change know, how to have default arguments for origin and scale_factor in this recipe or pass in kw arguments?

I would turn them into attributes if you need defaults and keywords.

1 Like

I think I have it all working, thank you.
I am having trouble when placing this recipe in an extension that gets loaded on MakieCore.

I don’t seem to be able to make the recipe generated plot function (quaternionplot) “external” or link back to the original module. Usually I would do something like OriginalModule.quaternionplot. But since that quaternionplot is created by the recipe, I can’t seem to make it hook up by just putting “external quaternionplot” in the extension.

I did have it working by defining empty functions in the OriginalModule for quaternionplot and quaternionplot!, but then I get a warning about broken modules because the @recipe macro was redefining it. ** incremental compilation may be fatally broken for this module **

I don’t see how it can have it both ways. I haven’t yet seen an example that lets me understand the best practice here. Any ideas?

Best Regards,
Allan Baker

Inside OriginalModule:

``````"quaternionplot requires a Makie backend to work as it is overloaded in OriginalModuleMakieExt.jl."
function quaternionplot() end

"quaternionplot! requires a Makie backend to work as it is overloaded in OriginalModuleMakieExt.jl."
function quaternionplot!() end

export quaternionplot, quaternionplot!
``````

Inside extension with recipe.
`import OriginalModule: quaternionplot, quaternionplot!`

Warning looks something like this where the I have changed some of the names to keep it generic:

`````` WARNING: Method definition quaternionplot() in module OriginalModule at D:\julia\development\OriginalModule\src\OriginalModule.jl:445 overwritten in module OriginalModuleMakieExt at C:\Users\username\.julia\packages\MakieCore\bttjb\src\recipes.jl:173.
│    ** incremental compilation may be fatally broken for this module **
``````

But it works. It just has nasty warnings.

If you put

``````function quaternionplot end  # <-- no parentheses
``````

instead, does it work without warning?

The latter defines an empty function (a function with no methods), whereas the former defines a function with one method (which does nothing).