{"spec_id":"scatter-shot-chart","library":"makie","language":"julia","code":"# anyplot.ai\n# scatter-shot-chart: Basketball Shot Chart\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 87/100 | Created: 2026-06-21\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\"\n\n# Semantic exception: made = success (green), missed = loss (red)\nconst COLOR_MADE   = colorant\"#009E73\"  # Imprint palette pos 1\nconst COLOR_MISSED = colorant\"#AE3030\"  # Imprint palette pos 5 (semantic bad/loss)\n\n# --- Data: 300 shot attempts (basket at origin) -----------------------------\n# NBA half-court: 50 ft wide (x ∈ [-25, 25]), baseline at y = -5.25,\n# half-court at y = 41.75, key x ∈ [-8, 8] y ∈ [-5.25, 13.75]\n\n# Paint shots — high percentage (~56% FG)\nn_paint = 80\nx_paint = (rand(n_paint) .- 0.5) .* 13\ny_paint = rand(n_paint) .* 12 .+ 0.5\nmade_paint = rand(n_paint) .< 0.56\n\n# Mid-range shots — lower percentage (~40% FG)\nx_mid_le = -8.0 .- rand(22) .* 6\ny_mid_le = 9.0 .+ rand(22) .* 10\nx_mid_ri = 8.0 .+ rand(22) .* 6\ny_mid_ri = 9.0 .+ rand(22) .* 10\nx_mid_to = (rand(26) .- 0.5) .* 14\ny_mid_to = 15.0 .+ rand(26) .* 7\nx_mid  = vcat(x_mid_le, x_mid_ri, x_mid_to)\ny_mid  = vcat(y_mid_le, y_mid_ri, y_mid_to)\nmade_mid = rand(70) .< 0.40\n\n# Three-point shots — longest range (~34% FG)\nn_three = 150\nθ_three = π .* rand(n_three)\nr_three = max.(23.75 .+ randn(n_three) .* 1.5, 22.3)\nx_three = clamp.(r_three .* cos.(θ_three), -24.5, 24.5)\ny_three = max.(r_three .* sin.(θ_three), -4.5)\nmade_three = rand(n_three) .< 0.34\n\n# Merge all shot data\nx_shots  = vcat(x_paint, x_mid, x_three)\ny_shots  = vcat(y_paint, y_mid, y_three)\nmade_all = vcat(made_paint, made_mid, made_three)\nmade_idx   = findall(made_all)\nmissed_idx = findall(.!made_all)\nn_made   = length(made_idx)\nn_missed = length(missed_idx)\n\n# --- Figure -----------------------------------------------------------------\nfig = Figure(size = (1200, 1200), fontsize = 14, backgroundcolor = PAGE_BG)\n\nax = Axis(\n    fig[1, 1];\n    title               = \"scatter-shot-chart · julia · makie · anyplot.ai\",\n    titlesize           = 20,\n    titlecolor          = INK,\n    aspect              = DataAspect(),\n    backgroundcolor     = PAGE_BG,\n    topspinevisible     = false,\n    rightspinevisible   = false,\n    leftspinevisible    = false,\n    bottomspinevisible  = false,\n    xticksvisible       = false,\n    yticksvisible       = false,\n    xticklabelsvisible  = false,\n    yticklabelsvisible  = false,\n    xgridvisible        = false,\n    ygridvisible        = false,\n)\n\n# --- Court lines (basket at origin, dims in feet) ---------------------------\nlc = INK_SOFT\nlw = 2.0\n\n# Half-court boundary\nlines!(ax, [-25, 25], [-5.25, -5.25]; color = lc, linewidth = lw)   # baseline\nlines!(ax, [-25, 25], [41.75, 41.75]; color = lc, linewidth = lw)   # half-court\nlines!(ax, [-25, -25], [-5.25, 41.75]; color = lc, linewidth = lw)  # left sideline\nlines!(ax, [25, 25], [-5.25, 41.75]; color = lc, linewidth = lw)    # right sideline\n\n# Paint (key): 16 ft wide, baseline to free-throw line (13.75 ft from basket)\nlines!(ax, [-8, -8], [-5.25, 13.75]; color = lc, linewidth = lw)\nlines!(ax, [8, 8], [-5.25, 13.75]; color = lc, linewidth = lw)\nlines!(ax, [-8, 8], [13.75, 13.75]; color = lc, linewidth = lw)\n\n# Free-throw circle (center (0, 13.75), radius 6 ft) — top half solid, bottom dashed\nθ_ft_top = range(0.0, π, length = 120)\nθ_ft_bot = range(π, 2π, length = 120)\nlines!(ax, 6 .* cos.(θ_ft_top), 13.75 .+ 6 .* sin.(θ_ft_top); color = lc, linewidth = lw)\nlines!(ax, 6 .* cos.(θ_ft_bot), 13.75 .+ 6 .* sin.(θ_ft_bot); color = lc, linewidth = lw, linestyle = :dash)\n\n# Restricted-area arc (radius 4 ft, upper semicircle)\nθ_rest = range(0.0, π, length = 120)\nlines!(ax, 4 .* cos.(θ_rest), 4 .* sin.(θ_rest); color = lc, linewidth = lw)\n\n# Three-point line: arc (radius 23.75 ft) + corner straight lines (x = ±22)\ny_corner  = sqrt(23.75^2 - 22.0^2)             # ≈ 8.95 ft from basket\nθ_3start  = atan(y_corner, 22.0)\nθ_3arc    = range(θ_3start, π - θ_3start, length = 200)\nlines!(ax, 23.75 .* cos.(θ_3arc), 23.75 .* sin.(θ_3arc); color = lc, linewidth = lw)\nlines!(ax, [22.0, 22.0], [-5.25, y_corner]; color = lc, linewidth = lw)\nlines!(ax, [-22.0, -22.0], [-5.25, y_corner]; color = lc, linewidth = lw)\n\n# Basket rim (radius 0.75 ft) and backboard\nθ_rim = range(0.0, 2π, length = 80)\nlines!(ax, 0.75 .* cos.(θ_rim), 0.75 .* sin.(θ_rim); color = lc, linewidth = lw + 0.5)\nlines!(ax, [-3.0, 3.0], [-1.25, -1.25]; color = lc, linewidth = lw + 1.0)\n\n# --- Shot markers -----------------------------------------------------------\n# Shape encoding adds a second channel for CVD accessibility (xcross = missed, circle = made)\nscatter!(ax, x_shots[missed_idx], y_shots[missed_idx];\n    color = (COLOR_MISSED, 0.65), markersize = 12,\n    marker = :xcross,\n    strokewidth = 0.5, strokecolor = PAGE_BG)\n\nscatter!(ax, x_shots[made_idx], y_shots[made_idx];\n    color = (COLOR_MADE, 0.80), markersize = 12,\n    marker = :circle,\n    strokewidth = 0.7, strokecolor = PAGE_BG)\n\n# --- Legend -----------------------------------------------------------------\nelem_missed = MarkerElement(; color = COLOR_MISSED, marker = :xcross, markersize = 14)\nelem_made   = MarkerElement(; color = COLOR_MADE,   marker = :circle, markersize = 14)\n\nLegend(\n    fig[2, 1],\n    [elem_missed, elem_made],\n    [\"Missed ($n_missed)\", \"Made ($n_made)\"];\n    orientation     = :horizontal,\n    tellwidth       = false,\n    tellheight      = true,\n    labelsize       = 14,\n    labelcolor      = INK,\n    framecolor      = INK_SOFT,\n    backgroundcolor = ELEVATED_BG,\n)\n\nxlims!(ax, -27, 27)\nylims!(ax, -8, 44)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}