{"spec_id":"energy-level-atomic","library":"makie","language":"julia","code":"# anyplot.ai\n# energy-level-atomic: Atomic Energy Level Diagram\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 85/100 | Created: 2026-05-30\n\nusing CairoMakie\nusing Colors\nusing Printf\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\nconst THEME     = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG   = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK       = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT  = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst INK_MUTED = THEME == \"light\" ? colorant\"#6B6A63\" : colorant\"#A8A79F\"\n\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\",  # 1 brand green\n    colorant\"#C475FD\",  # 2 lavender\n    colorant\"#4467A3\",  # 3 blue\n    colorant\"#BD8233\",  # 4 ochre\n    colorant\"#AE3030\",  # 5 matte red\n    colorant\"#2ABCCD\",  # 6 cyan\n    colorant\"#954477\",  # 7 rose\n    colorant\"#99B314\",  # 8 lime\n]\n\n# Hydrogen atom energy levels: E_n = -13.6 / n² eV\nconst energies = [-13.6 / n^2 for n in 1:4]\n\n# Nonlinear display positions proportional to n (principal quantum number).\n# This gives equal visual spacing between n = 1..4, fixing the linear-scale\n# compression where n = 2/3/4 occupied only ~22% of the axis height.\nconst n_pos   = [1.0, 2.0, 3.0, 4.0]   # display y for n = 1..4\nconst ion_pos = 5.5                      # display y for n = ∞ (ionization)\n\n# Rydberg wavelength: λ = hc / ΔE, using hc = 1240 eV·nm\nfunction wavelength_nm(n_hi::Int, n_lo::Int)\n    δe = 13.6 * (1.0 / n_lo^2 - 1.0 / n_hi^2)\n    return 1240.0 / δe\nend\nfmt_λ(n_hi, n_lo) = @sprintf(\"%d nm\", round(Int, wavelength_nm(n_hi, n_lo)))\n\n# Emission transitions (from_n, to_n)\nconst lyman_transitions   = [(2, 1), (3, 1), (4, 1)]   # UV\nconst balmer_transitions  = [(3, 2), (4, 2)]            # visible\nconst paschen_transitions = [(4, 3)]                    # infrared\n\n# Pseudo-x layout: level lines span [x_line_start, x_line_end]; labels at x_label\nconst x_line_start = 0.10\nconst x_line_end   = 0.82\nconst x_label      = 0.84\nconst lyman_xs     = [0.20, 0.30, 0.40]\nconst balmer_xs    = [0.54, 0.64]\nconst paschen_xs   = [0.74]\n\nconst lyman_color   = IMPRINT_PALETTE[1]   # brand green — Lyman UV\nconst balmer_color  = IMPRINT_PALETTE[2]   # lavender — Balmer visible\nconst paschen_color = IMPRINT_PALETTE[3]   # blue — Paschen infrared\n\n# Y-axis ticks: actual energy values at the nonlinear display positions\nconst ytick_pos    = [n_pos..., ion_pos]\nconst ytick_labels = [\"-13.6\", \"-3.4\", \"-1.51\", \"-0.85\", \"0\"]\n\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title               = \"energy-level-atomic · julia · makie · anyplot.ai\",\n    titlesize           = 20,\n    titlecolor          = INK,\n    ylabel              = \"Energy (eV)\",\n    ylabelcolor         = INK,\n    ylabelsize          = 14,\n    yticklabelcolor     = INK_SOFT,\n    yticklabelsize      = 12,\n    yticks              = (ytick_pos, ytick_labels),\n    backgroundcolor     = PAGE_BG,\n    topspinevisible     = false,\n    rightspinevisible   = false,\n    bottomspinevisible  = false,\n    leftspinecolor      = INK_SOFT,\n    ytickcolor          = INK_SOFT,\n    xticksvisible       = false,\n    xticklabelsvisible  = false,\n    xgridvisible        = false,\n    ygridvisible        = false,\n)\n\n# Energy level horizontal lines with quantum-number labels\nfor n in 1:4\n    y = n_pos[n]\n    lines!(ax, [x_line_start, x_line_end], [y, y];\n           color = INK_SOFT, linewidth = 2.2)\n    text!(ax, x_label, y;\n          text     = \"n = $n\",\n          color    = INK,\n          fontsize = 12,\n          align    = (:left, :center))\nend\n\n# Ionization continuum reference line (n = ∞, 0 eV)\nlines!(ax, [x_line_start, x_line_end], [ion_pos, ion_pos];\n       color     = INK_MUTED,\n       linewidth = 1.5,\n       linestyle = :dash)\ntext!(ax, x_label, ion_pos;\n      text     = \"n = ∞\\n(ionized)\",\n      color    = INK_MUTED,\n      fontsize = 11,\n      align    = (:left, :center))\n\n# Draw an emission arrow with a wavelength annotation at its midpoint.\n# The +0.18 upward offset on the label clears any coincident level line.\nfunction draw_arrow!(ax, xp, n_hi, n_lo, color)\n    y_from = n_pos[n_hi]\n    y_to   = n_pos[n_lo]\n    arrows!(ax, [xp], [y_from], [0.0], [y_to - y_from];\n            color = color, arrowsize = 14, linewidth = 2.4)\n    text!(ax, xp + 0.025, (y_from + y_to) / 2 + 0.18;\n          text  = fmt_λ(n_hi, n_lo),\n          color = color, fontsize = 10, align = (:left, :center))\nend\n\nfor (i, (fn, tn)) in enumerate(lyman_transitions)\n    draw_arrow!(ax, lyman_xs[i], fn, tn, lyman_color)\nend\nfor (i, (fn, tn)) in enumerate(balmer_transitions)\n    draw_arrow!(ax, balmer_xs[i], fn, tn, balmer_color)\nend\nfor (i, (fn, tn)) in enumerate(paschen_transitions)\n    draw_arrow!(ax, paschen_xs[i], fn, tn, paschen_color)\nend\n\n# Series legend below n = 1\ntext!(ax, 0.30, 0.5; text = \"Lyman (UV)\",\n      color = lyman_color, fontsize = 12, align = (:center, :center))\ntext!(ax, 0.59, 0.5; text = \"Balmer (visible)\",\n      color = balmer_color, fontsize = 12, align = (:center, :center))\ntext!(ax, 0.74, 0.5; text = \"Paschen (IR)\",\n      color = paschen_color, fontsize = 12, align = (:center, :center))\n\nxlims!(ax, 0.0, 1.06)\nylims!(ax, 0.0, 6.2)\n\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}