GLMakie: Create specify point color

I have this code that graphs a scatterplot of points based off their position in the list points, then each frame runs a function to update their position and then graphs them again. How can I specify the point’s color when creating it? Here’s the code:

using GLMakie

po = Observable(Point2f[(0, 0)])

fig, ax = scatter(po)
limits!(ax, 0, 100, 0, 100)

frames = 1:100

for p in points
    new_point = Point2f(p.pos.x, p.pos.y)
    po[] = push!(po[], new_point)
end

record(fig, "append_animation.mp4", frames, framerate = 20) do frame
    global po
    update()
    i = 1
    po[] = []
    for p in points
        new_point = Point2f(p.pos.x, p.pos.y)
        po[] = push!(po[], new_point)
    end
end

points is a list of a struct I made.
Thank you!

The example on the front page of the docs does this pretty much, use the new update! function to change positions and colors at the same time

points = Point3f[]
colors = Int[]

fig, ax, l = lines(points, color = colors,
    colormap = :inferno, transparency = true,
    axis = (; type = Axis3, protrusions = (0, 0, 0, 0),
              viewmode = :fit, limits = (-30, 30, -30, 30, 0, 50)))

record(fig, "lorenz.mp4", 1:120) do frame
    for i in 1:50
        push!(points, step!(attractor))
        push!(colors, frame)
    end
    ax.azimuth[] = 1.7pi + 0.3 * sin(2pi * frame / 120)
    Makie.update!(l, arg1 = points, color = colors) # Makie 0.24+
    l.colorrange = (0, frame)
end

I’m very confused by how this function works. Do I give it a point, and a list of RGB values and it will change that point to the color? Or does it take in all the points and all the colors? What format is the color in?
The front page code doesn’t help clear this up. It seems that the colors are a saved memory of previous frame indexes? I can’t find any documentation for this.

The update function just updates arbitrary positional and keyword arguments of plots at the same time so they stay in sync. The colors in that example are numbers resolved via a colormap but you can also pass actual colors. An overview is here Colors | Makie

You could still use the Observable strategy, if you like:

using GLMakie, Colors

points = Observable(Point2f[])
colors = Observable(RGB[])  # or Float64[] if you use a colormap
fig, ax, plt = scatter(points, color=colors)
xlims!(ax, 0, 1); ylims!(ax, 0, 1)
display(fig)

for i = 1:100
    push!(points[], rand(Point2f))
    push!(colors[], rand(RGB))
    notify(points); notify(colors)  # (Note: we need both notify s)
    sleep(0.1)
end

# wait(display(fig))  # if you're running this as a script and don't want the window to close

Using

for i = 1:100
    points[] = push!(points[], rand(Point2f))  # notifies here alreay
    colors[] = push!(colors[], rand(RGB))
    sleep(0.1)
end

also seems to work, but this is perhaps* more dangerous concerning the sync issue @jules mentions: what if the plot already gets updated when length(points[]) == length(colors[]) + 1? The manual update! in the latest versions of Makie provides a way to avoid this situation.

*Edit: I’m not sure, but the fact that scatter throws an error if you supply differently sized Vectors, but nothing happens when you only push! to one Observable, might indicate that this is sort of already taken care of by just not updating the plot when the lists go out of sync.