Animating a bar representing fractions that change with time

I want to make a single stacked bar where the differerent colors will show the fraction of some quantity. I get errors when I try to animate it:

MWE

using GLMakie
fig = Figure()
COLORS = [
    "#7143E0",
    "#0A9A84",
    "#171A2F",
    "#F30F0F",
    "#465F00",
    "#701B80",
]

fracax = Axis(fig[1,1]; width = 50, limits = (0,1,0,1))
barpos = Observable(fill(0.5, length(COLORS)))
stacks = Observable(collect(1:length(COLORS)))
heights = Observable(fill(0.1, length(COLORS)))
colors = Observable(to_color.(COLORS))
barplot!(fracax, barpos, heights; width = 1, gap = 0, stack=stacks, color = colors)

display(fig)

# New fractions coming in
heights.val = [0.1, 0.2, 0.5]
barpos.val = [0.5 for k in 1:3]
stacks.val = collect(1:3)
colors.val = [to_color(COLORS[k]) for k in 1:3]

# Errors:
notify.((heights, barpos, stacks, colors))

error:

ERROR: Metadata array needs to have same length as data.
                    Found 24 data items, and 6 metadata items

So, how can I animate this bar plot:

1 Like

Except for line #3

colors = [ ...

which I had to change to

COLORS = [ ...

your example works for me.

julia> versioninfo()
Julia Version 1.8.2
Commit 36034abf26 (2022-09-29 15:21 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: 24 Ă— AMD Ryzen 9 3900X 12-Core Processor
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, znver2)
  Threads: 4 on 24 virtual cores

(@v1.8) pkg> st
Status `C:\Users\Oli\.julia\environments\v1.8\Project.toml`
  [e9467ef8] GLMakie v0.7.0

I’ve updated the COLORS typo, however when I Run the code as is I get the error I posted:

ERROR: Metadata array needs to have same length as data.
                    Found 24 data items, and 6 metadata items
Stacktrace:
   [1] error(s::String)
     @ Base .\error.jl:35
   [2] #meta#122
     @ C:\Users\datse\.julia\packages\GeometryBasics\6JxlJ\src\metadata.jl:125 [inlined]     
   [3] pointmeta(mesh::GeometryBasics.Mesh{2, Float32, GeometryBasics.Ngon{2, Float32, 3, Point{2, Float32}}, GeometryBasics.SimpleFaceView{2, Float32, 3, GeometryBasics.OffsetInteger{-1, UInt32}, Point{2, Float32}, GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}}}; meta_data::Base.Pairs{Symbol, Vector{ColorTypes.RGBA{Float32}}, Tuple{Symbol}, NamedTuple{(:color,), Tuple{Vector{ColorTypes.RGBA{Float32}}}}})
     @ GeometryBasics C:\Users\datse\.julia\packages\GeometryBasics\6JxlJ\src\meshes.jl:253  
   [4] (::GLMakie.var"#238#241")(mesh::GeometryBasics.Mesh{2, Float32, GeometryBasics.Ngon{2, Float32, 3, Point{2, Float32}}, GeometryBasics.SimpleFaceView{2, Float32, 3, GeometryBasics.OffsetInteger{-1, UInt32}, Point{2, Float32}, GeometryBasics.NgonFace{3, GeometryBasics.OffsetInteger{-1, UInt32}}}}, color::Vector{ColorTypes.RGBA{Float32}})
     @ GLMakie C:\Users\datse\.julia\packages\GLMakie\K6iJk\src\drawing_primitives.jl:466    
   [5] #invokelatest#2
     @ .\essentials.jl:729 [inlined]
   [6] invokelatest
     @ .\essentials.jl:726 [inlined]
   [7] #15

Okay, you have a newer version of GLMakie. Updating now…

1 Like

Alright, I’ve updated Makie, and the original code works. However, it doesn’t work when I try to animate them. E.g.,

using GLMakie
fig = Figure()
COLORS = [
    "#7143E0",
    "#0A9A84",
    "#171A2F",
    "#F30F0F",
    "#465F00",
    "#701B80",
]
fracax = Axis(fig[1,1]; width = 50, limits = (0,1,0,1))
barpos = Observable(fill(0.5, length(COLORS)))
stacks = Observable(collect(1:length(COLORS)))
heights = Observable(fill(0.1, length(COLORS)))
colors = Observable(to_color.(COLORS))
barplot!(fracax, barpos, heights; width = 1, gap = 0, stack=stacks, color = colors)
display(fig)

# New fractions coming in
heights.val = [0.1, 0.2]
barpos.val = [0.5 for k in 1:2]
stacks.val = collect(1:2)
colors.val = [to_color(COLORS[k]) for k in 1:2]
# Works:
notify.((heights, barpos, stacks, colors))

# New fractions coming in
heights.val = [0.1, 0.2, 0.5]
barpos.val = [0.5 for k in 1:3]
stacks.val = collect(1:3)
colors.val = [to_color(COLORS[k]) for k in 1:3]
# Errors:
notify.((heights, barpos, stacks, colors))

the last line will error with

ERROR: BoundsError: attempt to access 2-element Vector{Int64} at index [[1, 2, 3]]
Stacktrace:
  [1] throw_boundserror(A::Vector{Int64}, I::Tuple{Vector{Int64}})
    @ Base .\abstractarray.jl:703
  [2] checkbounds
    @ .\abstractarray.jl:668 [inlined]
  [3] _getindex
    @ .\multidimensional.jl:874 [inlined]
  [4] getindex
    @ .\abstractarray.jl:1236 [inlined]
  [5] stack_grouped_from_to(i_stack::Vector{Int64}, y::Vector{Float32}, grp::NamedTuple{(:x,), Tuple{Vector{Float64}}})
    @ Makie C:\Users\datse\.julia\packages\Makie\D0WIp\src\basic_recipes\barplot.jl:119      
  [6] (::Makie.var"#calculate_bars#433"{Observable{Vector{ColorTypes.RGBA{Float32}}}, Observable{Vector{Vec{2, Float32}}}, Observable{Vector{Vec{2, Float32}}}, Observable{Vector{Tuple{String, Point{2, Float32}}}}})(xy::Vector{Point{2, Float32}}, fillto::MakieCore.Automatic, offset::Float64, width::Int64, dodge::MakieCore.Automatic, n_dodge::MakieCore.Automatic, gap::Int64, dodge_gap::Float64, stack::Vector{Int64}, dir::Symbol, bar_labels::Nothing, flip_labels_at::Float64, label_color::Symbol, color_over_background::MakieCore.Automatic, color_over_bar::MakieCore.Automatic, label_formatter::typeof(Makie.bar_label_formatter), label_offset::Int64)    @ Makie C:\Users\datse\.julia\packages\Makie\D0WIp\src\basic_recipes\barplot.jl:230      

So there is some indexing problem, or I am not using notify correctly.

I have solved my problem by realizing that I can only make the height an observable and setting the height of fractions that don’t exist to 0. So now I plot all fractions from the start, and set those that have 0 fraction to 0.

1 Like

Desynchronized observable arrays problem strikes again…

Is there a known solution to this? I thought that notify.() with broadcasting wouldn’t work, because in the end of the day the broadcasting is sequantial…

We have different ideas but haven’t had the time to try them for real yet.