Hi!
I am a big fan of de-marking boxes from their background using box shadows. Is there a way to do that in Makie?
Thanks for the help!
After studying a bit how to use mesh and thanks to the help of @asinghvi17 on slack, I manage to get a solution that works!
This was generated using the following code:
Summary
using CairoMakie, Colors
function generate_arc(radius, θ₀, θ₁, n_points=50)
@assert n_points ≥ 2 "n_points must be ≥ 2"
θs = range(start=θ₀, stop=θ₁, length=n_points)
vertices = [[0.0; cos.(θs)...] [0.0;sin.(θs)]]
faces = [ones(Int, n_points-1) 2:(n_points) 3:(n_points+1)]
return vertices .* radius, faces
end
function generate_box_shadow(width, height, extent=0.1*(width+height)/2; n_points=5, dark=RGBA(0.0, 0.0, 0.0, 0.5), light=RGBA(0.0, 0.0, 0.0, 0.0))
bottom_left_vertices, bottom_left_faces = generate_arc(extent, π, 3π/2, n_points)
bottom_right_vertices, bottom_right_faces = generate_arc(extent, -π/2, 0.0, n_points)
top_right_vertices, top_right_faces = generate_arc(extent, 0.0, π/2, n_points)
top_left_vertices, top_left_faces = generate_arc(extent, π/2, π, n_points)
bottom_right_vertices = bottom_right_vertices + repeat([width 0], n_points+1)
top_right_vertices = top_right_vertices + repeat([width height], n_points+1)
top_left_vertices = top_left_vertices + repeat([0 height], n_points+1)
vertices = [bottom_left_vertices; bottom_right_vertices; top_right_vertices; top_left_vertices]
face1 = [1 n_points+1 n_points+3]
face2 = [1 n_points+3 n_points+2]
faces = [
bottom_left_faces;
face1;
face2;
bottom_right_faces .+ (n_points+1);
face1 .+ (n_points+1);
face2 .+ (n_points+1);
top_right_faces .+ 2(n_points+1);
face1 .+ 2(n_points+1);
face2 .+ 2(n_points+1);
top_left_faces .+ 3(n_points+1);
[3(n_points+1)+1 4(n_points+1) 2];
[3(n_points+1)+1 2 1];
]
colors = repeat([dark; fill(light, n_points);], 4)
return vertices, faces, colors
end
function shadow(obj; extent=5, dark=RGBA(0.0, 0.0, 0.0, 0.5), padding=(5,5,5,5), meshkw=(;))
padding_left, padding_right, padding_bottom, padding_top = padding
inner_box = map(viewport(obj), obj.layoutobservables.protrusions) do v, protrusions
x,y = v.origin
w,h = v.widths
left = protrusions.left
right = protrusions.right
top = protrusions.top
bottom = protrusions.bottom
Rect2{Int}(
(x - left - padding_left, y - bottom - padding_bottom),
(
w + left + right + padding_left + padding_right ,
h + bottom + top + padding_bottom + padding_top
)
)
end
outer_box = map(inner_box) do v
x,y = v.origin
w,h = v.widths
Rect2{Int}(
(x - extent, y - extent),
( w + 2extent, h + 2extent)
)
end
mesh = map(inner_box) do v
widths = v.widths
vertices, faces, colors = generate_box_shadow(widths[1], widths[2], extent; n_points=5, dark)
vertices = vertices .+ repeat([extent extent], size(vertices, 1))
(;vertices, faces, colors)
end
vertices = map(mesh) do m
m.vertices
end
faces = map(mesh) do m
m.faces
end
colors = map(mesh) do m
m.colors
end
t = obj.scene
while !isnothing(t.parent)
t = t.parent
end
Box(t, color=:white, bbox=inner_box, strokevisible=false)
inset = LScene(t,
scenekw = (camera = campixel!,),
bbox=outer_box,
)
mesh!(inset, vertices, faces; color = colors, meshkw...)
translate!(inset.scene, 0, 0, 100)
inset
end
let
fig, ax, p = lines(-3π..3π, sin)
ax_inset = Axis(fig[1, 1],
width=Relative(0.2),
height=Relative(0.2),
halign=0.15,
valign=0.9,
spinewidth=0.2,
)
translate!(ax_inset.blockscene, 0, 0, 150)
lines!(ax_inset, -3π..3π, cos)
s = shadow(ax_inset, extent=5, padding=(5, 10, 5, 5), meshkw=(rasterize=4,), dark=RGBA(0.1, 0.1, 0.1, 0.25))
display(fig)
end
The problem is that meshes in CairoMakie are drawn triangle after triangle so you get ugly seams when you have transparency involved, as the pixels get more saturated there.
Yep, I noticed… I guess I could render the shadow in GLMakie then display it as an image, or use GLMakie for this specific figure.
Edit: or by accepting to become a criminal and offsetting the position of the nodes by \varepsilon to avoid seams
This looks awesome.
Are you planning to package this up to make it more accessible?
I can make PR to Makie’s repo, but would it be accepted despite the mesh artifact in CairoMakie? Which parameters would be interesting to keep / to add?
