Skip to content

API suggestion for avoiding synchronous update issues #798

@yha

Description

@yha

I would like to suggest a change to the API, which is somewhat of an extension of the "API wishlist" in #725.
I'm assuming the API will be modified so that basic plotting command (plot, lines, heatmap etc.) return a plot object which can then be added into a scene or axis, as in the example in #725.

Suggested change

The basic use pattern should be changed from observable parameters to plot commands (push!(ax, plot(Node(data)))), to observable plot objects over plain data (push!(ax, Node(plot(data)))).
Support for the former pattern can be dropped to make for a more consistent interface (and guard against the problem described below). Alternatively, it can be implemented easily on top of this API, with definitions such as

lines(obs::Observable)      = lift(lines, obs)
lines!(ax, obs::Observable) = push!(ax, lift(lines, obs))

The problem

The main advantage is solving problems with update synchronization, as described in this docs section http://makie.juliaplots.org/dev/interaction.html#Problems-With-Synchronous-Updates.

For example this:

# (1)
i = Observable(5)
x = @lift rand($i)
y = @lift rand($i)
c = @lift rand($i)
push!(ax, lines(x, y, color=c))
# or the current:
# lines!(ax, x, y, color=c)

would become

# (2)
i = Observable(5)
lns = lift(i) do i
    x = rand(i)
    y = rand(i)
    c = rand(i)
    lines(x, y, color=c)
end
push!(ax, lns)

Example (1) doesn't actually work because of the synchronous update problem: updating i updates x first, which updates the lines plot and throws an error, since x and y are of different length. Example (2) would achieve an atomic update of the plot, so there is no such issue.

A smaller advantage is that listening on the plot can make sense sometimes. Arguably on(lns) do lns; update_limits!(lns) end is better than on(i) do _; update_limits!(lns) end as it expresses the intent more precisely, and therefore more robust to changes in how lns is constructed.

Current workarounds

A possible workaround in the current design is doing lines(Point2.(x,y)), but this trick doesn't work for the color kwarg. It also wouldn't work for heatmap(x,y,z).
I was able to get synchronous updates for my own code by defining ad-hoc intermediate object and recipes for each such plot. (You can do heatmap(::Observable{MyCustomHeatmapDataStruct}) with a type recipe. Dealing with kwargs requires a full recipe as far as I can see). Changing the API would solve the problem more generally for all plot types, and for both their positional and keyword arguments.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ObservablesenhancementFeature requests and enhancementsplanningFor discussion and planning development

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions