Skip to content

Commit 3262acb

Browse files
Create CoverImage.jl
1 parent 448fa14 commit 3262acb

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

paper/Criticality.jl/CoverImage.jl

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
using LinearAlgebra
2+
using TimeseriesTools
3+
backgroundcolor = :transparent
4+
try # * Use GLMakie if available, much faster
5+
using GLMakie # * e.g. you could have it installed in the base project
6+
@info "Using GLMakie"
7+
global backgroundcolor = :white
8+
catch
9+
using CairoMakie
10+
end
11+
using Random
12+
13+
f = Figure(; size=(400, 600), backgroundcolor)
14+
ax = Axis3(f[1, 1]; aspect=:data, backgroundcolor)
15+
μ_ext = (-50, 20)
16+
xscale = 13
17+
x_ext = (-15, 15) .* xscale
18+
radius = 5
19+
hidedecorations!(ax)
20+
21+
# * Draw a series of potential functions
22+
V(x, μ) = -μ .* (x ./ xscale) .^ 2 / 2 + (x ./ xscale) .^ 4 ./ 4
23+
colormap = cgrad(:RdYlBu) |> reverse # , [0, μ_ext[2] / (μ_ext[2] - μ_ext[1]), 1]
24+
μs = range(μ_ext..., step=0.1)
25+
xs = range(x_ext..., step=0.1)
26+
surf = V.([xs], μs)
27+
surf = stack(surf)
28+
cmat = repeat(colormap[eachindex(μs)./(length(μs))]', length(xs))
29+
surface!(ax, xs, μs, surf, color=cmat)
30+
31+
μs = range(μ_ext..., step=10)
32+
for (i, μ) in enumerate(μs)
33+
y = (V.(xs, μ))
34+
lines!(ax, xs, fill(μ, length(y)), y, color=(colormap[i./(length(μs))], 0.9), linewidth=4, joinstyle=:round, linecap=:round)
35+
end
36+
37+
# * Add lines to basin minima
38+
begin
39+
μs = range(μ_ext..., step=0.0005)
40+
x1 = similar(μs)
41+
x1 .= NaN
42+
x2 = deepcopy(x1)
43+
x3 = deepcopy(x1)
44+
x1[μs.≤0] .= 0
45+
x2[μs.>0] .= sqrt.(μs[μs.>0]) .* xscale
46+
x3[μs.>0] .= -sqrt.(μs[μs.>0]) .* xscale
47+
lines!(ax, x1, μs, V.(x1, μs), color=:black, linewidth=2, alpha=0.2, joinstyle=:round, linecap=:round)
48+
lines!(ax, x2, μs, V.(x2, μs), color=:black, linewidth=2, alpha=0.2, joinstyle=:round, linecap=:round)
49+
lines!(ax, x3, μs, V.(x3, μs), color=:black, linewidth=2, alpha=0.2, joinstyle=:round, linecap=:round)
50+
end
51+
52+
function centeroffset(μ, x, z; radius=radius, x_scale=xscale)
53+
f_x(x, μ) = -μ * x / (x_scale^2) + x^3 / (x_scale^4)
54+
f_μ(x, μ) = -0.5 * (x / x_scale)^2
55+
g = [f_x(x, μ), f_μ(x, μ), -1]
56+
g = g ./ norm(g)
57+
p = (x, μ, z) .- radius .* g
58+
return p
59+
end
60+
function centeroffset::AbstractVector, x::AbstractVector, z::AbstractVector; kwargs...)
61+
ps = centeroffset.(μ, x, z; kwargs...)
62+
x = first.(ps)
63+
μ = getindex.(ps, 2)
64+
z = last.(ps)
65+
return x, μ, z
66+
end
67+
# * Add ball and trajectory
68+
function ball!(ax, μs, ηs; color=:black, μmax=last(μs))
69+
function traj(μs, ηs; x0=0.0)
70+
if ηs isa Number
71+
ηs = fill(ηs, length(μs))
72+
end
73+
dt = 1e-4
74+
x = similar(μs)
75+
x[1] = x0
76+
for i in 2:length(μs)
77+
r = x[i-1]
78+
x[i] = r + (μs[i] * (r) / xscale^2 - (r)^3 / xscale^4) * dt + xscale * ηs[i] * sqrt(dt) * randn() # * Fix for xscale
79+
end
80+
return x
81+
end
82+
if μs isa Number
83+
μs = range(μ_ext[1], μmax, step=0.01)
84+
end
85+
xs = traj(μs, ηs)
86+
xs = .-xs
87+
zs = V.(xs, μs)
88+
xs, μs, zs = centeroffset(μs, xs, zs)
89+
trajectory = Point3f.(xs, μs, zs)
90+
91+
idxs = μs .≤ μmax
92+
xs = xs[idxs]
93+
μs = μs[idxs]
94+
zs = zs[idxs]
95+
line = Point3f.(xs, μs, zs) |> Observable
96+
lines!(ax, line, color=(color, 0.8), linewidth=2, joinstyle=:round, linecap=:round)
97+
98+
x = xs[end]
99+
μ = μs[end]
100+
z = zs[end]
101+
102+
ball = Point3f(x, μ, z) |> Observable
103+
104+
meshscatter!(ax, ball, markersize=radius, color=color, shininess=10.0f0, specular=1)
105+
return (; ball, line, trajectory)
106+
end
107+
108+
Random.seed!(83)
109+
cmap = cgrad(:inferno)
110+
nballs = 4
111+
μmax = μ_ext[2] - 2
112+
balls = [ball!(ax, 5, 1; color=cmap[1/(nballs+1)], μmax),
113+
ball!(ax, -15, 2; color=cmap[2/(nballs+1)], μmax),
114+
ball!(ax, -25, 3; color=cmap[3/(nballs+1)], μmax),
115+
ball!(ax, μ_ext[2] - 2, 14 / 4; color=cmap[4/(nballs+1)], μmax)]
116+
ax.azimuth = π / 1.7
117+
ax.elevation = π / 3
118+
limits!(ax, (-100, 100), (nothing, nothing), (-50, 100))
119+
hidespines!(ax)
120+
tightlimits!(ax)
121+
save(joinpath(@__DIR__, "CoverImage_potential.png"), f; px_per_unit=10)
122+
f
123+
124+
125+
begin # * Now an animated version
126+
map(balls) do ball # ? Reset positions
127+
ball[:ball][] = ball[:trajectory][1]
128+
ball[:line][] = ball[:trajectory][[1]]
129+
end
130+
ts = map(enumerate(balls)) do (i, b)
131+
t = eachindex(b[:trajectory])
132+
zip(t, fill(i, length(t))) |> collect
133+
end |> Iterators.flatten |> collect
134+
ts = ts[1:50:end]
135+
aztrack = range/ 2 - π / 4, π / 2 + π / 4, length=ceil(Int, length(ts) / 2))
136+
aztrack = vcat(aztrack, reverse(aztrack))
137+
aztrack = aztrack[1:length(ts)]
138+
139+
140+
record(f, joinpath(@__DIR__, "CoverImage_potential.mp4"), enumerate(ts);
141+
framerate=32) do (i, (t, b))
142+
ball = balls[b]
143+
ball[:ball][] = ball[:trajectory][t] .+ Point3f(0, 0, 0.001)
144+
ball[:line][] = ball[:trajectory][1:t] .+ Point3f(0, 0, 0.001)
145+
ax.azimuth[] = aztrack[i]
146+
end
147+
end

0 commit comments

Comments
 (0)