Interactive subplots inspection with Makie

Hi,

I am using Makie for exploring the data of particle trajectories. Currently, my output looks like this:

The trajectories can be long, and I may wish to zoom-in to a specific time interval for careful inspection. Now in GLMakie, since all the axis for the line plots are linked, I can select a range on the screen and zoom-in. However, the 3D plots are not updated in synchronization. Are there any ways to improve the visualization?

I looked at IntervalSlider and Slider, but I am not sure if they are what I am looking for. Any suggestions?

Here is a link to the demo data output_1.jld2.

Source code:

using JLD2
using GLMakie
#using CairoMakie

filename = "output_1.jld2"

k = [300., 800., 3000., 5000*pi]

d = jldopen(filename)
t = d["trange"] ./ 2π
x, y, z = d["x"], d["y"], d["z"]
vx, vy, vz = d["vx"], d["vy"], d["vz"]
bx, by, bz = d["bx"], d["by"], d["bz"]
bmag = @. sqrt(bx^2 + by^2 + bz^2)
bmag_perp = @. sqrt(bx^2 + by^2)
rL = d["rL"]
jx, jy, jz = d["jx"], d["jy"], d["jz"]
jmag = @. sqrt(jx^2 + jy^2 + jz^2)

# pitch angle
μ = [
   (bx[i] * vx[i] + by[i] * vy[i] + bz[i] * vz[i]) /
   (sqrt(bx[i]^2 + by[i]^2 + bz[i]^2) *
    sqrt(vx[i]^2 + vy[i]^2 + vz[i]^2)) for i in eachindex(x)
]

invariant1st = similar(x)

for i in eachindex(bx)
   local bmag = hypot(bx[i], by[i], bz[i])
   vpar = (vx[i] * bx[i] + vy[i] * by[i] + vz[i] * bz[i]) / bmag
   vperp2 = vx[i]^2 + vy[i]^2 + vz[i]^2 - vpar^2
   invariant1st[i] = vperp2 / bmag
end

f = Figure(backgroundcolor = RGBf(0.98, 0.98, 0.98),
    size = (2000, 1200), fontsize=18)

colsize!(f.layout, 1, Relative(3/5))

ga = f[1,1] = GridLayout()
gcd = f[1, 2] = GridLayout()
gc = gcd[1, 1] = GridLayout()
gd = gcd[2, 1] = GridLayout()

nrow = 7

axs = [Axis(ga[row, col]) for row in 1:nrow, col in 1:1]

ind_ = findfirst("_", filename)[1] + 1
id = parse(Int, filename[ind_:end-5])
ik_ = (id - 1) % length(k) + 1

axs[1].title = "Particle ID $id, k = $(round(k[ik_], digits=0))"

lines!(axs[1], t, μ)
lines!(axs[2], t, invariant1st)
lines!(axs[3], t, z)
lines!(axs[4], t, bx, label = L"B_x")
lines!(axs[4], t, by, label = L"B_y")
lines!(axs[4], t, bz, label = L"B_z")
lines!(axs[5], t, bmag, label = L"B_\text{total}")
lines!(axs[5], t, bmag_perp, label = L"B_\text{perp}")
lines!(axs[6], t, rL)
lines!(axs[7], t, jx, label = L"J_x")
lines!(axs[7], t, jy, label = L"J_y")
lines!(axs[7], t, jz, label = L"J_z")
lines!(axs[7], t, jmag, linestyle=:dash, color=:black, label = L"J_\text{total}")

linkxaxes!(axs...)
@views hidexdecorations!.(axs[1:end-1], grid=false, ticks=false)
xlims!(axs[end], low=0, high=round(t[end]))

Legend(ga[4,2], axs[4], nbanks=1)
Legend(ga[5,2], axs[5], nbanks=1)
Legend(ga[7,2], axs[7], nbanks=1)

ylabels = (L"\mu", L"M", "Z", L"B", L"B_\text{total}", L"r_L", L"J")

for (i, ax) in enumerate(axs)
   ax.ylabel = ylabels[i]
end
axs[end].xlabel = L"t\,\Omega_0/2\pi"

## Trajectory with B field magnitude
ax3d = Axis3(gc[1, 1], title = "Trajectory")
scatter!(ax3d, x[1], y[1], z[1], color=:tomato, markersize=10)
scatter!(ax3d, x[end], y[end], z[end], color=:purple, markersize=10)
m = lines!(ax3d, x, y, z,  linewidth=2,color = bmag, colormap=:berlin)

Colorbar(gc[1, 2], m, label = "|B|")

## Trajectory with time
ax3d = Axis3(gd[1, 1], title = "Trajectory")
scatter!(ax3d, x[1], y[1], z[1], color=:tomato, markersize=10)
scatter!(ax3d, x[end], y[end], z[end], color=:purple, markersize=10)
m = lines!(ax3d, x, y, z, color = t, colormap=:solar)

Colorbar(gd[1, 2], m, label = "time")

f

You can grab the observable ax.finallimits from any of the 2d axes and restrict the 3d axes based on that, there’s currently no inbuilt axis linking for Axis3

1 Like

Oh that would be nice! Could you elaborate a bit how to modify my code? I now have these following lines added:

time_span = axs[1].finallimits

t1_ = findfirst(x -> x >= time_span[].origin[1], t)
t2_ = findlast(x -> x <= time_span[].widths[1], t)
xs = @views Observable(x[t1_:t2_])
ys = @views Observable(y[t1_:t2_])
zs = @views Observable(z[t1_:t2_])

and I can see that by selecting a range in the line plots, xs, ys, zs are updated. However, I missed the part to update the 3D plots. Should I use on() or lift() to achieve that?