{"spec_id":"campbell-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# campbell-basic: Campbell Diagram\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 90/100 | Created: 2026-05-28\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# Theme tokens\nconst THEME       = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG     = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst ELEVATED_BG = THEME == \"light\" ? colorant\"#FFFDF6\" : colorant\"#242420\"\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  (1st Bending)\n    colorant\"#C475FD\",   # 2 — lavender     (2nd Bending)\n    colorant\"#4467A3\",   # 3 — blue         (1st Torsional)\n    colorant\"#BD8233\",   # 4 — ochre        (Axial)\n]\nconst CRITICAL_COLOR = colorant\"#AE3030\"   # semantic: danger / resonance\n\n# Data — turbine rotor, 0–6000 RPM operating range\nspeeds   = collect(range(0.0, 6000.0; length = 100))\nspeed_eo = [0.0, 6000.0]\n\n# Natural frequency curves (Hz) — gyroscopic variation with speed\nfreq_1b = @.  50.0 + 25.0 * (speeds / 6000.0)   # 1st Bending:   50 → 75 Hz\nfreq_2b = @. 120.0 + 25.0 * (speeds / 6000.0)   # 2nd Bending:  120 → 145 Hz\nfreq_1t = @. 200.0 - 15.0 * (speeds / 6000.0)   # 1st Torsional: 200 → 185 Hz\nfreq_ax = @. 310.0 +  8.0 * (speeds / 6000.0)   # Axial:         310 → 318 Hz\n\n# Engine order excitation lines (Hz = n × RPM / 60)\neo_freq_1x = speed_eo ./ 60.0\neo_freq_2x = 2.0 .* speed_eo ./ 60.0\neo_freq_3x = 3.0 .* speed_eo ./ 60.0\n\n# Critical speed intersections (mode curve ∩ engine order line, analytically solved)\n# Mode 1 + 1×: 4000 RPM,  66.7 Hz  |  Mode 2 + 2×: 4114 RPM, 137.1 Hz\n# Mode 1 + 2×: 1714 RPM,  57.1 Hz  |  Mode 2 + 3×: 2618 RPM, 130.9 Hz\n# Mode 1 + 3×: 1091 RPM,  54.5 Hz  |  Mode 3 + 3×: 3810 RPM, 190.5 Hz\ncrit_spd = [4000.0, 1714.3, 1090.9, 4114.3, 2618.2, 3809.5]\ncrit_frq  = [66.67,  57.14,  54.55, 137.14, 130.91, 190.48]\n\n# Figure\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title              = \"campbell-basic · julia · makie · anyplot.ai\",\n    titlesize          = 20,\n    titlecolor         = INK,\n    xlabel             = \"Rotational Speed (RPM)\",\n    ylabel             = \"Frequency (Hz)\",\n    xlabelcolor        = INK,\n    ylabelcolor        = INK,\n    xlabelsize         = 14,\n    ylabelsize         = 14,\n    xticklabelcolor    = INK_SOFT,\n    yticklabelcolor    = INK_SOFT,\n    xticklabelsize     = 12,\n    yticklabelsize     = 12,\n    backgroundcolor    = PAGE_BG,\n    topspinevisible    = false,\n    rightspinevisible  = false,\n    leftspinecolor     = INK_SOFT,\n    bottomspinecolor   = INK_SOFT,\n    xtickcolor         = INK_SOFT,\n    ytickcolor         = INK_SOFT,\n    xgridvisible       = false,\n    ygridcolor         = RGBAf(INK.r, INK.g, INK.b, 0.12),\n    yminorgridvisible  = false,\n    xminorgridvisible  = false,\n)\n\n# Engine order excitation lines (dashed reference lines from origin)\nl_eo = lines!(ax, speed_eo, eo_freq_1x; color = INK_MUTED, linestyle = :dash, linewidth = 1.5)\nlines!(ax, speed_eo, eo_freq_2x; color = INK_MUTED, linestyle = :dash, linewidth = 1.5)\nlines!(ax, speed_eo, eo_freq_3x; color = INK_MUTED, linestyle = :dash, linewidth = 1.5)\n\n# Natural frequency curves\nl_1b = lines!(ax, speeds, freq_1b; color = IMPRINT_PALETTE[1], linewidth = 2.5)\nl_2b = lines!(ax, speeds, freq_2b; color = IMPRINT_PALETTE[2], linewidth = 2.5)\nl_1t = lines!(ax, speeds, freq_1t; color = IMPRINT_PALETTE[3], linewidth = 2.5)\nl_ax = lines!(ax, speeds, freq_ax; color = IMPRINT_PALETTE[4], linewidth = 2.5)\n\n# Critical speed intersection markers\nsc_crit = scatter!(ax, crit_spd, crit_frq;\n    color       = CRITICAL_COLOR,\n    marker      = :diamond,\n    markersize  = 14,\n    strokewidth = 0,\n)\n\nxlims!(ax, 0.0, 6100.0)\nylims!(ax, 0.0, 360.0)\n\n# Engine order labels (placed mid-plot to avoid overlap with mode curves)\ntext!(ax, 2100.0, 1.0 * 2100.0 / 60.0 + 3.0; text = \"1×\", color = INK_MUTED, fontsize = 11, align = (:left, :bottom))\ntext!(ax, 2100.0, 2.0 * 2100.0 / 60.0 + 3.0; text = \"2×\", color = INK_MUTED, fontsize = 11, align = (:left, :bottom))\ntext!(ax, 2100.0, 3.0 * 2100.0 / 60.0 + 3.0; text = \"3×\", color = INK_MUTED, fontsize = 11, align = (:left, :bottom))\n\n# Legend\nLegend(\n    fig[1, 2],\n    [l_1b, l_2b, l_1t, l_ax, l_eo, sc_crit],\n    [\"1st Bending\", \"2nd Bending\", \"1st Torsional\", \"Axial\", \"Engine Orders\", \"Critical Speed\"],\n    backgroundcolor = ELEVATED_BG,\n    framecolor      = INK_SOFT,\n    framewidth      = 0.5,\n    labelcolor      = INK,\n)\n\ncolsize!(fig.layout, 1, Relative(0.82))\n\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}