using Observables using AbstractPlotting growth(🐇, 🥕) = 🐇 * 🥕 * (1.0 - 🐇) function orbitdiagram(growth, r1, r2, n = 500, a = zeros(1000, n); T = 1000) rs = range(r1, stop = r2, length = 1000) for (j, r) in enumerate(rs) x = 0.5 for _ in 1:T; x = growth(x, r); end for i in 1:n x = growth(x, r) @inbounds a[j, i] = x end end rs, a end r1 = slider(0:0.001:4, raw = true, camera=campixel!, start = 0.0) r2 = slider(0:0.001:4, raw = true, camera=campixel!, start = 4) n1 = 500; n2 = 1000; a = zeros(n2, n1) positions = Vector{Point2f0}(undef, n1 * n2) r1node, r2node = r1[end][:value], r2[end][:value] r1r2 = async_latest(lift(tuple, r1node, r2node)) pos = lift(r1r2) do (r1, r2,) global a rs, a = orbitdiagram(growth, r1, r2, size(a, 2), a) dim = size(a, 2) for (i, r) in enumerate(rs) positions[((i-1)*dim) + 1 : (i*dim)] .= Point2f0.(r, view(a, i, :)) end positions end p = scatter( pos, markersize = 0.006, color = (:black, 0.2), show_axis = false ) onany(pos) do pos # faster to give the boundingbox ourselves, since otherwise we'll need to # loop over pos! #AbstractPlotting.update_limits!(p) # <- would calculate bb AbstractPlotting.update_limits!(p, FRect(r1node[], 0, r2node[] - r1node[], 1)) AbstractPlotting.update!(p) end scene = hbox(p, vbox(r1, r2)) # Do not execute beyond this point! RecordEvents(scene, "output")