Skip to content

Falling bodies in the Solar System.

julia
using GLMakie, FileIO
GLMakie.activate!()
# See following links for references:
# https://twitter.com/AnsonBiggs/status/1444823816031510529/photo/1
# https://github.com/JuliaAnimators/Javis.jl/blob/master/examples/gravities.jl
diameters = Dict(
    "Mercury" => 4879,
    "Venus" => 12104,
    "Earth" => 12756,
    "Moon" => 3475,
    "Mars" => 6792,
    "Jupiter" => 142984,
    "Saturn" => 120536,
    "Uranus" => 51118,
    "Neptune" => 49528,
    "Pluto" => 2370)
struct Body
    position::Int
    name::String
    gravity::Real
    color::String
    radius::Real
end
Body(pos, name, grav, color) = Body(pos, name, grav, color, log(diameters[name]) * 2)
bodies = [
    Body(1, "Mercury", 3.7, "snow4")
    Body(2, "Venus", 8.9, "navajowhite")
    Body(3, "Earth", 9.8, "lightskyblue")
    Body(4, "Moon", 1.6, "gainsboro")
    Body(5, "Mars", 3.7, "orangered")
    Body(6, "Jupiter", 23.1, "olive")
    Body(7, "Saturn", 9, "burlywood")
    Body(8, "Uranus", 8.7, "cyan3")
    Body(9, "Neptune", 11, "dodgerblue")
    Body(10, "Pluto", 0.7, "rosybrown4")
]
framerate = 24
height = 1000 # meters

frames = let
    # Get planet with slowest acceleration
    slowest = [p.gravity for p in bodies] |> minimum
    # Kinematic equation to determine seconds to fall from height
    time = 2 * height / slowest |> sqrt
    # Calculate total frames and add a few seconds at the end to display final result
    ceil(Int, time * framerate) + framerate * 5
end

fontsize = 12
f = Observable(1)
with_theme(theme_dark()) do
    fig = Figure(; size=(900, 500))
    axs = [Axis(fig[i, j]) for j in 1:11, i in 1:3]
    for (idx, body) in enumerate(bodies)
        # Kinematic equations to calculate how many frames each planet will be active
        t_final = 2 * height / body.gravity |> sqrt
        v_final = body.gravity * t_final
        frame_final = ceil(Int, t_final * framerate)
        # Calculate time of current frame for kinematic math
        time = @lift($f / framerate)
        # Calculate planets current position
        body_y = @lift(height - 0.5 * body.gravity * $time^2)
        sec = @lift($body_y <= 0 ? round(t_final, digits = 1) : round($time, digits = 1))
        velocity = @lift($body_y <= 0 ? round(v_final, digits = 1) : round(body.gravity * $time, digits = 1))
        body_y = @lift($body_y <= 0 ? 0 : $body_y)

        # Set planet's position
        scatter!(axs[idx+1, 2], @lift(Point2f(1, $body_y)), color = body.color, markersize = 25)
        text!(axs[idx+1, 1], @lift(string($sec, "s")),
            position = Point2f(1, 1),
            fontsize = fontsize, color = @lift($body_y <= 0 ? "orange" : "grey90"),
            align = (:left, :center))
        text!(axs[idx+1, 1],
            @lift(string($velocity, "m/s")),
            position = Point2f(1, 0), fontsize = fontsize,
            color = @lift($body_y <= 0 ? "orange" : "grey90"),
            align = (:left, :center))

        ylims!(axs[idx+1, 2], -30, height + 30)

        scatter!(axs[idx+1,3], [Point2f(0,0)]; color = @lift($body_y <= 0 ? body.color : "grey9"),
            strokecolor = :white, markersize = 85,
            strokewidth = @lift($body_y <= 0 ? 2 : 0.25))
        # Set text that is static during entire planet translation
        text!(axs[idx+1, 1], body.name, position = Point2f(1, 3),
            fontsize = fontsize, align = (:left, :center))
        text!(axs[idx+1, 1], string(body.gravity, "m/s²"),
            position = Point2f(1, 2), fontsize = fontsize,
            align = (:left, :center))
        limits!(axs[idx+1, 1], 0, 3, -0.5, 3.5)
    end
    ylims!(axs[1, 2], -15, height + 15)
    text!(axs[1, 2], "\n1 Km", position = Point2f(0, 1000),
        color=:orangered, fontsize = fontsize, align = (:left, :center))
    text!(axs[1, 2], "0 Km", position = Point2f(0, 0),
        color=:dodgerblue, fontsize = fontsize, align = (:left, :center))

    text!(axs[1, 1], "Body:", position = Point2f(0, 3),
        fontsize = fontsize, align = (:left, :center))
    text!(axs[1, 1], "Acceleration:", position = Point2f(0, 2),
        fontsize = fontsize, align = (:left, :center))
    text!(axs[1, 1], "Time:", position = Point2f(0, 1),
        fontsize = fontsize, align = (:left, :center))
    text!(axs[1, 1], "Velocity:", position = Point2f(0, 0),
        fontsize = fontsize, align = (:left, :center))
    limits!(axs[1, 1], 0, 3, -0.5, 3.5)

    [hidedecorations!(axs[idx, k]) for idx in 1:11, k in 1:3]
    [hidespines!(axs[idx, k]) for idx in 1:11, k in 1:3]

    Label(fig[0, :], "Falling Bodies in the Solar System\n ", font=:bold,
        tellwidth = false, color = :grey70, fontsize=16)
    Label(fig[0, :],
        rich("Visualization by ",
        rich("Lazaro Alonso\n ", color=:dodgerblue)),
        justification=:left,
        halign=1.0
        )
    rowgap!(fig.layout, 0)
    colgap!(fig.layout, 0)
    rowsize!(fig.layout, 1, Auto(0.15))
    rowsize!(fig.layout, 3, Auto(0.2))
    fig

    record(fig, "gravities.mp4", framerate = 2 * framerate) do io
        for i in 1:frames
            f[] = i
            recordframe!(io)  # record a new frame
        end
    end
end
"gravities.mp4"