|
| 1 | +function DynamicalSystems.interactive_2d_clicker(ds; |
| 2 | + # DynamicalSystems kwargs: |
| 3 | + times = 100:10_000, |
| 4 | + Δt = 1, |
| 5 | + # Makie kwargs: |
| 6 | + color = randomcolor, |
| 7 | + plotkwargs = () |
| 8 | + ) |
| 9 | + |
| 10 | + figure = Figure(size = (1000, 800)) |
| 11 | + |
| 12 | + T_slider, m_slider = _add_clicker_controls!(figure, times) |
| 13 | + ax = figure[1, :] = Axis(figure; tellheight = true) |
| 14 | + |
| 15 | + # Compute the initial plot |
| 16 | + u0 = DynamicalSystems.current_state(ds) |
| 17 | + data, = trajectory(ds, T_slider[]; Δt) |
| 18 | + positions_node = Observable(data) |
| 19 | + colors = (c = color(u0); [c for _ in 1:length(data)]) |
| 20 | + colors_node = Observable(colors) |
| 21 | + |
| 22 | + if isdiscretetime(ds) |
| 23 | + scatter!( |
| 24 | + ax, positions_node, color = colors_node, |
| 25 | + markersize = lift(o -> o*px, m_slider), marker = :circle, plotkwargs... |
| 26 | + ) |
| 27 | + else |
| 28 | + scatterlines!( |
| 29 | + ax, positions_node, color = colors_node, |
| 30 | + markersize = lift(o -> o*px, m_slider), marker = :circle, plotkwargs... |
| 31 | + ) |
| 32 | + end |
| 33 | + |
| 34 | + # Interactive clicking on the phase space: |
| 35 | + laststate = Observable(u0) |
| 36 | + Makie.deactivate_interaction!(ax, :rectanglezoom) |
| 37 | + spoint = select_point(ax.scene) |
| 38 | + on(spoint) do newstate |
| 39 | + data, = trajectory(ds, T_slider[], newstate; Δt) |
| 40 | + pushfirst!(vec(data), fill(NaN, dimension(data))) # ensures break for scatterlines |
| 41 | + positions = positions_node[]; colors = colors_node[] |
| 42 | + append!(positions, data) |
| 43 | + c = color(newstate) |
| 44 | + append!(colors, fill(c, length(data))) |
| 45 | + # Update all the observables with Array as value: |
| 46 | + positions_node[], colors_node[], laststate[] = positions, colors, newstate |
| 47 | + end |
| 48 | + |
| 49 | + display(figure) |
| 50 | + return figure, laststate |
| 51 | +end |
| 52 | + |
| 53 | +function _add_clicker_controls!(figure, times) |
| 54 | + sg1 = SliderGrid(figure[2, :][1, 1], |
| 55 | + (label = "T", range = times, |
| 56 | + format = x -> string(round(x)), |
| 57 | + startvalue = times[1]) |
| 58 | + ) |
| 59 | + sg2 = SliderGrid(figure[2, :][1, 2], |
| 60 | + (label = "ms", range = 10.0 .^ range(0, 2, length = 100), |
| 61 | + format = x -> string(round(x)), startvalue = 10) |
| 62 | + ) |
| 63 | + return sg1.sliders[1].value, sg2.sliders[1].value |
| 64 | +end |
| 65 | + |
| 66 | +# interactive psos is based in the 2D clicker |
| 67 | +function DynamicalSystems.interactive_poincaresos(ds, plane, idxs, complete; |
| 68 | + # PSOS kwargs: |
| 69 | + direction = -1, |
| 70 | + rootkw = (xrtol = 1e-6, atol = 1e-6), |
| 71 | + Tmax = 1e3, |
| 72 | + kw... |
| 73 | + ) |
| 74 | + |
| 75 | + # Basic sanity checks on the method arguments |
| 76 | + @assert typeof(plane) <: Tuple |
| 77 | + @assert length(idxs) == 2 |
| 78 | + @assert eltype(idxs) == Int |
| 79 | + @assert plane[1] ∉ idxs |
| 80 | + |
| 81 | + i = DynamicalSystems.SVector{2, Int}(idxs) |
| 82 | + |
| 83 | + # Construct a new `PoincareMap` structure with the given parameters |
| 84 | + pmap = DynamicalSystems.DynamicalSystemsBase.PoincareMap(ds, plane; |
| 85 | + direction, rootkw, Tmax) |
| 86 | + |
| 87 | + # construct a 2d projected system compatible with the clicker |
| 88 | + z = plane[2] # third variable comes from plane |
| 89 | + complete_state = u -> complete(u..., z) |
| 90 | + project = i |
| 91 | + newds = ProjectedDynamicalSystem(pmap, project, complete_state) |
| 92 | + return interactive_2d_clicker(newds; kw...) |
| 93 | +end |
0 commit comments