Makie: How to convert keyword arguments in addition to positional arguments?

Hi,

When calling say Makie.mesh(args...;kwargs...) for some arguments with user defined-type, one can define an argument conversion for the positional arguments args using function Makie.convert_arguments.

But how to convert the keyword arguments kwargs ? I also have some user-defined types in the keyword arguments that can be mapped to the usual keyword arguments.

As a fallback, I am trying to define method

funciton Makie.plot!(p::Makie.Mesh{<:Tuple{<:MyType}})
   ## ... Some conversions ...
   Makie.mesh!(p,converted_args...;converted_kwargs...)
end

which gives access to the keyword arguments as well, But it does not work for Makie.Mesh.

Any clue on how to convert keyword arguments?

Thanks!

cc @sdanisch @jules

You can overload convert_attribute(value::YourType, ::Makie.key"attribute", ::Makie.key"mesh") .
See: Makie.jl/Makie/src/conversions.jl at master · MakieOrg/Makie.jl · GitHub for some examples.
Note, these will only get applied automatically for core Makie plots, not for user recipes, since it’s a bit hard to foresee what people want to do with e.g. a color attribute.

1 Like

Hi @sdanisch

thanks for the quick reply.

To convert the attribute, I need both the attribute and the passed argument. I guess you only have the attribute in convert_attribute.

This is my actual setting (after simplifying a bit): I have a custom type that contains a mesh plus a dict with arrays of data associated with the nodes of the mesh:

struct MeshWithData
    mesh::SomeMeshType
    node_data::Dict{String,Vector{Float64}}
end
struct NodeColor
    name::String
end

I want this API:

foo = MeshWithData(...)
Makie.mesh(foo;color=NodeColor("dataname"))

Which should colorate foo according to the data in key “dataname”.

Any ideas how to achieve this? I was hopping to dispatch on NodeColor somewhere.

BTW, I cannot pass directly the array of data like this

Makie.mesh(foo;color=foo.node_data["dataname"])

since the conversion of foo.mesh will possibly create and destroy nodes and foo.node_data["dataname"] will become out of sync.

You can add whatever vertex data you want to a mesh and it’ll expand like positions, normals, etc:

using GeometryBasics

m = normal_mesh(Rect3f(0,0,0,1,1,1), facetype = QuadFace{Int})
# Mesh{3, Float32, QuadFace{Int64}}
#     faces: 6
#     vertex position: 8
#     vertex normal: 6

m2 = GeometryBasics.mesh(
    m, 
    color = [RGBf(x, y, z) for x in 0:1 for y in 0:1 for z in 0:1],
    vertex_name = string.(1:8),
    face_name = per_face(string.(1:6), m)
)
# Mesh{3, Float32, NgonFace{3, OffsetInteger{-1, UInt32}}}
#     faces: 12
#     vertex position: 8
#     vertex normal: 6
#     vertex color: 8
#     vertex vertex_name: 8
#     vertex face_name: 6

expand_faceviews(m2)
# Mesh{3, Float32, NgonFace{3, OffsetInteger{-1, UInt32}}}
#     faces: 12
#     vertex position: 24
#     vertex normal: 24
#     vertex color: 24
#     vertex vertex_name: 24
#     vertex face_name: 24

If you need to do further processing with the data before plotting you can run expand_faceviews() like this and pass the result to Makie.mesh with the processed data as attributes.

1 Like

Hi @ffreyer,

Thanks for the hint. In a more complex example, I would like pass a function-like object in the color attribute and interpolate it on the mesh nodes, and use the resulting vector as color. So, I need a way of converting an attribute while having access to the positional arguments.

I provably will define a new recipe (which gives grater control) instead of trying to extend Makie.mesh.

But it would be cool to use a function that already exists (Makie.mesh) instead of creating a new one, if the difference is mainly conversions of arguments/attributes.

Yea convert_attribute() only considers the attribute itself, so you’d need something else. Maybe it’s useful to note that Makie.mesh can extract colors from a mesh and that the positions don’t change in convert_arguments/expand_faceviews. They just get duplicated. So you can figure out your vertex colors without worrying about what the plot function does to the mesh.

function build_mesh(N)
    ps = [Point2f(x, y) for x in 1:N for y in 1:2]
    fs = [QuadFace(2x-1, 2x, 2x+2, 2x+1) for x in 1:N-1]
    cs = per_face([x % 2 for x in 1:N-1], fs)
    return GeometryBasics.mesh(ps, fs, color = cs)
end
fig = Figure()
sl = Slider(fig[1,1], range = 2:20, startvalue = 20)
Makie.mesh(fig[2,1], map(build_mesh, sl.value))
fig
1 Like