using Pkg for p in ["GLMakie", "FFMPEG"] try Base.require(Symbol(p)) catch Pkg.add(p) end end using GLMakie using FFMPEG ############################################################ # SETTINGS ############################################################ const FPS = 60 const DURATION = 20 const FRAMES = FPS * DURATION const OUTFILE = "4drops_electrons_forces.gif" # normalized constants for visualization const G = 1.0 const K = 1.0 ############################################################ # CIRCLE POINTS ############################################################ function circlepts(r, cx, cy; n=80) Point2f[ Point2f( cx + r*cos(t), cy + r*sin(t) ) for t in range(0, 2π, length=n) ] end ############################################################ # MAIN ############################################################ function main() fig = Figure(resolution=(1850, 980), fontsize=18) ######################################################## # LEFT PANEL ######################################################## ax = Axis( fig[1:2,1], title = "2 Water Drops (gravity) + 2 Electrons (Coulomb)", aspect = DataAspect() ) limits!(ax, -15, 15, -6, 6) ######################################################## # TOP RIGHT = FORCES ######################################################## axF = Axis( fig[1,2], title = "F₁(r)=Gravity vs F₂(r)=Coulomb" ) ######################################################## # BOTTOM RIGHT = DISTANCE r ######################################################## axR = Axis( fig[2,2], title = "Realtime Separation r(t)" ) ######################################################## # OBSERVABLE DATA ######################################################## t = Observable(Float64[]) F1 = Observable(Float64[]) F2 = Observable(Float64[]) r1 = Observable(Float64[]) r2 = Observable(Float64[]) ######################################################## # FORCE GRAPH ######################################################## lines!(axF, t, F1, linewidth=4, label="Water Drops Gravity F₁" ) lines!(axF, t, F2, linewidth=4, label="Electrons Coulomb F₂" ) axislegend(axF, position=:rt) ######################################################## # DISTANCE GRAPH ######################################################## lines!(axR, t, r1, linewidth=3, linestyle=:dash, label="Drops r" ) lines!(axR, t, r2, linewidth=3, linestyle=:dot, label="Electrons r" ) axislegend(axR, position=:rt) ######################################################## # OBJECTS ######################################################## # water drops drop1 = Observable(circlepts(1.5, -10, 0)) drop2 = Observable(circlepts(1.5, -5, 0)) # electrons el1 = Observable(circlepts(0.45, 6.0, 0)) el2 = Observable(circlepts(0.45, 8.5, 0)) poly!(ax, drop1) poly!(ax, drop2) poly!(ax, el1) poly!(ax, el2) display(fig) ######################################################## # RENDER ######################################################## println("Rendering MP4...") record(fig, OUTFILE, 1:FRAMES; framerate=FPS) do i tt = i / FPS #################################################### # DISTANCES r(t) #################################################### # gravity pair slowly attracts rg = 2.5 - 3.5*(1 - exp(-0.25*tt)) # electron pair repels rc = 2.5 + 3.5*(1 - exp(-0.25*tt)) rg = max(rg, 0.8) rc = max(rc, 0.8) #################################################### # MASSES / CHARGES #################################################### m1 = 1.0 m2 = 1.0 q1 = 1.0 q2 = 1.0 #################################################### # FORCES #################################################### FG = G * m1*m2 / rg^2 FC = K * q1*q2 / rc^2 #################################################### # CHANGE RADII BY FORCE #################################################### # water drops deform slightly with attraction Rg = 1.2 + 0.9*FG # electrons visual radius pulse Re = 0.35 + 0.6*FC #################################################### # POSITIONS #################################################### cx1 = -7.5 - rg/2 cx2 = -7.5 + rg/2 ex1 = 7.2 - rc/2 ex2 = 7.2 + rc/2 #################################################### # UPDATE SHAPES #################################################### drop1[] = circlepts(Rg, cx1, 0) drop2[] = circlepts(Rg, cx2, 0) el1[] = circlepts(Re, ex1, 0) el2[] = circlepts(Re, ex2, 0) #################################################### # PUSH DATA #################################################### push!(t[], tt) push!(F1[], FG) push!(F2[], FC) push!(r1[], rg) push!(r2[], rc) #################################################### # REFRESH #################################################### notify(t) notify(F1) notify(F2) notify(r1) notify(r2) autolimits!(axF) autolimits!(axR) end println("Saved => $OUTFILE") println("Press ENTER to close") readline() end main()