{"spec_id":"network-directed","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nnetwork-directed: Directed Network Graph\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 96/100 | Updated: 2026-05-14\n\"\"\"\n\nimport os\n\nimport matplotlib.patches as mpatches\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\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\n# Okabe-Ito palette for node groups\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\"]\n\nnp.random.seed(42)\n\nsns.set_theme(\n    style=\"white\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.10,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Software module dependencies with hierarchical layout\nmodules = {\n    \"app\": {\"group\": \"core\", \"pos\": (0.5, 0.88)},\n    \"config\": {\"group\": \"core\", \"pos\": (0.18, 0.70)},\n    \"api\": {\"group\": \"services\", \"pos\": (0.50, 0.68)},\n    \"auth\": {\"group\": \"services\", \"pos\": (0.82, 0.70)},\n    \"db\": {\"group\": \"data\", \"pos\": (0.28, 0.45)},\n    \"cache\": {\"group\": \"data\", \"pos\": (0.72, 0.45)},\n    \"log\": {\"group\": \"utils\", \"pos\": (0.15, 0.22)},\n    \"valid\": {\"group\": \"utils\", \"pos\": (0.50, 0.22)},\n    \"router\": {\"group\": \"services\", \"pos\": (0.85, 0.22)},\n    \"model\": {\"group\": \"data\", \"pos\": (0.32, 0.05)},\n    \"mware\": {\"group\": \"services\", \"pos\": (0.68, 0.05)},\n}\n\n# Directed edges with weights\nedges = [\n    (\"app\", \"config\", 3),\n    (\"app\", \"db\", 5),\n    (\"app\", \"api\", 5),\n    (\"app\", \"auth\", 4),\n    (\"api\", \"valid\", 4),\n    (\"api\", \"router\", 3),\n    (\"api\", \"log\", 2),\n    (\"auth\", \"cache\", 4),\n    (\"auth\", \"db\", 3),\n    (\"auth\", \"log\", 2),\n    (\"db\", \"config\", 3),\n    (\"db\", \"log\", 1),\n    (\"cache\", \"config\", 2),\n    (\"router\", \"valid\", 3),\n    (\"router\", \"mware\", 2),\n    (\"model\", \"db\", 4),\n    (\"model\", \"valid\", 3),\n    (\"mware\", \"auth\", 3),\n    (\"mware\", \"log\", 1),\n]\n\n# Map groups to Okabe-Ito palette\ngroups = [\"core\", \"data\", \"services\", \"utils\"]\ngroup_colors = {\"core\": IMPRINT[0], \"data\": IMPRINT[1], \"services\": IMPRINT[2], \"utils\": IMPRINT[3]}\n\n# Prepare node data\nnode_data = []\nfor name, data in modules.items():\n    node_data.append({\"name\": name, \"x\": data[\"pos\"][0], \"y\": data[\"pos\"][1], \"group\": data[\"group\"]})\nnodes_df = pd.DataFrame(node_data)\n\n# Create figure\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Draw edges with directed arrows\nfor source, target, weight in edges:\n    start = modules[source][\"pos\"]\n    end = modules[target][\"pos\"]\n\n    dx = end[0] - start[0]\n    dy = end[1] - start[1]\n    length = np.sqrt(dx**2 + dy**2)\n    dx_norm = dx / length if length > 0 else 0\n    dy_norm = dy / length if length > 0 else 1\n\n    # Shorten arrows to avoid overlapping nodes\n    shrink = 0.055\n    start_adj = (start[0] + dx_norm * shrink, start[1] + dy_norm * shrink)\n    end_adj = (end[0] - dx_norm * shrink, end[1] - dy_norm * shrink)\n\n    line_width = 1.0 + weight * 0.6\n\n    arrow = mpatches.FancyArrowPatch(\n        start_adj,\n        end_adj,\n        connectionstyle=\"arc3,rad=0.1\",\n        arrowstyle=\"->,head_length=8,head_width=5\",\n        color=INK_SOFT,\n        alpha=0.5 + weight * 0.08,\n        linewidth=line_width,\n        zorder=1,\n    )\n    ax.add_patch(arrow)\n\n# Draw nodes\npalette_list = [group_colors[g] for g in nodes_df[\"group\"]]\nsns.scatterplot(\n    data=nodes_df,\n    x=\"x\",\n    y=\"y\",\n    hue=\"group\",\n    palette=group_colors,\n    s=3000,\n    edgecolor=PAGE_BG,\n    linewidth=3,\n    legend=False,\n    ax=ax,\n    zorder=2,\n)\n\n# Add node labels\nfor name, data in modules.items():\n    label_color = PAGE_BG if data[\"group\"] != \"data\" else INK\n    ax.text(\n        data[\"pos\"][0],\n        data[\"pos\"][1],\n        name,\n        ha=\"center\",\n        va=\"center\",\n        fontsize=16,\n        fontweight=\"bold\",\n        color=label_color,\n        zorder=3,\n    )\n\n# Create legend\nlegend_handles = [\n    plt.scatter([], [], c=[group_colors[g]], s=400, label=g.capitalize(), edgecolors=PAGE_BG, linewidths=2)\n    for g in groups\n]\nlegend_handles.append(plt.Line2D([0], [0], color=INK_SOFT, linewidth=1.6, label=\"Weak (1)\", alpha=0.6))\nlegend_handles.append(plt.Line2D([0], [0], color=INK_SOFT, linewidth=4.0, label=\"Strong (5)\", alpha=0.9))\n\nax.legend(\n    handles=legend_handles,\n    loc=\"upper left\",\n    fontsize=14,\n    title=\"Module Type / Edge Weight\",\n    title_fontsize=16,\n    framealpha=0.95,\n    markerscale=1.0,\n    borderpad=1,\n    facecolor=ELEVATED_BG,\n    edgecolor=INK_SOFT,\n)\n\n# Styling\nax.set_title(\"network-directed · seaborn · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK, pad=20)\nax.set_xlim(-0.02, 1.02)\nax.set_ylim(-0.08, 1.02)\nax.axis(\"off\")\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}