plotjuliajulia-gpu

How can you animate a time-varying function using Makie's streamplot?


I've been trying to plot a simple function:

v(x, y) = (y*t, 2*x*t)

(actual implementation: v(x::Point2{T}, t) where T = Point2{T}(one(T) * x[2] * t, 4 * x[1]))

using Makie's 2D streamplot function.

While I can plot each timestep individually, by creating an anonymous function f = x -> v(x, 5e0) (for example) and plotting f, when I try to wrap it in an Observable (using Node(f)), then I can no longer update that Observable to point to another function, so my record loop fails.

I've tried forcing the type of the Node to be more abstract (Node{Function}). Unfortunately, this abstract type seems to get lost somewhere in the internals of Makie, and is thus lost.

Is there any way I could wrap my type, so as to not run into type conflicts, but still be a Function?


Solution

  • So it turns out that there's this lovely Base method Fix2(function, argument), which basically fixes my problem (since it returns a wrapped type).

    Consider this example function:

    v(x::Point2{T}, t) where T = Point2{T}(one(T) * x[2] * t, 4 * x[1])
    

    One can create a callable function with the time set to 1.0 by:

    fixedfunc = Base.Fix2(v, 1.0)
    

    which can then be loaded into an Observable, and updated to have a new time value without issue.

    Here's the final code, for reference:

    v(x::Point2{T}, t) where T = Point2{T}(one(T) * x[2] * t, 4 * x[1])
    sf = Node(Base.Fix2(v, 0e0))
    
    title_str = Node("t = 0.00")
    
    sp = streamplot(
            sf,
            -2..2, -2..2;
            linewidth = 2,
            padding = (0, 0),
            arrow_size = 0.09,
            colormap =:magma
        )
    
    sc = title(sp, title_str)
    
    record(sc, "test.mp4", LinRange(0, 20, 5*30)) do i
      sf[] = Base.Fix2(v, i)
      title_str[] = "t = $(round(i; sigdigits = 2))"
    end
    

    video