{"spec_id":"parliament-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nparliament-basic: Parliament Seat Chart\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Okabe-Ito palette (canonical order, starting with brand green)\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Data - fictional parliament with 6 parties (400 total seats)\nparties = [\n    \"Green Alliance\",\n    \"Labor Coalition\",\n    \"Conservative bloc\",\n    \"Reform Party\",\n    \"Centrist Union\",\n    \"Progressive Front\",\n]\nseats = [98, 87, 76, 65, 48, 26]\ncolors = IMPRINT[: len(parties)]\ntotal_seats = sum(seats)\n\n# Parliament layout parameters\nn_rows = 8\ninner_radius = 3.0\nrow_gap = 0.8\nangle_margin = 0.08\n\n# Calculate seats per row (more seats in outer rows)\nrow_weights = np.array([inner_radius + i * row_gap for i in range(n_rows)])\nrow_weights = row_weights / row_weights.sum()\nseats_per_row = np.round(row_weights * total_seats).astype(int)\n\n# Adjust to match total\ndiff = total_seats - seats_per_row.sum()\nseats_per_row[-1] += diff\n\n# Generate all seat positions with angles\nseat_positions = []\nfor row_idx in range(n_rows):\n    radius = inner_radius + row_idx * row_gap\n    n_seats_in_row = seats_per_row[row_idx]\n    angles = np.linspace(np.pi - angle_margin, angle_margin, n_seats_in_row)\n    for angle in angles:\n        seat_positions.append((radius, angle))\n\n# Sort all seats by angle (left to right = pi to 0)\nseat_positions.sort(key=lambda p: -p[1])\n\n# Assign colors based on sorted position\nall_x = []\nall_y = []\nall_colors = []\n\nparty_idx = 0\ncumulative_seats = np.cumsum([0] + seats)\n\nfor i, (radius, angle) in enumerate(seat_positions):\n    x = radius * np.cos(angle)\n    y = radius * np.sin(angle)\n    all_x.append(x)\n    all_y.append(y)\n\n    while party_idx < len(seats) - 1 and i >= cumulative_seats[party_idx + 1]:\n        party_idx += 1\n    all_colors.append(colors[party_idx])\n\nall_x = np.array(all_x)\nall_y = np.array(all_y)\n\n# Create figure\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Plot seats as circles\nax.scatter(all_x, all_y, c=all_colors, s=160, edgecolors=PAGE_BG, linewidth=0.5, zorder=2)\n\n# Create legend entries with seat counts\nlegend_elements = []\nfor party, seat_count, color in zip(parties, seats, colors, strict=True):\n    legend_elements.append(\n        plt.scatter([], [], c=color, s=200, edgecolors=PAGE_BG, linewidth=0.5, label=f\"{party} ({seat_count})\")\n    )\n\nleg = ax.legend(\n    handles=legend_elements, loc=\"lower center\", ncol=3, fontsize=16, frameon=True, bbox_to_anchor=(0.5, -0.05)\n)\nleg.get_frame().set_facecolor(PAGE_BG)\nleg.get_frame().set_edgecolor(INK_SOFT)\nleg.get_frame().set_linewidth(0.8)\nfor text in leg.get_texts():\n    text.set_color(INK_SOFT)\n\n# Add majority threshold annotation\nmajority = total_seats // 2 + 1\nax.text(0, -0.8, f\"Majority threshold: {majority} seats\", ha=\"center\", fontsize=14, color=INK_SOFT, style=\"italic\")\n\n# Styling\nax.set_xlim(-8.5, 8.5)\nax.set_ylim(-2.5, 8)\nax.set_aspect(\"equal\")\nax.axis(\"off\")\n\n# Title\nax.set_title(\"parliament-basic · matplotlib · anyplot.ai\", fontsize=24, pad=20, fontweight=\"medium\", color=INK)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}