[GLMakie] Objects are still displayed after `delete!()`

I am currently working on the next release of an interactive 3D display for neutrino events and the main new feature is the ability to interact with the scene from the Julia REPL. Modifying existing objects (colouring and size of the mesh scatter elements which represent PMT hits) works really nicely and in real-time (for tens of thousands of objects) but I have difficulties with deleting objects from a scene.

If I call delete!(scene, the_object), it successfully removes it – as far as I can tell. I think it does because calling it again complains that it’s “not in scene”. So far so good. The problem is that it’s still displayed :wink:

I do not have a MWE yet but I assume that I am not using the API correctly.

I currently have a simple Dict in the main RBA struct to store the references to all the plots, so that I can manipulate them when the main loop is running.

To quickly summarise how my scene is created, filled and modified:

This is the main struct to hold things together:

@kwdef mutable struct RBA
    scene::Scene = Scene(backgroundcolor=RGBf(0.9))
    cam::Makie.Camera3D = cam3d!(scene, rotation_center = :lookat)
    # ...
    _plots::Dict{String, Any} = Dict()
end

And I fill it with a bunch of stuff (most of them are Lines and MeshScatters) like this:

    rba._plots["Detector"] = meshscatter!(
        scene,
        [m.pos for m ∈ basemodules],
        marker=Rect3f(Vec3f(-0.5), Vec3f(0.5)),
        markersize=5,
        color=:black
    )

The references to the plot objects are stored In the rba._plots dictionary.

And the main loop which is essentially (simplified):

function start_eventloop(rba)
    screen = display(GLMakie.Screen(start_renderloop=false, focus_on_show=true, title="RainbowAlga"), rba.scene)
    glw = screen.glscreen
    GLMakie.GLFW.SetWindowPos(glw, displayparams.pos...)
    GLMakie.GLFW.SetWindowSize(glw, displayparams.size...)

    while isopen(screen)
        frame_start = time()

        rotation_enabled() && rotate_cam!(rba.scene, Vec3f(0, 0.001, 0))

        GLMakie.pollevents(screen)
        GLMakie.render_frame(screen)
        GLMakie.GLFW.SwapBuffers(GLMakie.to_native(screen))

        yield()

        Δt = time() - frame_start
        sleep_time = 1.0/simparams.fps - Δt
        if sleep_time > 0
            sleep(sleep_time)
        end
    end

    GLMakie.destroy!(screen)
end

So now if I want to delete the e.g. the detector base modules, which are the small black boxes at the bottom of each string, I call (here you can see that the second call suggests that it worked):

julia> delete!(rba.scene, rba._plots["Detector"])

julia> delete!(rba.scene, rba._plots["Detector"])
ERROR: MakieCore.MeshScatter{Tuple{Vector{GeometryBasics.Point{3, Float64}}}} not in scene!
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] delete!(scene::Makie.Scene, plot::MakieCore.MeshScatter{Tuple{Vector{GeometryBasics.Point{3, Float64}}}})
   @ Makie ~/.julia/packages/Makie/We6MY/src/scenes.jl:489
 [3] top-level scope
   @ REPL[4]:1

julia> 

however, the elements are still displayed.

No matter what I delete from the scene, they are still displayed. Any idea?

I know you said you don’t have an MWE, but this really needs an MWE to debug it. Delete is tested and should work like this, so if it isn’t I can only assume that you’re doing something weird and creating an MWE that reproduces the behaviour will be the main work to figure out what’s going wrong.

1 Like

OK, I am almost there!

Alright, I am glad I created the MWE because it’s a “working” example in the sense that I cannot reproduce the behaviour, so indeed I am doing something wrong :wink:

I am now trying to figure out what’s happening with my code.

For the sake of completeness (maybe someone finds this useful), here is a MWE which works correctly:

using Makie
using GLMakie
using GLFW


@kwdef mutable struct App
    scene::Scene = Scene(backgroundcolor=RGBf(0.9))
    cam::Makie.Camera3D = cam3d!(scene, rotation_center = :lookat)
    plots::Vector{Any} = []
    fps::Int = 60
end


"""
Initialise the app, enter the event loop and return the app instance to
the REPL.
"""
function initialize()
    app = App()
    #center!(app.scene)
    update_cam!(app.scene, app.cam, Vec3f(5), Vec3f(0, 0, 0), Vec3f(0, 0, 1))

    n = 10

    a_mesh = meshscatter!(
        app.scene,
        rand(Vec3f, n),
        markersize = [rand()/10 for _ in 1:n]
    )
    push!(app.plots, a_mesh)

    a_line = lines!(app.scene, [rand(Vec3f) for _ in 1:n]; color=:red, linewidth=4)
    push!(app.plots, a_line)

    Threads.@spawn :interactive eventloop(app)
    app
end


function eventloop(app::App)
    screen = display(GLMakie.Screen(start_renderloop=false, focus_on_show=true, title="RainbowAlga"), app.scene)
    glw = screen.glscreen
    GLMakie.GLFW.SetWindowPos(glw, 100, 100)
    GLMakie.GLFW.SetWindowSize(glw, 400, 400)

    scene = app.scene

    while isopen(screen)
        frame_start = time()
        rotate_cam!(scene, Vec3f(0, 0.005, 0))

        GLMakie.pollevents(screen)
        GLMakie.render_frame(screen)
        GLMakie.GLFW.SwapBuffers(GLMakie.to_native(screen))

        yield()

        sleep_time = 1.0/app.fps - (time() - frame_start)
        sleep_time > 0 && sleep(sleep_time)
    end

    GLMakie.destroy!(screen)
end

And here is an example session:

julia> include("mwe.jl")
eventloop (generic function with 1 method)

julia> app = initialize();  # opens the window, returns to the REPL

julia> delete!(app.scene, app.plots[1])

julia> delete!(app.scene, app.plots[2])

Found the bug: some elements (coincidentally those which I tried to remove) where accidentally added twice :see_no_evil:

1 Like