{"spec_id":"parliament-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# parliament-basic: Parliament Seat Chart\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 87/100 | Created: 2026-09-02\n\nusing CairoMakie\nusing Colors\n\n# --- Theme tokens -------------------------------------------------------\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\"\nconst MUTED    = THEME == \"light\" ? colorant\"#6B6A63\" : colorant\"#A8A79F\"\n\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\", colorant\"#C475FD\", colorant\"#4467A3\",\n    colorant\"#BD8233\", colorant\"#AE3030\", colorant\"#2ABCCD\",\n]\n\n# --- Data: fictional national assembly, 2026 election results -----------\n# Independents carry no party color, so they use the theme-adaptive\n# \"other / rest\" anchor instead of a 7th categorical slot.\nparties       = [\"Green Alliance\", \"Progressive Union\", \"Centrist Coalition\",\n                  \"Reform Party\", \"Conservative Bloc\", \"Liberty Party\", \"Independents\"]\nseats         = [68, 84, 52, 45, 71, 20, 10]\nparty_colors  = vcat(IMPRINT_PALETTE, [MUTED])\ntotal_seats   = sum(seats)\n\n# --- Layout: seats arranged in concentric semicircular arcs -------------\n# Seats per row are proportional to the row's radius so that the arc\n# length per seat (and thus visual density) stays roughly constant\n# across rows.\nn_rows        = 9\ninner_radius  = 4.0\nrow_spacing   = 1.0\nrow_radii     = inner_radius .+ (0:(n_rows - 1)) .* row_spacing\n\nrow_counts = round.(Int, row_radii ./ sum(row_radii) .* total_seats)\nwhile sum(row_counts) != total_seats\n    delta = total_seats - sum(row_counts)\n    idx = delta > 0 ? argmax(row_radii) : argmin(row_radii)\n    row_counts[idx] += sign(delta)\nend\n\nseat_angles = Float64[]\nseat_radii = Float64[]\nfor (r, cnt) in zip(row_radii, row_counts)\n    cnt == 0 && continue\n    θs = cnt == 1 ? [π / 2] : collect(range(π, 0, length = cnt))\n    append!(seat_angles, θs)\n    append!(seat_radii, fill(r, cnt))\nend\n\n# Sort left (θ≈π) to right (θ≈0) so party blocks read left-to-right,\n# matching the canonical political-spectrum ordering.\norder = sortperm(seat_angles, rev = true)\nseat_angles = seat_angles[order]\nseat_radii = seat_radii[order]\n\nseat_x = seat_radii .* cos.(seat_angles)\nseat_y = seat_radii .* sin.(seat_angles)\nseat_colors = reduce(vcat, [fill(c, n) for (n, c) in zip(seats, party_colors)])\n\n# Majority-threshold angle: the ray through the (total_seats/2 + 1)-th seat\n# in left-to-right order marks the 50%+1 split of the whole assembly.\nmajority_seats  = div(total_seats, 2) + 1\nthreshold_angle = seat_angles[majority_seats]\n\n# Plurality leader, called out in the legend.\nplurality_idx = argmax(seats)\n\n# --- Plot -----------------------------------------------------------------\nfig = Figure(\n    resolution      = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title           = \"parliament-basic · julia · makie · anyplot.ai\",\n    titlesize       = 20,\n    titlecolor      = INK,\n    backgroundcolor = PAGE_BG,\n    aspect          = DataAspect(),\n)\nhidedecorations!(ax)\nhidespines!(ax)\nxlims!(ax, -row_radii[end] - 1, row_radii[end] + 1)\nylims!(ax, -0.5, row_radii[end] + 1)\n\nscatter!(\n    ax, seat_x, seat_y;\n    color = seat_colors, markersize = 22,\n    strokewidth = 1.0, strokecolor = INK,\n)\n\n# Majority-threshold indicator: dashed radial line + label at 50%+1 seats.\nline_len = row_radii[end] + 0.6\nlines!(\n    ax, [0.0, line_len * cos(threshold_angle)], [0.0, line_len * sin(threshold_angle)];\n    color = INK_SOFT, linewidth = 1.5, linestyle = :dash,\n)\ntext!(\n    ax, line_len * cos(threshold_angle), line_len * sin(threshold_angle);\n    text = \"Majority ($majority_seats)\", color = INK_SOFT, fontsize = 12,\n    align = (threshold_angle < π / 2 ? :left : :right, :bottom),\n)\n\nlegend_labels = [\n    \"$p ($s)$(i == plurality_idx ? \"  ★ largest\" : \"\")\"\n    for (i, (p, s)) in enumerate(zip(parties, seats))\n]\nlegend_markers = [MarkerElement(color = c, marker = :circle, markersize = 18) for c in party_colors]\nLegend(\n    fig[1, 2], legend_markers, legend_labels;\n    framevisible = false,\n    labelcolor   = INK_SOFT,\n    labelsize    = 14,\n    patchsize    = (18, 18),\n)\n\ncolsize!(fig.layout, 1, Relative(0.87))\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}