How can I trigger Node(array) after changing array element in Makie?

The root issue is that when I have data = Node([1 2 3 4 5 6]), I cannot force data to update elsewhere by simply changing the value of one element (data[][1] = 0). As far as I can tell, I have to assign data[] = [0 2 3 4 5 6], or data[] = data[] after changing just the element etc, which wastes resources and becomes prohibitive for big arrays.

Is there something w/ the equivalent of bash shell’s touch filename that will trigger the equivalent of data[] = data[] without doing the assignment?

using Makie
myn=Node(fill(0, 5))
println(to_value(myn))   #--------> [0,0,0,0,0]
on(println, myn)
myn[] = fill(1, 5)   #             prints [1,1,1,1,1]
myn[][2:3] = [2,3]  #            nothing prints 
println(to_value(myn)) #----------> [1,2,3,1,1]

And yes I can create an array of nodes anodes = Node.(fill(0,5) and then use onany(println, anodes...) to trigger, but this becomes slow very quickly with size.

In particular, I want an animated image to live update display(f) as the data changes in the spirit of the following code…

using Makie
data = fill(0, (100,100)
f = Figure(resolution = (100,100))
Makie.Axis(f[1, 1], aspect = DataAspect(), xticks = [], yticks=[])
display(f)
image!(data)

for i in [1:10, 11:20, 51:60]
    data[i] = fill(1,10)
    sleep(.001)
end

I’ve tried many things that don’t quite work… One thing that kind of “worked” was just throwing image!(data) inside the loop; but this creates memory problems b/c the memory from previous images is not released.

How can I do this without reassigning the entire data array?

many many thanks - Makie looks like it can do great things, but i don’t know how to/if i can get it to do the simple things I want :confused:

@sdanisch ?

data[] = data[] doesn’t actually copy anything, it is equivalent to doing notify(data), so it just triggers listeners. Of coure you are right that resources are wasted by doing the computations that are triggered for the whole array and not just the changed values, but we currently don’t have the pipeline in place to distinguish between such cases. For example, if some following step scaled the image by the mean, that would still have to be run on the whole array, even if just one number changes.

But for image sizes of (100, 100) it should be quite fast to update these images, I’ve used fluent updates of images that were much larger than that. Maybe your sleep interval is too low as well?

If you do image! over and over again, you just layer these on top of each other, so of course the memory is not released. But that has to be strictly slower than just changing the whole array inside the node anyway, because it does that step plus all the setup steps for new plot objects.

thanks jules.
A single data[]=data[] (or notify) call takes 200ms w/ a 2k by 2k array on my machine. so not ideal for smooth real-time processing. is my machine just slow?

I see the point about having things in sync, but i’d also gladly use that option with the caveat :slight_smile: … changing subarrays/elements with or without notification as needed. e.g. using something like ’ view_obs() ’ vs view()

for the former case I’m looking into using Sampler type based on advice from a different post, but haven’t succeeded in propagation yet (maybe it’s impossible). I’ll post here if I can get it to work.
thanks again.

So Sampler type works well and is available when using GLMakie. for me functionally accessing the Sampler object works like calling view that propagates the updated portion to the image.

Regarding the data[]=data[] call, using it provided a speedup proportional to the size of the Sampler array to the data array. So in my case that was a speed up of at least 100x

https://github.com/JuliaPlots/GLMakie.jl/blob/master/test/glmakie_tests.jl#L34

https://github.com/SimonDanisch/ShaderAbstractions.jl/blob/master/src/types.jl#L67

it works perfectly with Matrix{RGBAf0} in Makie. I was originally trying to use it with Float32 array. @sdanisch was awesome enough to show how Float32 could work with a conversion overload. this example uses heatmap

using GLMakie 
using GLMakie.ShaderAbstractions: Sampler

# Missing overload to have Sampler work
AbstractPlotting.el32convert(x::AbstractArray{Float32}) = x

imag = rand(Float32, (2000,2000))
data = Sampler(imag, minfilter=:nearest) # or linear for interpolation
n = 25
f = Figure(resolution = size(imag));
ax = Makie.Axis(f[1, 1], aspect = DataAspect()); 
hidedecorations!(ax)
tightlimits!(ax)
heatmap!(ax, data)
display(f)

@time for i in 1:n
    data[1:400, 1:400] = fill(0f0, 400, 400)
    yield()
end