{"spec_id":"treemap-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\ntreemap-basic: Basic Treemap\nLibrary: matplotlib 3.11.1 | Python 3.13.14\nQuality: 91/100 | Updated: 2026-08-04\n\"\"\"\n\nimport colorsys\nimport os\n\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\nfrom matplotlib.colors import to_hex, to_rgb\nfrom matplotlib.patches import Rectangle\n\n\n# Theme tokens\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\n# Imprint palette for categories\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\"]\n\n# Data - Budget allocation by department and project. Operations has no\n# subcategory split (renders as one unsubdivided rectangle) to demonstrate\n# that the hierarchy nesting is optional, per the spec.\ndata = [\n    (\"Engineering\", \"Product Dev\", 45),\n    (\"Sales\", \"Enterprise\", 35),\n    (\"Marketing\", \"Digital\", 30),\n    (\"Engineering\", \"Infrastructure\", 25),\n    (\"Sales\", \"SMB\", 25),\n    (\"Marketing\", \"Events\", 20),\n    (\"Operations\", None, 35),\n    (\"Engineering\", \"QA\", 15),\n    (\"Sales\", \"Partners\", 15),\n    (\"HR\", \"Recruiting\", 12),\n    (\"HR\", \"Training\", 8),\n]\n\n# Extract sorted data\ncategories = [d[0] for d in data]\nsubcategories = [d[1] for d in data]\nvalues = [d[2] for d in data]\n\n# Category to color mapping (canonical Imprint order)\nunique_categories = [\"Engineering\", \"Sales\", \"Marketing\", \"Operations\", \"HR\"]\ncategory_colors = {cat: IMPRINT[i % len(IMPRINT)] for i, cat in enumerate(unique_categories)}\ncategory_max = {cat: max(v for c, _, v in data if c == cat) for cat in unique_categories}\n\n# Normalize values to fill a 160x90 area (matching figsize aspect ratio)\ntotal = sum(values)\nwidth, height = 160, 90\nnormalized = [v / total * width * height for v in values]\n\n# Squarify algorithm - compute rectangle positions\nrects = []\nremaining = list(zip(normalized, range(len(normalized)), strict=True))\nx, y, w, h = 0, 0, width, height\n\nwhile remaining:\n    vertical = w >= h\n    fixed = h if vertical else w  # dimension held constant while the strip fills\n\n    # Add items to the current strip one at a time, backing off as soon as\n    # the strip's worst aspect ratio would get worse. Shared by both the\n    # vertical-strip and horizontal-strip cases below, which differ only in\n    # which dimension (h or w) is held fixed.\n    strip_items = []\n    strip_area = 0\n    for area, idx in remaining:\n        strip_items.append((area, idx))\n        strip_area += area\n        thickness = strip_area / fixed\n        if len(strip_items) > 1:\n            aspects = [max(thickness / (a / thickness), (a / thickness) / thickness) for a, _ in strip_items]\n            prev_area = strip_area - area\n            prev_aspects = []\n            if prev_area > 0:\n                prev_thickness = prev_area / fixed\n                prev_aspects = [\n                    max(prev_thickness / (a / prev_thickness), (a / prev_thickness) / prev_thickness)\n                    for a, _ in strip_items[:-1]\n                ]\n            if prev_aspects and max(aspects) > max(prev_aspects):\n                strip_items.pop()\n                strip_area -= area\n                break\n\n    # Lay out the finished strip along its fixed dimension\n    thickness = strip_area / fixed if fixed > 0 else 0\n    pos = y if vertical else x\n    for area, idx in strip_items:\n        length = area / thickness if thickness > 0 else 0\n        if vertical:\n            rects.append((x, pos, thickness, length, idx))\n        else:\n            rects.append((pos, y, length, thickness, idx))\n        pos += length\n    if vertical:\n        x += thickness\n        w -= thickness\n    else:\n        y += thickness\n        h -= thickness\n\n    # Remove placed items\n    placed_indices = {idx for _, idx in strip_items}\n    remaining = [(a, i) for a, i in remaining if i not in placed_indices]\n\n# Create plot (3200x1800 px)\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Draw rectangles with labels. Fill lightness is value-driven within each\n# category (largest item keeps the full-strength hue, smaller ones lighten\n# toward a soft tint) so area and shade reinforce the same magnitude signal.\n# The single largest rectangle overall gets a bold ink outline as a focal-point\n# callout, sharpening the visual hierarchy beyond area and shading alone.\nfocal_idx = values.index(max(values))\nfor rx, ry, rw, rh, idx in rects:\n    cat = categories[idx]\n    base_r, base_g, base_b = to_rgb(category_colors[cat])\n    hue, lightness, sat = colorsys.rgb_to_hls(base_r, base_g, base_b)\n    weight = values[idx] / category_max[cat]\n    tint = to_hex(colorsys.hls_to_rgb(hue, min(0.92, lightness + (1 - weight) * 0.18), sat))\n\n    is_focal = idx == focal_idx\n    edge_color = INK if is_focal else PAGE_BG\n    edge_width = 3.5 if is_focal else 1.5\n    rect = Rectangle((rx, ry), rw, rh, facecolor=tint, edgecolor=edge_color, linewidth=edge_width)\n    ax.add_patch(rect)\n\n    # Add labels for all visible rectangles\n    area = rw * rh\n    if area > 80:\n        fontsize = min(9, max(6, round(area**0.35 * 0.5)))\n\n        label = f\"{subcategories[idx] or cat}\\n${values[idx]}M\"\n        ax.text(\n            rx + rw / 2, ry + rh / 2, label, ha=\"center\", va=\"center\", fontsize=fontsize, fontweight=\"bold\", color=INK\n        )\n\n# Set axis limits and remove axes\nax.set_xlim(0, width)\nax.set_ylim(0, height)\nax.axis(\"off\")\nax.set_aspect(\"equal\")\n\n# Title\nax.set_title(\"treemap-basic · python · matplotlib · anyplot.ai\", fontsize=12, fontweight=\"medium\", color=INK, pad=10)\n\n# Legend for categories (canonical Imprint hue, unshaded, for brand fidelity)\nlegend_handles = [mpatches.Patch(color=category_colors[cat], label=cat) for cat in unique_categories]\nleg = ax.legend(\n    handles=legend_handles,\n    loc=\"upper center\",\n    fontsize=8,\n    framealpha=0.95,\n    edgecolor=INK_SOFT,\n    ncol=5,\n    bbox_to_anchor=(0.5, -0.03),\n)\nleg.get_frame().set_facecolor(ELEVATED_BG)\nleg.get_frame().set_edgecolor(INK_SOFT)\nfor text in leg.get_texts():\n    text.set_color(INK_SOFT)\n\nfig.subplots_adjust(left=0.02, right=0.98, top=0.90, bottom=0.13)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}