HolyLab / FlyThroughPaths.jl

Create "fly-throughs" in 3d visualization
MIT License
11 stars 1 forks source link

`set_view!` doesn't force an update #12

Closed timholy closed 3 months ago

timholy commented 3 months ago

I'm sure I'm making a complete mess of this, but I'm trying to do a plot in which:

This makes use of FlyThroughPaths, ProtPlot, and some other machinery. Here's a slightly stripped-down version of the script:

using GLMakie
using FlyThroughPaths
using ProgressMeter

if !isdefined(@__MODULE__, :familyxyz)  # this creates a dataframe
    include("analyze_binding_residue_kinds.jl")
end

## Animated plot of residue locations by kind
pdbname = joinpath(v1r.parentdir, opsdname*"_aligned.pdb")
fig = Figure(size=(800, 800));
ax = ribbon_scene(pdbname; colors=[fill(RGBA(ribboncolor, 0.3), length(opsd))], backgroundcolor=:white)
mgrid = range(-30, stop=30, length=2)
zgrid = fill(15.0, 2, 2)
cgrid = fill(RGBA(0, 0, 0, 0.1), 2, 2)
surface!(ax, mgrid, mgrid, zgrid, color=cgrid, transparency=true)
surface!(ax, mgrid, mgrid, -zgrid, color=cgrid, transparency=true)
# Use an Observable for transparency so we can control it during animation.
alpha_even, alpha_odd = Observable(1.0), Observable(0.0)
xpos_even, ypos_even, zpos_even = Observable(Float64[]), Observable(Float64[]), Observable(Float64[])
xpos_odd, ypos_odd, zpos_odd = Observable(Float64[]), Observable(Float64[]), Observable(Float64[])
markersize=10
scatter!(ax, xpos_even, ypos_even, zpos_even; color=familycolordict["V1R"], markersize, alpha=alpha_even)
scatter!(ax, xpos_odd, ypos_odd, zpos_odd; color=familycolordict["V1R"], markersize, alpha=alpha_odd)
gdf = groupby(familyxyz, [:family, :orclass, :msacode])
vgdf = filter(collect(pairs(gdf))) do (k, v)
    k.family == "V1R"
end

# Fly in to the midpoint
r = 75
θ = -0.32175
n = 200
dθ = 2π / n
tland = 5
tdur = 4
path = Path(ViewState(eyeposition=Vec3f(120, -40, 35), lookat=zero(Vec3f), upvector=Vec3f(0, 0, 1), fov=45))
path *= ConstrainedMove(tland, ViewState(eyeposition=r*Vec3f(cos(θ), sin(θ), 0)))
idxlast = 0
prog = Progress(n; desc="Recording movie...")
display(fig)
# record(fig, "positive_residue_locations.mp4", LinRange(0, tland + tdur*20, n); framerate=10) do t
foreach(LinRange(0, tland + tdur*20, n)) do t
    global idxlast

    cview = t <= tland ? path(t) : ViewState(eyeposition=r*Vec3f(cos(θ + (t-tland)*dθ), sin(θ + (t-tland)*dθ), 0), lookat=zero(Vec3f), upvector=Vec3f(0, 0, 1), fov=45)
    if t > tland
        tnow = t - tland
        ti = round(Int, tnow / tdur)
        if ti >= idxlast
            idxlast += 1
            nextname, nextreceptor = vgdf[idxlast]
            if idxlast % 2 == 0
                empty!(xpos_even[]); empty!(ypos_even[]); empty!(zpos_even[])
                for row in eachrow(nextreceptor)
                    push!(xpos_even.val, row.x)
                    push!(ypos_even.val, row.y)
                    push!(zpos_even.val, row.z)
                end
                notify(xpos_even)
            else
                empty!(xpos_odd[]); empty!(ypos_odd[]); empty!(zpos_odd[])
                for row in eachrow(nextreceptor)
                    push!(xpos_odd.val, row.x)
                    push!(ypos_odd.val, row.y)
                    push!(zpos_odd.val, row.z)
                end
                notify(xpos_odd)
            end
        end
    end
    next!(prog)
    set_view!(ax, cview)
    alpha_even.val = (cos(π * t / tdur) + 1)/2
    alpha_odd[]  = (sin(π * t / tdur) + 1)/2
    sleep(0.01)
end
println("done")

While this is executing I just get a blank screen. It stays that way at the end. But if at the REPL I do

julia> set_view!(ax, capture_view(ax))

then I get

image

which is basically what I want. (The purple dots are the locations I'm showing in the even/odd lists.)

The record line also records a blank movie.

asinghvi17 commented 3 months ago

You need to run Makie.update_state_before_display!(fig) before taking the initial ViewState - that instantiates all limits. It's an optimization on the Makie end to avoid loops and extra computation.

timholy commented 3 months ago

Thanks! That didn't seem to be enough, somehow, but I found that

fig = Figure(size=(800, 800), backgroundcolor=:white);
ax = LScene(fig[1, 1]; show_axis=false)
ribbon!(ax, pdbname; colors=[fill(RGBA(ribboncolor, 0.2), length(opsd))], backgroundcolor=:white)
display(fig)     # this is critical

seems to work. Then I can do the scatter! plot and all the fancy view/transparency stuff and it updates nicely.

I presume this is more of a Makie nuance than anything this package needs to fix, so I'll close.

asinghvi17 commented 3 months ago

Huh, that's surprising. Probably update_state_before_display is not implemented properly for LScene - that will have to be fixed.