Skip to content
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ The changelog here therefore lists either major changes to the overarching Dynam

The changelogs of individual sub-packages are self-contained for each package.

# v3.6

- New interactive GUI function: `interactive_2d_clicker`.

# v3.5

Expand Down
12 changes: 7 additions & 5 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "DynamicalSystems"
uuid = "61744808-ddfa-5f27-97ff-6e42cc95d634"
repo = "https://github.com/JuliaDynamics/DynamicalSystems.jl.git"
version = "3.5.0"
version = "3.6.0"

[deps]
Attractors = "f3fd9213-ca85-4dba-9dfd-7fc91308fec7"
Expand All @@ -11,13 +11,14 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
DelayEmbeddings = "5732040d-69e3-5649-938a-b6b4f237613f"
DynamicalSystemsBase = "6e36e845-645a-534a-86f2-f5d4aa5a06b4"
FractalDimensions = "4665ce21-e117-4649-aed8-08bbe5ccbead"
PeriodicOrbits = "41be5fce-5647-450b-ae37-a6739b881a1c"
PredefinedDynamicalSystems = "31e2f376-db9e-427a-b76e-a14f56347a14"
RecurrenceAnalysis = "639c3291-70d9-5ea2-8c5b-839eba1ee399"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
SignalDecomposition = "11a47235-7b84-4c7c-b885-fc3e2a9cf955"
StateSpaceSets = "40b095a5-5852-4c12-98c7-d43bf788e795"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
TimeseriesSurrogates = "c804724b-8c18-5caa-8579-6025a0767c70"
SignalDecomposition = "11a47235-7b84-4c7c-b885-fc3e2a9cf955"
PeriodicOrbits = "41be5fce-5647-450b-ae37-a6739b881a1c"

[weakdeps]
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
Expand All @@ -34,13 +35,14 @@ DelayEmbeddings = "2.7"
DynamicalSystemsBase = "3.11.0"
FractalDimensions = "1"
Makie = "≥ 0.19"
PeriodicOrbits = "0.1"
PredefinedDynamicalSystems = "1"
RecurrenceAnalysis = "2"
Reexport = "1"
SignalDecomposition = "1.2"
StateSpaceSets = "2"
Statistics = "1"
TimeseriesSurrogates = "2.1"
SignalDecomposition = "1.2"
PeriodicOrbits = "0.1"
julia = "1.9"

[extras]
Expand Down
65 changes: 56 additions & 9 deletions docs/src/visualizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,25 @@ oddata = interactive_orbitdiagram(ds, p_index, p_min, p_max, i;
ps, us = scaleod(oddata)
```

## Interactive 2D clicker

```@docs
interactive_2d_clicker
```

Example:

```julia
using GLMakie, DynamicalSystems

lorenz = Systems.lorenz()
projection = [1, 2]
complete_state = [12.0]
projected_ds = ProjectedDynamicalSystem(lorenz, projection, complete_state)

interactive_2d_clicker(projected_ds; Δt = 0.01, times = 10:100)
```

## Interactive Poincaré Surface of Section

```@raw html
Expand All @@ -254,14 +273,12 @@ interactive_poincaresos

To generate the animation at the start of this section you can run
```julia
using InteractiveDynamics, GLMakie, OrdinaryDiffEq, DynamicalSystems
diffeq = (alg = Vern9(), abstol = 1e-9, reltol = 1e-9)

using DynamicalSystems, GLMakie
hh = Systems.henonheiles()

hh = CoupledODEs(hh, (abstol = 1e-9, reltol = 1e-9))
potential(x, y) = 0.5(x^2 + y^2) + (x^2*y - (y^3)/3)
energy(x,y,px,py) = 0.5(px^2 + py^2) + potential(x,y)
const E = energy(get_state(hh)...)
const E = energy(current_state(hh)...)

function complete(y, py, x)
V = potential(x, y)
Expand All @@ -276,13 +293,18 @@ plane = (1, 0.0) # first variable crossing 0

# Coloring points using the Lyapunov exponent
function λcolor(u)
λ = lyapunovs(hh, 4000; u0 = u)[1]
u0 = complete(u..., 0.0)
λ = lyapunov(hh, 4000; u0)
λmax = 0.1
return RGBf(0, 0, clamp(λ/λmax, 0, 1))
level = clamp(λ/λmax, 0, 1)
return RGBf(level, 0, level)
end

state, scene = interactive_poincaresos(hh, plane, (2, 4), complete;
labels = ("q₂" , "p₂"), color = λcolor, diffeq...)
figure, state = interactive_poincaresos(hh, plane, (2, 4), complete; color = λcolor)

ax = content(figure[1,1][1,1])
ax.xlabel, ax.ylabel = ("q₂" , "p₂")
figure
```

## Scanning a Poincaré Surface of Section
Expand Down Expand Up @@ -318,3 +340,28 @@ j = 2 # the dimension of the plane

interactive_poincaresos_scan(trs, j; linekw = (transparency = true,))
```

## Interactive 2D dynamical system

```@docs
interactive_clicker
```

The `interactive_clicker` function can be used to spin up a GUI
for interactively exploring the state space of a 2D dynamical system.

For example, the following code show how to interactively explore a
[`ProjectedDynamicalSystem`](@ref):

```julia
using GLMakie, DynamicalSystems

# This is the 3D Lorenz model
lorenz = Systems.lorenz()

projection = [1, 2]
complete_state = [0.0]
projected_ds = ProjectedDynamicalSystem(lorenz, projection, complete_state)

interactive_clicker(projected_ds; tfinal = (10.0, 150.0))
```
4 changes: 2 additions & 2 deletions ext/DynamicalSystemsVisualizations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ include("src/dynamicalsystemobservable.jl")
include("src/interactive_trajectory.jl")
include("src/cobweb.jl")
include("src/orbitdiagram.jl")
include("src/poincareclick.jl")
include("src/brainscan.jl")
include("src/2dclicker.jl")

subscript = DynamicalSystemsVisualizations.subscript

end
end
93 changes: 93 additions & 0 deletions ext/src/2dclicker.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
function DynamicalSystems.interactive_2d_clicker(ds;
# DynamicalSystems kwargs:
times = 100:10_000,
Δt = 1,
# Makie kwargs:
color = randomcolor,
plotkwargs = ()
)

figure = Figure(size = (1000, 800))

T_slider, m_slider = _add_clicker_controls!(figure, times)
ax = figure[1, :] = Axis(figure; tellheight = true)

# Compute the initial plot
u0 = DynamicalSystems.current_state(ds)
data, = trajectory(ds, T_slider[]; Δt)
positions_node = Observable(data)
colors = (c = color(u0); [c for _ in 1:length(data)])
colors_node = Observable(colors)

if isdiscretetime(ds)
scatter!(
ax, positions_node, color = colors_node,
markersize = lift(o -> o*px, m_slider), marker = :circle, plotkwargs...
)
else
scatterlines!(
ax, positions_node, color = colors_node,
markersize = lift(o -> o*px, m_slider), marker = :circle, plotkwargs...
)
end

# Interactive clicking on the phase space:
laststate = Observable(u0)
Makie.deactivate_interaction!(ax, :rectanglezoom)
spoint = select_point(ax.scene)
on(spoint) do newstate
data, = trajectory(ds, T_slider[], newstate; Δt)
pushfirst!(vec(data), fill(NaN, dimension(data))) # ensures break for scatterlines
positions = positions_node[]; colors = colors_node[]
append!(positions, data)
c = color(newstate)
append!(colors, fill(c, length(data)))
# Update all the observables with Array as value:
positions_node[], colors_node[], laststate[] = positions, colors, newstate
end

display(figure)
return figure, laststate
end

function _add_clicker_controls!(figure, times)
sg1 = SliderGrid(figure[2, :][1, 1],
(label = "T", range = times,
format = x -> string(round(x)),
startvalue = times[1])
)
sg2 = SliderGrid(figure[2, :][1, 2],
(label = "ms", range = 10.0 .^ range(0, 2, length = 100),
format = x -> string(round(x)), startvalue = 10)
)
return sg1.sliders[1].value, sg2.sliders[1].value
end

# interactive psos is based in the 2D clicker
function DynamicalSystems.interactive_poincaresos(ds, plane, idxs, complete;
# PSOS kwargs:
direction = -1,
rootkw = (xrtol = 1e-6, atol = 1e-6),
Tmax = 1e3,
kw...
)

# Basic sanity checks on the method arguments
@assert typeof(plane) <: Tuple
@assert length(idxs) == 2
@assert eltype(idxs) == Int
@assert plane[1] ∉ idxs

i = DynamicalSystems.SVector{2, Int}(idxs)

# Construct a new `PoincareMap` structure with the given parameters
pmap = DynamicalSystems.DynamicalSystemsBase.PoincareMap(ds, plane;
direction, rootkw, Tmax)

# construct a 2d projected system compatible with the clicker
z = plane[2] # third variable comes from plane
complete_state = u -> complete(u..., z)
project = i
newds = ProjectedDynamicalSystem(pmap, project, complete_state)
return interactive_2d_clicker(newds; kw...)
end
83 changes: 0 additions & 83 deletions ext/src/poincareclick.jl

This file was deleted.

Loading
Loading