{"spec_id":"venn-labeled-items","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nvenn-labeled-items: Chartgeist-Style Venn Diagram with Labeled Items\nLibrary: letsplot 4.9.0 | Python 3.14.4\nQuality: 87/100 | Created: 2026-04-25\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    coord_fixed,\n    element_blank,\n    element_rect,\n    geom_polygon,\n    geom_text,\n    ggplot,\n    ggsave,\n    ggsize,\n    theme,\n    theme_void,\n)\n\n\nLetsPlot.setup_html()\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Okabe-Ito categorical (positions 1, 2, 3)\nCOLOR_A = \"#009E73\"\nCOLOR_B = \"#C475FD\"\nCOLOR_C = \"#4467A3\"\n\n# Geometry — three equally sized overlapping circles\nRADIUS = 1.5\ncenters = {\"A\": (-0.85, 0.50), \"B\": (0.85, 0.50), \"C\": (0.00, -1.00)}\ncircle_meta = [\n    {\"key\": \"A\", \"name\": \"Overhyped\", \"color\": COLOR_A},\n    {\"key\": \"B\", \"name\": \"Actually Useful\", \"color\": COLOR_B},\n    {\"key\": \"C\", \"name\": \"Secretly Loved\", \"color\": COLOR_C},\n]\n\ntheta = np.linspace(0, 2 * np.pi, 240)\n\n# Items distributed across the seven interior zones (+ outside)\nitems = [\n    # A — Overhyped\n    (\"NFTs\", -2.00, 1.05),\n    (\"Metaverse\", -2.05, 0.55),\n    (\"Smart Fridges\", -1.95, 0.05),\n    # B — Actually Useful\n    (\"Google Maps\", 2.00, 1.05),\n    (\"Spreadsheets\", 2.05, 0.55),\n    (\"Calendar Apps\", 1.95, 0.05),\n    # C — Secretly Loved\n    (\"Roller Skating\", -0.75, -2.05),\n    (\"Soap Operas\", 0.75, -2.05),\n    # A ∩ B\n    (\"ChatGPT\", 0.00, 1.20),\n    (\"Smartwatches\", 0.00, 0.85),\n    # A ∩ C\n    (\"Crocs\", -1.05, -0.20),\n    (\"Vinyl Records\", -1.00, -0.60),\n    # B ∩ C\n    (\"Dolly Parton\", 1.05, -0.20),\n    (\"Spotify\", 1.00, -0.60),\n    # A ∩ B ∩ C\n    (\"Sourdough\", 0.00, 0.15),\n    (\"TikTok\", 0.00, -0.30),\n    # outside\n    (\"Beige Walls\", -3.10, -2.50),\n]\nitems_df = pd.DataFrame(items, columns=[\"label\", \"x\", \"y\"])\n\n# Plot\nplot = ggplot() + coord_fixed(xlim=[-4.0, 4.0], ylim=[-3.5, 3.5])\n\n# Three overlapping circles with semi-transparent fills\nfor c in circle_meta:\n    cx, cy = centers[c[\"key\"]]\n    circle_df = pd.DataFrame({\"x\": cx + RADIUS * np.cos(theta), \"y\": cy + RADIUS * np.sin(theta)})\n    plot = plot + geom_polygon(\n        aes(x=\"x\", y=\"y\"), data=circle_df, fill=c[\"color\"], color=c[\"color\"], alpha=0.22, size=1.4\n    )\n\n# Item labels (serif, INK)\nplot = plot + geom_text(aes(x=\"x\", y=\"y\", label=\"label\"), data=items_df, size=8, family=\"serif\", color=INK)\n\n# Category names outside each circle, in the circle's color\nplot = plot + geom_text(\n    x=-2.40, y=1.95, label=\"Overhyped\", size=13, family=\"serif\", fontface=\"bold\", color=COLOR_A, hjust=1.0\n)\nplot = plot + geom_text(\n    x=2.40, y=1.95, label=\"Actually Useful\", size=13, family=\"serif\", fontface=\"bold\", color=COLOR_B, hjust=0.0\n)\nplot = plot + geom_text(\n    x=0.00, y=-2.85, label=\"Secretly Loved\", size=13, family=\"serif\", fontface=\"bold\", color=COLOR_C, hjust=0.5\n)\n\n# Hint label for the \"outside\" item cluster\nplot = plot + geom_text(\n    x=-3.10,\n    y=-2.20,\n    label=\"(neither here nor there)\",\n    size=6,\n    family=\"serif\",\n    fontface=\"italic\",\n    color=INK_MUTED,\n    hjust=0.5,\n)\n\n# Editorial kicker + canonical anyplot.ai title line\nplot = plot + geom_text(\n    x=0, y=3.20, label=\"Chartgeist 2026\", size=18, family=\"serif\", fontface=\"bold_italic\", color=INK, hjust=0.5\n)\nplot = plot + geom_text(\n    x=0, y=2.70, label=\"venn-labeled-items · letsplot · anyplot.ai\", size=7, family=\"serif\", color=INK_MUTED, hjust=0.5\n)\n\n# Theme — gridless editorial background\nplot = (\n    plot\n    + theme_void()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        legend_position=\"none\",\n        axis_text=element_blank(),\n        axis_title=element_blank(),\n        axis_ticks=element_blank(),\n    )\n)\n\nplot = plot + ggsize(1200, 1200)\n\n# Save\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}