Makie: Animation via Node() works with text(), but not with scatter()

I have been struggling with this for days, I don’t know if this is a bug or if I’m just missing something.

I want to animate both text and and scatters based on real-time input. I’m using Node for this (Observables). This works fine for text, but not for scatter (or lines). I hope my code is understandable:

module Animate
export doAnimate
using Makie

scene = Scene(resolution = (500,500))
attrs = String[]
posX = []
posY = []

function doAnimate(woord,fitness)

    newPosX = rand(1:200)
    newPosY = rand(1:200)

    global attrs
    global posX
    global posY

    cur = 0
    for i = 1:length(attrs)
        if(attrs[i] == woord)
            cur = i
        end
    end

    if (cur>0)
        oldPosX = posX[cur][]
        oldPosY = posY[cur][]

        xStep = (newPosX - oldPosX) / 30
        yStep = (newPosY - oldPosY) / 30

        for i = 1:30

            push!(posX[cur], oldPosX + xStep * i)
            push!(posY[cur], oldPosY + yStep * i)

            sleep(1/30)

            display(scene)
        end

    else
        push!(posX, Node(0.0))
        push!(posY, Node(0.0))

        s = scatter!([posX[end]], [posX[end]], color = :blue)
        t = text!(scene, "fgdsg", position = (posX[end], posY[end]))

        push!(attrs, woord)
    end
  return

end

end

The problem lies with the following line:

s = scatter!([posX[end]], [posX[end]], color = :blue)

It gives me the following error:

ERROR: LoadError: MethodError: no method matching push!(::Annotations{...}, ::Array{String,1}, ::Tuple{Int64,Float64}; rotation=0.0, textsize=0.059999995f0, align=(:center, :top), color=:black, font="Dejavu Sans")

Anyone understands what’s going on?

If you need display in your animation loop, you’re doing something wrong :wink:
I’m not sure I understand your code… Can you make a runnable example?
Best without module & function definition and as simple as possible!

Thank you for your quick reply. I’m afraid that stripping it down further will obfuscate the problem. However, I have made it so that it runs as is:

using Makie
using Random

scene = Scene(resolution = (500,500))
attrs = String[]
posX = []
posY = []

function doAnimate(woord,fitness)

    newPosX = rand(1:200)
    newPosY = rand(1:200)

    global attrs
    global posX
    global posY

    cur = 0
    for i = 1:length(attrs)
        if(attrs[i] == woord)
            cur = i
        end
    end

    if (cur>0)
        oldPosX = posX[cur][]
        oldPosY = posY[cur][]

        xStep = (newPosX - oldPosX) / 30
        yStep = (newPosY - oldPosY) / 30

        for i = 1:30

            push!(posX[cur], oldPosX + xStep * i)
            push!(posY[cur], oldPosY + yStep * i)

            sleep(1/30)

            display(scene)
        end

    else
        push!(posX, Node(0.0))
        push!(posY, Node(0.0))


        println(woord)

        #s = scatter!([posX[end]], [posX[end]], color = :blue)
        t = text!(scene, "fgdsg", position = (posX[end], posY[end]))

        push!(attrs, woord)
    end
  return

end



for i = 1:20

    doAnimate(string(rand(1:5)),0)

    sleep(1)
end

If you uncomment the commented line, you’ll get an error that I really don’t understand. I expect there to appear scatters moving around the scene just like the text.

There are a lot of things I’d do differently… This should work:

using Makie
using Random

scene = Scene(resolution = (500,500), limits = FRect3D(Vec3f0(0), Vec3f0(150, 150, 0)))
words = Node(String[" "]) # Empty array of string doesn't work :( ... Need a better way for this!
positions = Node(Point2f0[Point2f0(0)])

function doAnimate(word, fitness)
    newpos = rand(Point2f0) .+ 1.0 .* 100
    cur = findfirst(isequal(word), words[])
    if cur !== nothing
        oldpos = positions[][cur]
        step = (newpos .- oldpos) ./ 30
        for i = 1:30
            isopen(scene) || break
            positions[][cur] = oldpos .+ step .* i
            positions[] = positions[]
            # update!(scene)
            sleep(1/30)
        end
    else
        println(word)
        push!(positions[], Point2f0(0.0))
        push!(words[], word)
        positions[] = positions[]
        words[] = words[]
        # update!(scene)
    end
  return
end

scatter!(positions, color = :blue, markersize = 5)
annotations!(words, positions)
display(scene)
for i = 1:20
    isopen(scene) || break
    doAnimate(string(rand(1:5)), 0)
    sleep(1)
end

Wow, thanks for taking the time! I made it work with your method. I come from a sloppy JavaScript background, so getting used to Julia is quite hard.

Could you explain why in my version lines() worked while scatter() didn’t?

I didn’t look too closely, but I think you created something like: [Node(number)], which doesn’t work… You need to supply an array, or Node(array), but not Array(node)

I’m posting here since the topic name is the same.

I’d like to update a matrix using Node. Although I manage to update text, I cannot see to update the matrix. Here is a MWE, similar to the Appending data with Observables

using GLMakie
A = Node(ones(30,30))
time = Node(1)
fig, ax = heatmap(A, 
            axis = (title = @lift("t = $(round($time, digits = 1))"),)
           )

frames = 1:30

record(fig, "append_animation.mp4", frames;
        framerate = 30) do t
  time[] = t
  A[][t,t] += t
end

I’m posting here since the topic name is the same.

One should usually just open a new topic :wink:

A[][t,t] += t

You’re not updating the Node, instead you update the matrix in-place…
To make this work you need to call:

notify(A)

With GLMakie, you can also try the Sampler type, which allows to really just upload the indices you change to the gpu:

It’s not well tested, and may only work with image though… If it doesn’t work like intended with Heatmap, feel free to open an issue:

1 Like

This works perfectly:

Thank you! Haven’t tried Sampler, no GPU at the moment :smiley: