{"spec_id":"radar-innovation-timeline","library":"makie","language":"julia","code":"# anyplot.ai\n# radar-innovation-timeline: Innovation Radar with Time-Horizon Rings\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 88/100 | Created: 2026-05-29\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 — AI & ML (Imprint brand green, always first series)\n    colorant\"#C475FD\",  # 2 — Cloud & Infra\n    colorant\"#4467A3\",  # 3 — Security\n    colorant\"#BD8233\",  # 4 — DevEx\n]\n\nconst SECTORS = [\"AI & ML\", \"Cloud & Infra\", \"Security\", \"DevEx\"]\nconst RINGS   = [\"Adopt\", \"Trial\", \"Assess\", \"Hold\"]\n\n# Ring boundaries (inner → outer radii, 4 bands)\nconst RING_BOUNDS  = [0.0, 0.26, 0.50, 0.74, 0.95]\nconst RING_CENTERS = [(RING_BOUNDS[i] + RING_BOUNDS[i+1]) / 2 for i in 1:4]\n\n# Three-quarter (270°) layout — gap at bottom (225°–315°)\n# Chart spans counterclockwise from 315° (= −π/4) to 225° (= 5π/4)\nconst TOTAL_SPAN    = 3π / 2\nconst SECTOR_SPAN   = TOTAL_SPAN / 4   # 67.5° per sector\nconst CHART_START   = -π / 4           # 315° from positive x-axis\nconst SECTOR_STARTS = [CHART_START + i * SECTOR_SPAN for i in 0:3]\n\n# Items: (label, sector_idx, ring_idx, fraction_within_sector 0→1)\nconst ITEMS = [\n    (\"LLM APIs\",          1, 1, 0.25),\n    (\"Vector DBs\",        1, 1, 0.75),\n    (\"RAG\",               1, 2, 0.30),\n    (\"Fine-tuning\",       1, 2, 0.72),\n    (\"Multimodal AI\",     1, 3, 0.28),\n    (\"AI Agents\",         1, 3, 0.68),\n    (\"AGI Infra\",         1, 4, 0.50),\n    (\"Kubernetes\",        2, 1, 0.25),\n    (\"Terraform\",         2, 1, 0.75),\n    (\"eBPF\",              2, 2, 0.35),\n    (\"WASM\",              2, 2, 0.72),\n    (\"Edge Native\",       2, 3, 0.45),\n    (\"Unikernels\",        2, 4, 0.50),\n    (\"Zero Trust\",        3, 1, 0.50),\n    (\"SBOM\",              3, 2, 0.28),\n    (\"Sigstore\",          3, 2, 0.72),\n    (\"Confidential Comp\", 3, 3, 0.50),\n    (\"Post-Quantum\",      3, 4, 0.42),\n    (\"GitHub Actions\",    4, 1, 0.25),\n    (\"Dev Containers\",    4, 1, 0.75),\n    (\"OpenTelemetry\",     4, 2, 0.35),\n    (\"AI Code Assist\",    4, 2, 0.72),\n    (\"Backstage\",         4, 3, 0.35),\n    (\"DORA Metrics\",      4, 3, 0.65),\n]\n\npol2cart(r, θ) = (r * cos(θ), r * sin(θ))\n\n# Figure — square canvas → 2400×2400 px\nfig = Figure(\n    size            = (1200, 1200),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    backgroundcolor = PAGE_BG,\n    aspect          = DataAspect(),\n)\nhidedecorations!(ax)\nhidespines!(ax)\n\nn_arc = 120\n\n# 1. Per-ring background fills — alternating neutral shading separates time horizons\nring_fill_alphas = [0.08, 0.02, 0.07, 0.02]\nfor (i, alpha) in enumerate(ring_fill_alphas)\n    r_inner = RING_BOUNDS[i]\n    r_outer = RING_BOUNDS[i + 1]\n    θs  = LinRange(CHART_START, CHART_START + TOTAL_SPAN, n_arc)\n    pts = Point2f[]\n    for t in θs\n        push!(pts, Point2f(pol2cart(r_outer, t)...))\n    end\n    if r_inner == 0.0\n        push!(pts, Point2f(0.0f0, 0.0f0))\n    else\n        for t in reverse(θs)\n            push!(pts, Point2f(pol2cart(r_inner, t)...))\n        end\n    end\n    poly!(ax, pts; color = (INK, alpha), strokewidth = 0)\nend\n\n# 2. Sector fills — subtle tinted wedges overlay ring fills for sector identity\nfor i in 1:4\n    θ_start = SECTOR_STARTS[i]\n    θ_end   = θ_start + SECTOR_SPAN\n    pts = Point2f[(0.0f0, 0.0f0)]\n    for t in LinRange(θ_start, θ_end, n_arc)\n        push!(pts, Point2f(pol2cart(RING_BOUNDS[5], t)...))\n    end\n    poly!(ax, pts; color = (IMPRINT_PALETTE[i], 0.07f0), strokewidth = 0)\nend\n\n# 3. Ring outlines — partial arcs spanning the 270° chart\nfor (k, r) in enumerate(RING_BOUNDS)\n    r == 0.0 && continue\n    θs = LinRange(CHART_START, CHART_START + TOTAL_SPAN, n_arc + 1)\n    xs = r .* cos.(θs)\n    ys = r .* sin.(θs)\n    lw = (k == length(RING_BOUNDS)) ? 1.5f0 : 0.8f0\n    lines!(ax, xs, ys; color = (INK, 0.30f0), linewidth = lw)\nend\n\n# 4. Sector dividers — radial lines at each sector boundary and chart endpoints\nfor θ in [SECTOR_STARTS; CHART_START + TOTAL_SPAN]\n    x2, y2 = pol2cart(RING_BOUNDS[5], θ)\n    lines!(ax, [0.0f0, Float32(x2)], [0.0f0, Float32(y2)];\n           color = (INK, 0.28f0), linewidth = 1.0f0)\nend\n\n# 5. Ring labels — placed in the 90° gap (−90° = straight down) for clear separation\nfor (i, ring_name) in enumerate(RINGS)\n    r = RING_CENTERS[i]\n    xl, yl = pol2cart(r, -π / 2)\n    text!(ax, xl, yl; text = ring_name,\n          fontsize = 12,\n          color    = INK_MUTED,\n          align    = (:center, :center))\nend\n\n# 6. Sector headers — labels just outside the outermost ring\nheader_r = RING_BOUNDS[5] + 0.10\nfor (i, sector_name) in enumerate(SECTORS)\n    θ_mid = SECTOR_STARTS[i] + SECTOR_SPAN / 2\n    xl, yl = pol2cart(header_r, θ_mid)\n    ha = cos(θ_mid) > 0.15 ? :left : (cos(θ_mid) < -0.15 ? :right : :center)\n    va = sin(θ_mid) > 0.15 ? :bottom : (sin(θ_mid) < -0.15 ? :top : :center)\n    text!(ax, xl, yl; text = sector_name,\n          fontsize = 14,\n          color    = IMPRINT_PALETTE[i],\n          align    = (ha, va))\nend\n\n# 7. Data points — scatter, colored by sector\nfor (label, sec, ring, frac) in ITEMS\n    θ = SECTOR_STARTS[sec] + frac * SECTOR_SPAN\n    r = RING_CENTERS[ring]\n    x, y = pol2cart(r, θ)\n    scatter!(ax, [x], [y];\n             color       = IMPRINT_PALETTE[sec],\n             markersize  = 13,\n             strokewidth = 1.2f0,\n             strokecolor = PAGE_BG)\nend\n\n# 8. Item labels — offset radially outward from each point\nfor (label, sec, ring, frac) in ITEMS\n    θ = SECTOR_STARTS[sec] + frac * SECTOR_SPAN\n    r = RING_CENTERS[ring]\n    x, y = pol2cart(r, θ)\n    offset = 0.055\n    xl = x + offset * cos(θ)\n    yl = y + offset * sin(θ)\n    ha = cos(θ) > 0.15 ? :left : (cos(θ) < -0.15 ? :right : :center)\n    va = sin(θ) > 0.15 ? :bottom : (sin(θ) < -0.15 ? :top : :center)\n    text!(ax, xl, yl; text = label,\n          fontsize = 10,\n          color    = INK_SOFT,\n          align    = (ha, va))\nend\n\n# Axis limits — accommodate sector headers, ring labels in gap, and item labels\nxlims!(ax, -1.40, 1.40)\nylims!(ax, -1.10, 1.25)\n\n# Title\ntitle_str      = \"radar-innovation-timeline · julia · makie · anyplot.ai\"\ntitle_n        = length(title_str)\ntitle_fontsize = title_n > 67 ? round(Int, 20 * 67 / title_n) : 20\n\nLabel(fig[0, 1], title_str;\n      fontsize  = title_fontsize,\n      color     = INK,\n      tellwidth = false)\n\n# Legend\nlegend_elems = [MarkerElement(color = IMPRINT_PALETTE[i], marker = :circle, markersize = 13)\n                for i in 1:4]\nLegend(fig[2, 1], legend_elems, SECTORS;\n       orientation  = :horizontal,\n       framevisible = false,\n       labelcolor   = INK_SOFT,\n       labelsize    = 11,\n       tellwidth    = false)\n\n# Save\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}