{"spec_id":"spc-xbar-r","library":"makie","language":"julia","code":"# anyplot.ai\n# spc-xbar-r: Statistical Process Control Chart (X-bar/R)\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 88/100 | Created: 2026-06-20\n\nusing CairoMakie\nusing Colors\nusing Random\nusing Statistics\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\"\n\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\",   # 1 — brand green (first series)\n    colorant\"#C475FD\",   # 2 — lavender\n    colorant\"#4467A3\",   # 3 — blue\n    colorant\"#BD8233\",   # 4 — ochre\n    colorant\"#AE3030\",   # 5 — matte red (control limits / out-of-control)\n    colorant\"#2ABCCD\",   # 6 — cyan\n    colorant\"#954477\",   # 7 — rose\n    colorant\"#99B314\",   # 8 — lime\n]\nconst ANYPLOT_AMBER = colorant\"#DDCC77\"  # warning / caution semantic anchor\n\n# SPC control chart constants for subgroup size n = 5\nconst n_sub = 5\nconst A2    = 0.577    # X-bar ±3σ factor\nconst D4    = 2.115    # R-chart UCL factor\nconst d2    = 2.326    # unbiasing constant (R-bar → σ estimate)\n\n# Data: shaft diameter measurements (mm) — CNC machining, 30 subgroups × 5 parts\nconst n_samples = 30\nmeasurements = 25.00 .+ 0.04 .* randn(n_samples, n_sub)\n\n# Inject known out-of-control signals at fixed positions\nmeasurements[8, :]  = [25.22, 25.24, 25.21, 25.23, 25.25]    # X-bar above UCL\nmeasurements[19, :] = [24.74, 24.76, 24.73, 24.78, 24.75]    # X-bar below LCL\nmeasurements[25, :] = [24.92, 25.28, 24.90, 25.24, 24.94]    # Wide range → R above UCL\n\n# Subgroup statistics\nsample_means  = [mean(measurements[i, :]) for i in 1:n_samples]\nsample_ranges = [maximum(measurements[i, :]) - minimum(measurements[i, :]) for i in 1:n_samples]\n\nx_bar_bar = mean(sample_means)\nr_bar     = mean(sample_ranges)\n\n# X-bar control limits (±3σ) and warning limits (±2σ)\nucl_xbar   = x_bar_bar + A2 * r_bar\nlcl_xbar   = x_bar_bar - A2 * r_bar\nsigma_xbar = r_bar / (d2 * sqrt(n_sub))\nuwl_xbar   = x_bar_bar + 2.0 * sigma_xbar\nlwl_xbar   = x_bar_bar - 2.0 * sigma_xbar\n\n# R chart control limits (LCL=0 when D3=0 for n<7; only show UCL and warning)\nucl_r = D4 * r_bar\nuwl_r = r_bar + (2.0 / 3.0) * (ucl_r - r_bar)\n\n# Out-of-control point indices\nooc_xbar     = findall(m -> m > ucl_xbar || m < lcl_xbar, sample_means)\nin_ctrl_xbar = findall(m -> lcl_xbar <= m <= ucl_xbar, sample_means)\nooc_r        = findall(r -> r > ucl_r, sample_ranges)\nin_ctrl_r    = findall(r -> r <= ucl_r, sample_ranges)\n\nxs      = collect(1:n_samples)\nx_right = Float64(n_samples) + 0.7    # x-position for right-edge limit labels\n\n# Title (length scaling: only shrink if > 67 chars)\ntitle_str = \"Shaft Diameter QC · spc-xbar-r · julia · makie · anyplot.ai\"\nn_chars   = length(title_str)\nratio     = n_chars > 67 ? 67.0 / n_chars : 1.0\ntitle_fs  = max(14, round(Int, 20.0 * ratio))\n\ngrid_c = RGBAf(INK.r, INK.g, INK.b, 0.12f0)\n\n# Figure — landscape 3200 × 1800 (resolution × px_per_unit = 2)\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nLabel(fig[0, 1],\n    title_str;\n    fontsize  = title_fs,\n    color     = INK,\n    font      = :bold,\n    halign    = :center,\n    tellwidth = false,\n)\n\n# X-bar Axis (top panel — x tick labels hidden; shared via linkxaxes!)\nax_xbar = Axis(fig[1, 1];\n    backgroundcolor    = PAGE_BG,\n    ylabel             = \"Sample Mean (mm)\",\n    ylabelcolor        = INK,\n    ylabelsize         = 14,\n    xticklabelsvisible = false,\n    xticksize          = 0,\n    yticklabelcolor    = INK_SOFT,\n    yticklabelsize     = 12,\n    topspinevisible    = false,\n    rightspinevisible  = false,\n    leftspinecolor     = INK_SOFT,\n    bottomspinecolor   = INK_SOFT,\n    xgridcolor         = grid_c,\n    ygridcolor         = grid_c,\n    xminorgridvisible  = false,\n    yminorgridvisible  = false,\n    xticks             = collect(5:5:n_samples),\n)\n\n# R Axis (bottom panel — carries x-axis labels)\nax_r = Axis(fig[2, 1];\n    backgroundcolor   = PAGE_BG,\n    xlabel            = \"Sample Number\",\n    xlabelcolor       = INK,\n    xlabelsize        = 14,\n    ylabel            = \"Sample Range (mm)\",\n    ylabelcolor       = INK,\n    ylabelsize        = 14,\n    xticklabelcolor   = INK_SOFT,\n    yticklabelcolor   = INK_SOFT,\n    xticklabelsize    = 12,\n    yticklabelsize    = 12,\n    topspinevisible   = false,\n    rightspinevisible = false,\n    leftspinecolor    = INK_SOFT,\n    bottomspinecolor  = INK_SOFT,\n    xgridcolor        = grid_c,\n    ygridcolor        = grid_c,\n    xminorgridvisible = false,\n    yminorgridvisible = false,\n    xticks            = collect(5:5:n_samples),\n)\n\nlinkxaxes!(ax_xbar, ax_r)\nxlims!(ax_xbar, 0.5, n_samples + 4.0)\nxlims!(ax_r, 0.5, n_samples + 4.0)\n\n# --- X-bar chart ---\n# Warning limits (±2σ) — amber dashed\nlines!(ax_xbar, [1.0, Float64(n_samples)], [uwl_xbar, uwl_xbar];\n    color=ANYPLOT_AMBER, linewidth=1.4, linestyle=:dash)\nlines!(ax_xbar, [1.0, Float64(n_samples)], [lwl_xbar, lwl_xbar];\n    color=ANYPLOT_AMBER, linewidth=1.4, linestyle=:dash)\n# Control limits (±3σ) — red dashed\nlines!(ax_xbar, [1.0, Float64(n_samples)], [ucl_xbar, ucl_xbar];\n    color=IMPRINT_PALETTE[5], linewidth=2.2, linestyle=:dash)\nlines!(ax_xbar, [1.0, Float64(n_samples)], [lcl_xbar, lcl_xbar];\n    color=IMPRINT_PALETTE[5], linewidth=2.2, linestyle=:dash)\n# Center line (X̄-bar) — solid ink\nlines!(ax_xbar, [1.0, Float64(n_samples)], [x_bar_bar, x_bar_bar];\n    color=INK, linewidth=1.8)\n# Data line and markers\nlines!(ax_xbar, xs, sample_means; color=IMPRINT_PALETTE[1], linewidth=2.2)\nscatter!(ax_xbar, in_ctrl_xbar, sample_means[in_ctrl_xbar];\n    color=IMPRINT_PALETTE[1], markersize=10, strokewidth=0)\nif !isempty(ooc_xbar)\n    scatter!(ax_xbar, ooc_xbar, sample_means[ooc_xbar];\n        color=IMPRINT_PALETTE[5], markersize=14, marker=:diamond,\n        strokewidth=1, strokecolor=PAGE_BG)\nend\n# Right-edge limit labels\ntext!(ax_xbar, x_right, ucl_xbar;\n    text=\"UCL\", fontsize=11, color=IMPRINT_PALETTE[5], align=(:left, :center))\ntext!(ax_xbar, x_right, x_bar_bar;\n    text=\"X̄\", fontsize=11, color=INK, align=(:left, :center))\ntext!(ax_xbar, x_right, lcl_xbar;\n    text=\"LCL\", fontsize=11, color=IMPRINT_PALETTE[5], align=(:left, :center))\n\n# --- R chart ---\n# Warning limit — amber dashed\nlines!(ax_r, [1.0, Float64(n_samples)], [uwl_r, uwl_r];\n    color=ANYPLOT_AMBER, linewidth=1.4, linestyle=:dash)\n# UCL — red dashed\nlines!(ax_r, [1.0, Float64(n_samples)], [ucl_r, ucl_r];\n    color=IMPRINT_PALETTE[5], linewidth=2.2, linestyle=:dash)\n# Center line (R̄) — solid ink\nlines!(ax_r, [1.0, Float64(n_samples)], [r_bar, r_bar];\n    color=INK, linewidth=1.8)\n# Data line and markers\nlines!(ax_r, xs, sample_ranges; color=IMPRINT_PALETTE[1], linewidth=2.2)\nscatter!(ax_r, in_ctrl_r, sample_ranges[in_ctrl_r];\n    color=IMPRINT_PALETTE[1], markersize=10, strokewidth=0)\nif !isempty(ooc_r)\n    scatter!(ax_r, ooc_r, sample_ranges[ooc_r];\n        color=IMPRINT_PALETTE[5], markersize=14, marker=:diamond,\n        strokewidth=1, strokecolor=PAGE_BG)\nend\n# Right-edge limit labels\ntext!(ax_r, x_right, ucl_r;\n    text=\"UCL\", fontsize=11, color=IMPRINT_PALETTE[5], align=(:left, :center))\ntext!(ax_r, x_right, r_bar;\n    text=\"R̄\", fontsize=11, color=INK, align=(:left, :center))\n\nrowgap!(fig.layout, 1, 16)\n\nsave(\"plot-$(THEME).png\", fig; px_per_unit=2)\n"}