{"spec_id":"circlepacking-basic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\ncirclepacking-basic: Circle Packing Chart\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 93/100 | Created: 2026-05-11\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nfrom matplotlib.patches import Circle\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\n# Data: File system hierarchy\nnp.random.seed(42)\nnodes = []\n\nnode_id = 0\nnodes.append({\"id\": node_id, \"parent\": None, \"value\": 100, \"label\": \"root/\", \"depth\": 0})\nnode_id += 1\n\nfolders = [\"Documents\", \"Media\", \"Projects\", \"Archive\"]\nfor folder in folders:\n    size = np.random.uniform(15, 25)\n    nodes.append({\"id\": node_id, \"parent\": 0, \"value\": size, \"label\": folder, \"depth\": 1})\n    parent_id = node_id\n    node_id += 1\n\n    for i in range(np.random.randint(3, 5)):\n        file_size = np.random.uniform(2, 6)\n        nodes.append({\"id\": node_id, \"parent\": parent_id, \"value\": file_size, \"label\": f\"item_{i + 1}\", \"depth\": 2})\n        node_id += 1\n\ndf = pd.DataFrame(nodes)\n\n\ndef pack_circles(df):\n    positions = {}\n\n    # Root at center\n    root = df[df[\"parent\"].isna()].iloc[0]\n    root_radius = np.sqrt(root[\"value\"]) * 3\n    positions[root[\"id\"]] = (0, 0, root_radius)\n\n    # Level 1 nodes (direct children of root)\n    level_1 = df[df[\"parent\"] == root[\"id\"]].sort_values(\"value\", ascending=False)\n    angle_step = 2 * np.pi / len(level_1)\n    distance_from_root = root_radius + 35\n\n    for idx, (_, node) in enumerate(level_1.iterrows()):\n        radius = np.sqrt(node[\"value\"]) * 3\n        angle = idx * angle_step\n        x = distance_from_root * np.cos(angle)\n        y = distance_from_root * np.sin(angle)\n        positions[node[\"id\"]] = (x, y, radius)\n\n    # Level 2 nodes (grandchildren)\n    for _, parent_node in level_1.iterrows():\n        parent_id = parent_node[\"id\"]\n        parent_x, parent_y, parent_radius = positions[parent_id]\n\n        children = df[df[\"parent\"] == parent_id]\n        if len(children) == 0:\n            continue\n\n        angle_step = 2 * np.pi / len(children)\n        distance = parent_radius + 18\n\n        for idx, (_, child_node) in enumerate(children.iterrows()):\n            radius = np.sqrt(child_node[\"value\"]) * 3\n            angle = idx * angle_step\n            x = parent_x + distance * np.cos(angle)\n            y = parent_y + distance * np.sin(angle)\n            positions[child_node[\"id\"]] = (x, y, radius)\n\n    return positions\n\n\npositions = pack_circles(df)\n\n# Plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Draw circles\nfor _, row in df.iterrows():\n    x, y, radius = positions[row[\"id\"]]\n    depth = row[\"depth\"]\n\n    color = IMPRINT[depth % len(IMPRINT)]\n    circle = Circle((x, y), radius, facecolor=color, edgecolor=INK_SOFT, linewidth=2, alpha=0.85)\n    ax.add_patch(circle)\n\n    # Label for larger circles\n    if radius > 8:\n        text_color = PAGE_BG if depth == 0 else INK\n        ax.text(\n            x,\n            y,\n            row[\"label\"],\n            ha=\"center\",\n            va=\"center\",\n            fontsize=14 if depth == 0 else 12,\n            color=text_color,\n            fontweight=\"bold\",\n        )\n\n# Set limits\nall_x = [pos[0] for pos in positions.values()]\nall_y = [pos[1] for pos in positions.values()]\nall_r = [pos[2] for pos in positions.values()]\nxlim = (min(all_x) - max(all_r) - 15, max(all_x) + max(all_r) + 15)\nylim = (min(all_y) - max(all_r) - 15, max(all_y) + max(all_r) + 15)\nax.set_xlim(xlim)\nax.set_ylim(ylim)\nax.set_aspect(\"equal\")\n\n# Remove axes\nax.set_xticks([])\nax.set_yticks([])\nfor spine in ax.spines.values():\n    spine.set_visible(False)\n\n# Title\nax.text(\n    0.5,\n    0.98,\n    \"circlepacking-basic · seaborn · anyplot.ai\",\n    ha=\"center\",\n    va=\"top\",\n    transform=ax.transAxes,\n    fontsize=24,\n    fontweight=\"medium\",\n    color=INK,\n)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\nplt.close()\n"}