{"spec_id":"pictogram-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# pictogram-basic: Pictogram Chart (Isotype Visualization)\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 86/100 | Created: 2026-06-03\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\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\"\n\n# Imprint palette — semantic color assignments matched to fruit identity\nconst COLORS = [\n    colorant\"#99B314\",  # Bananas — lime (yellow-green banana)\n    colorant\"#009E73\",  # Apples — brand green (green apple)\n    colorant\"#BD8233\",  # Oranges — ochre (warm orange)\n    colorant\"#C475FD\",  # Grapes — lavender (purple grapes)\n    colorant\"#AE3030\",  # Mangoes — matte red (tropical warm)\n]\n\n# Data — global fruit production (million tonnes, approx. FAO 2021-22), sorted descending\nconst categories = [\"Bananas\", \"Apples\", \"Oranges\", \"Grapes\", \"Mangoes\"]\nconst values     = [116, 88, 79, 77, 57]\nconst unit_size  = 20  # each icon = 20 million tonnes\n\n# Layout constants\nconst n_cats = length(categories)\nconst icon_r = 0.45   # icon radius in data units\nconst x_gap  = 1.5    # horizontal spacing between icon centres\nconst y_gap  = 1.5    # vertical spacing between category rows\n\n# Y positions — first category at top (highest y), last at bottom\ny_positions = [(n_cats - i + 1) * y_gap for i in 1:n_cats]\n\n# Figure\nfig = Figure(\n    resolution      = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\n# max_icons = ceil(116 / 20) = 6\nmax_icons = ceil(Int, maximum(values) / unit_size)\n\nax = Axis(\n    fig[1, 1];\n    title              = \"Fruit Production · pictogram-basic · julia · makie · anyplot.ai\",\n    titlesize          = 20,\n    titlecolor         = INK,\n    aspect             = DataAspect(),\n    yticks             = (y_positions, categories),\n    yticklabelsize     = 14,\n    yticklabelcolor    = INK_SOFT,\n    yticksvisible      = false,\n    xticklabelsvisible = false,\n    xticksvisible      = false,\n    backgroundcolor    = PAGE_BG,\n    topspinevisible    = false,\n    rightspinevisible  = false,\n    bottomspinevisible = false,\n    leftspinevisible   = false,\n    xgridvisible       = false,\n    ygridvisible       = false,\n)\n\n# 60-gon circle approximation — reliable for poly! with DataAspect data-coordinate circles\nθ_circle = range(0, 2π, length = 61)[1:60]\n\n# Fixed x for right-aligned value labels\nlabel_x = (max_icons + 1) * x_gap + 0.3\n\nfor i in 1:n_cats\n    val      = values[i]\n    color    = COLORS[i]\n    y_pos    = y_positions[i]\n    n_full   = floor(Int, val / unit_size)\n    fraction = (val / unit_size) - n_full\n\n    # Full icons — filled circle polygons\n    for j in 1:n_full\n        x_pos = j * x_gap\n        poly!(ax,\n            Point2f.(collect(zip(\n                x_pos .+ icon_r .* cos.(θ_circle),\n                y_pos .+ icon_r .* sin.(θ_circle),\n            )));\n            color = color, strokewidth = 0)\n    end\n\n    # Partial icon — ghost circle outline + filled pie-sector\n    if fraction > 0.02\n        x_pos = (n_full + 1) * x_gap\n\n        # Ghost circle: 25% alpha fill + 2px stroke for readability in both themes\n        poly!(ax,\n            Point2f.(collect(zip(\n                x_pos .+ icon_r .* cos.(θ_circle),\n                y_pos .+ icon_r .* sin.(θ_circle),\n            )));\n            color = (color, 0.25), strokecolor = color, strokewidth = 2.0)\n\n        # Filled pie sector (clockwise from 12 o'clock)\n        θ_arc     = range(π/2, π/2 - 2π * fraction, length = 60)\n        xs_sector = vcat([x_pos], x_pos .+ icon_r .* cos.(θ_arc), [x_pos])\n        ys_sector = vcat([y_pos], y_pos .+ icon_r .* sin.(θ_arc), [y_pos])\n        poly!(ax,\n            Point2f.(collect(zip(xs_sector, ys_sector)));\n            color = color, strokewidth = 0)\n    end\n\n    # Value label — aligned right for easy comparison\n    text!(ax, label_x, y_pos;\n        text     = \"$(val) Mt\",\n        fontsize = 12,\n        color    = INK_SOFT,\n        align    = (:left, :center),\n    )\nend\n\n# Focal-point annotation — draws the viewer's eye to the world leader\ntext!(ax, 0.5 * x_gap, y_positions[1] + y_gap * 0.52;\n    text     = \"↑ world's most produced fruit\",\n    fontsize = 10,\n    color    = INK_SOFT,\n    align    = (:left, :center),\n)\n\n# Axis limits — x span tuned to ~16:9 with DataAspect\nxlims!(ax, -0.8, (max_icons + 3.5) * x_gap)\nylims!(ax, y_positions[end] - y_gap * 0.9, y_positions[1] + y_gap * 0.7)\n\n# Unit legend — below the lowest category row\ntext!(ax, 0.5 * x_gap, y_positions[end] - y_gap * 0.6;\n    text     = \"● = $(unit_size) million tonnes\",\n    fontsize = 11,\n    color    = INK_SOFT,\n    align    = (:left, :center),\n)\n\n# Save\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}