Makie.jl icon indicating copy to clipboard operation
Makie.jl copied to clipboard

Global figure time

Open Kolaru opened this issue 2 years ago • 8 comments

Description

Implement a global figure time that is incremented when the figure is displayed. This allow to have some animation shown when the GLMakie windows shows up for example and to have everything in sync across the figure.

For example with this PR

using GLMakie

begin
    fig = Figure()

    axs = [Axis(fig[i, j]) for i in 1:2, j in 1:2]
    xlims = [[-1.2, 0], [0, 1.2]]
    ylims = [[0, 1.2], [-1.2, 0]]
    wavelengths = [
        5 8
        11 14
    ]
    ω = 0.3
    θs = range(0, 2π ; length = 1000)

    for i in 1:2 
        for j in 1:2
            ax = axs[i, j]
            xlims!(ax, xlims[j])
            ylims!(ax, ylims[i])
            w = wavelengths[i, j]

            ρs = @lift [sin(w * (θ + ω * $(fig.time))) for θ in θs]
            xx = @lift $ρs .* cos.(θs)
            yy = @lift $ρs .* sin.(θs)

            lines!(ax, xx, yy)
        end
    end
    
    fig
end

record(fig, "flower.gif", 1:240) do i
    fig.time[] = i/24
end

Gives (whenever the figure is displayed) flower

Documentation will be straightforward once we are set on the semantic, but I have no idea if it would be possible to have the feature tested.

Type of change

Delete options that do not apply:

  • [x] New feature (non-breaking change which adds functionality)

Checklist

  • [ ] Added an entry in NEWS.md (for new features and breaking changes)
  • [ ] Added or changed relevant sections in the documentation
  • [ ] Added unit tests for new algorithms, conversion methods, etc.
  • [ ] Added reference image tests for new plotting functions, recipes, visual options, etc.

Kolaru avatar Oct 16 '23 14:10 Kolaru

I'd rather have something like this implemented as a (render) tick in Events. In GLMakie that could probably just react to screen.render_tick and synchronize with the renderloop, which is preferable to a clock that runs out of sync. For recording that wouldn't work on master since colorbuffer doesn't trigger render_tick, but would after #3246. With the changes you could have

timer = Observable(0)
on(_ -> timer[] = timer[] + time_per_frame, events(fig).tick)

to do the same kind of thing as your example. (Or, if we have a frame counter as part of the tick just map(tick -> f(tick.frames * frame_time), events(fig).tick). But frames are conceptually a bit awkward because render_tick triggering doesn't mean a new frame is drawn. It triggers every so often to update events, and then only draws a new frame if that is actually necessary.)

ffreyer avatar Oct 16 '23 15:10 ffreyer

I can indeed sync that on the screen render_tick update.

Since I have to add the callback when the screen is known, I add it at each display. It is not super pretty to have those dangling connections, but I don't know how I could avoid them.

Kolaru avatar Oct 17 '23 15:10 Kolaru

I outlined this somewhere else, not sure where, but I also wanted to have some sort of time accessible to the rendering logic. However, I would not want that to be time() in all cases. The reason is that in record you don't care about real time, but I still want to be able to render out videos as they would have looked if played live. So in that case, the timestamps would need to be set to the exact frame times.

jkrumbiegel avatar Oct 18 '23 11:10 jkrumbiegel

The way I see how it could be done is to have a frame observable that trigger at each frame and contain the dt to the previous frame, like game engines do.

Something like the following

struct Frame
    n::Observable  # Frame number because why not
    dt::Observable  # Time from the previous frame
    t::Observable  # Real time from fig beginning
end

Then in record or the render loop, you can ignore dt and t values and have a per-frame callback.

However I don't know how it plays with what @ffreyer said, since AFAIK Makie doesn't have a proper "frame" concept right now.

Kolaru avatar Oct 18 '23 11:10 Kolaru

No, this is pretty much what I want too. After thinking about it some more I don't think there is a meaningful difference between "I tried to render a new frame and realized it wasn't necessary so I didn't" and "I rendered a new frame" for this. It's just time/work saving, and a bit misleading if someone uses it to judge performance.

My thoughts on the implementation are that we should add a render_tick or delta_time to Events: https://github.com/MakieOrg/Makie.jl/blob/0329a70a8832f9b95c28196922c41ff44e767f7f/src/types.jl#L41-L104 This requires changes to the events interface here: https://github.com/MakieOrg/Makie.jl/blob/master/src/interaction/events.jl And for GLMakie here, where you can grab screen._rendertick: https://github.com/MakieOrg/Makie.jl/blob/master/GLMakie/src/events.jl

Regarding naming I would prefer calling the struct something else because Frame sounds like something visible to me, something related to rendering. Maybe just DeltaTime or RenderTick instead? You may also want a raw last_time in there to calculate time deltas, or maybe change screen.render_tick to hold that.

For CairoMakie this should probably only trigger when an image is produced. To be compatible with an interactive Cairo backend it should come from actual drawing, maybe here: https://github.com/MakieOrg/Makie.jl/blob/0329a70a8832f9b95c28196922c41ff44e767f7f/CairoMakie/src/infrastructure.jl#L10-L57

For WGLMakie we would want the same behavior as GLMakie, but I don't know if we have anything set up for that. Maybe the event pipeline is already synced to rendering and you'd just need to add a notification here: https://github.com/MakieOrg/Makie.jl/blob/0329a70a8832f9b95c28196922c41ff44e767f7f/WGLMakie/src/events.jl#L51-L58

ffreyer avatar Oct 18 '23 12:10 ffreyer

If it was added to events we could sync on it inside recipes and blocks right?

jkrumbiegel avatar Oct 18 '23 13:10 jkrumbiegel

Yes, everything that has access to a scene would have access to to that event.

ffreyer avatar Oct 18 '23 13:10 ffreyer

Related issue: #3163

ffreyer avatar Dec 27 '23 13:12 ffreyer

Closed via #3948

ffreyer avatar Aug 09 '24 13:08 ffreyer