Plot can't take Observables

I want to plot lines!() with Observables as x and y parameters.
The value of the Obervables changes obviously, because I print them to the console.
However, the lines!() plot won’t update:

using GLMakie

x =  [[[4, 3], [-4, -3], [-0.75, 2]], [[5, 4], [-7, 8], [2, 2]], [[5, 7], [-2, 1], [9, 10]]]

xs = [Float32.(getindex.(getindex.(x,i),1)[1:end]) for i in 1:3]
ys = [Float32.(getindex.(getindex.(x,i),2)[1:end]) for i in 1:3]

x_trail = Observable(getindex.(xs, [1:1]))
y_trail = Observable(getindex.(ys, [1:1]))

f = Figure()
Axis(f[1, 1])

lines!.(x_trail, y_trail, linewidth = 3)

display(f)

for i in 1:3

    x_trail[] = getindex.(xs, [1:i])
    y_trail[] = getindex.(ys, [1:i])

    println(x_trail[])
    println(y_trail[])

    sleep(1)

end

Do you have any idea, how to update the plot as x_trail and y_trail are growing, without calling lines!() each iteration? I tried, but:

lines!.(x_trail[], y_trail[], linewidth = 3)

doesn’t do anything, and

lines!.(x_trail, y_trail, linewidth = 3)

gives the error:

ERROR: MethodError: no method matching length(::Observable{Vector{Vector{Float32}}})

Your input to lines! is nested too deeply, it needs to be Observable{Vector{Float32}} not Observable{Vector{Vector{Float32}}}
How about this:

using GLMakie

x =  [[[4, 3], [-4, -3], [-0.75, 2]], [[5, 4], [-7, 8], [2, 2]], [[5, 7], [-2, 1], [9, 10]]]

idx = Observable(1)

xs = map(idx) do i
    getindex.(x[i], 1)
end

ys = map(idx) do i
    getindex.(x[i], 2)
end

f = Figure()
Axis(f[1, 1])
limits!(-10, 10, -10, 10)

lines!(xs, ys, linewidth = 3)

for i in 1:3
    idx[] = i
    display(f)
    sleep(1)
end

Or, perhaps more inline with your original data structure

pts = [Point2f.(l) for l in x]

ln = map(idx) do i
    pts[i]
end

lines!(ln, linewidth=3)

Thanks,
I get your idea, and the info that the input needs to be Observable{Vector{Float32}} is very helpful!

But my goal is to have all 3 line segments in one plot. It should look like this:

lines!.(xs, ys, linewidth = 3)

xs = [[4.0], [-4.0], [-0.75]]
ys = [[3.0], [-3.0], [2.0]]

(only points, so no line is visible)

xs = [[4.0, 5.0], [-4.0, -7.0], [-0.75, 2.0]]
ys = [[3.0, 4.0], [-3.0, 8.0], [2.0, 2.0]]
xs = [[4.0, 5.0, 5.0], [-4.0, -7.0, -2.0], [-0.75, 2.0, 9.0]]
ys = [[3.0, 4.0, 7.0], [-3.0, 8.0, 1.0], [2.0, 2.0, 10.0]]

I can produce these vectors.
The lines! command works for these vectors, as long as I plug them in manually.
Though when I write lines!.(xs[], ys[]) for example which prints me the exact same vector that I plugged in manually, the program crashes

Ah, I see. How about this:

using GLMakie

x =  [[[4, 3], [-4, -3], [-0.75, 2]], [[5, 4], [-7, 8], [2, 2]], [[5, 7], [-2, 1], [9, 10]]]
pts = [Point2f.(l) for l in x]

idx = Observable(1)
obs = [map(i->pt[1:i], idx) for pt in pts]

f = Figure()
ax = Axis(f[1,1])
limits!(-10, 10, -10, 10)

lines!.(ax, obs)

for i in 1:3
    idx[] = i
    display(f)
    sleep(1)
end

This create a vector of observables and broadcasts over that, broadcasting over an observable isn’t meaningful since an observable has one entry.

Btw, don’t call display in a loop :wink:
It’s a one off operation to create the window and OpenGL state, so it’s quite expensive.
Also, it’s more efficient to just have one call to lines or linesegments, but maybe that’s not relevant here :wink:

1 Like

Thanks!

Thanks, but it’s not quite right…

See, your program produces different images than it should:

Top: my program
Bottom: your program

As far as I can understand, your array obs gets updated each time idx is incremented. That causes obs to become an Observable itself, which again contains Observables?

However, the paramters I need for lines!.() are:

x = [[4.0, 5.0, 5.0], [-4.0, -7.0, -2.0], [-0.75, 2.0, 9.0]]
y = [[3.0, 4.0, 7.0], [-3.0, 8.0, 1.0], [2.0, 2.0, 10.0]]

But your program produces:

Observable{Vector{Point{2, Float32}}}[Observable{Vector{Point{2, Float32}}} with 1 listeners. Value:
Point{2, Float32}[[4.0, 3.0], [-4.0, -3.0], [-0.75, 2.0]], Observable{Vector{Point{2, Float32}}} with 1 listeners. Value:
Point{2, Float32}[[5.0, 4.0], [-7.0, 8.0], [2.0, 2.0]], Observable{Vector{Point{2, Float32}}} with 1 listeners. Value:   
Point{2, Float32}[[5.0, 7.0], [-2.0, 1.0], [9.0, 10.0]]]

Maybe I am asking for something that’s not possible, sorry :sweat_smile:
In this case just tell me, and I will close this question

Oh, oops, just a transpose

using GLMakie

x =  [[[4, 3], [-4, -3], [-0.75, 2]], [[5, 4], [-7, 8], [2, 2]], [[5, 7], [-2, 1], [9, 10]]]
pts = [Point2f.(l) for l in x]
pts = reduce((a,b)->cat(a, b; dims=2), pts)

idx = Observable(1)
obs = [map(i->pts[j, 1:i], idx) for j in 1:size(pts, 1)]

f = Figure()
ax = Axis(f[1,1])
limits!(-10, 10, -10, 10)

lines!.(ax, obs)

for i in 1:3
    idx[] = i
    sleep(1)
end
2 Likes

Thank you very much! That’s exactly what I was looking for.