Skip to content

Commit 9d02e46

Browse files
feat(bokeh): implement venn-labeled-items (#5376)
## Implementation: `venn-labeled-items` - python/bokeh Implements the **python/bokeh** version of `venn-labeled-items`. **File:** `plots/venn-labeled-items/implementations/python/bokeh.py` **Parent Issue:** #5364 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/24923336606)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 23fc2d6 commit 9d02e46

2 files changed

Lines changed: 423 additions & 0 deletions

File tree

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
""" anyplot.ai
2+
venn-labeled-items: Chartgeist-Style Venn Diagram with Labeled Items
3+
Library: bokeh 3.9.0 | Python 3.14.4
4+
Quality: 86/100 | Created: 2026-04-25
5+
"""
6+
7+
import os
8+
9+
import numpy as np
10+
from bokeh.io import export_png, output_file, save
11+
from bokeh.models import Label
12+
from bokeh.plotting import figure
13+
14+
15+
# Theme tokens
16+
THEME = os.getenv("ANYPLOT_THEME", "light")
17+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
18+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
19+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
20+
21+
# Okabe-Ito palette — first circle is brand green, then vermillion, then blue
22+
COLOR_A = "#009E73" # Overhyped
23+
COLOR_B = "#D55E00" # Actually Useful
24+
COLOR_C = "#0072B2" # Secretly Loved
25+
26+
# Geometry — equilateral triangle of centers (apex pointing down)
27+
r = 1.0
28+
s = 1.1
29+
h_low = s * np.sqrt(3) / 6
30+
center_a = (-s / 2, h_low)
31+
center_b = (s / 2, h_low)
32+
center_c = (0.0, -2 * h_low)
33+
34+
# Data — circle categories with editorial labels positioned outside each circle
35+
circles = [
36+
{"name": "Overhyped", "color": COLOR_A, "center": center_a, "label_xy": (-1.65, 1.45), "align": "right"},
37+
{"name": "Actually Useful", "color": COLOR_B, "center": center_b, "label_xy": (1.65, 1.45), "align": "left"},
38+
{"name": "Secretly Loved", "color": COLOR_C, "center": center_c, "label_xy": (0.0, -1.92), "align": "center"},
39+
]
40+
41+
# Items distributed across the seven interior zones
42+
items = [
43+
# A only — Overhyped, neither useful nor loved
44+
{"label": "NFTs", "x": -1.42, "y": 0.88},
45+
{"label": "Metaverse", "x": -1.50, "y": 0.32},
46+
{"label": "Web3", "x": -1.30, "y": -0.05},
47+
# B only — Useful, neither overhyped nor loved
48+
{"label": "Google Maps", "x": 1.42, "y": 0.88},
49+
{"label": "Sticky Notes", "x": 1.40, "y": 0.30},
50+
# C only — Loved, neither overhyped nor useful
51+
{"label": "Karaoke", "x": -0.45, "y": -1.30},
52+
{"label": "Postcards", "x": 0.45, "y": -1.30},
53+
# AB — Overhyped + Useful
54+
{"label": "Smartphones", "x": 0.00, "y": 0.92},
55+
{"label": "Email", "x": 0.00, "y": 0.62},
56+
# AC — Overhyped + Loved
57+
{"label": "Crocs", "x": -0.78, "y": -0.18},
58+
{"label": "Pumpkin Spice", "x": -0.55, "y": -0.50},
59+
# BC — Useful + Loved
60+
{"label": "Spotify", "x": 0.78, "y": -0.18},
61+
{"label": "Dolly Parton", "x": 0.55, "y": -0.50},
62+
# ABC — all three
63+
{"label": "Sourdough", "x": 0.00, "y": 0.10},
64+
{"label": "TikTok", "x": 0.00, "y": -0.20},
65+
]
66+
67+
# Plot — square canvas suits the radial Venn layout
68+
p = figure(
69+
width=3600,
70+
height=3600,
71+
title="Tech Vibes 2026 · venn-labeled-items · bokeh · anyplot.ai",
72+
x_range=(-2.7, 2.7),
73+
y_range=(-2.7, 2.7),
74+
toolbar_location=None,
75+
tools="",
76+
match_aspect=True,
77+
)
78+
79+
# Three semi-transparent circles
80+
for circle in circles:
81+
cx, cy = circle["center"]
82+
p.ellipse(
83+
x=cx,
84+
y=cy,
85+
width=2 * r,
86+
height=2 * r,
87+
fill_color=circle["color"],
88+
fill_alpha=0.22,
89+
line_color=circle["color"],
90+
line_width=4,
91+
line_alpha=0.85,
92+
)
93+
94+
# Category names outside each circle, in the circle's own color
95+
for circle in circles:
96+
lx, ly = circle["label_xy"]
97+
p.add_layout(
98+
Label(
99+
x=lx,
100+
y=ly,
101+
text=circle["name"],
102+
text_font="serif",
103+
text_font_size="64pt",
104+
text_font_style="italic",
105+
text_color=circle["color"],
106+
text_align=circle["align"],
107+
text_baseline="middle",
108+
)
109+
)
110+
111+
# Items as text-only placements inside their assigned zones
112+
for item in items:
113+
p.add_layout(
114+
Label(
115+
x=item["x"],
116+
y=item["y"],
117+
text=item["label"],
118+
text_font="serif",
119+
text_font_size="38pt",
120+
text_color=INK,
121+
text_align="center",
122+
text_baseline="middle",
123+
)
124+
)
125+
126+
# Editorial subtitle anchored under the diagram
127+
p.add_layout(
128+
Label(
129+
x=0.0,
130+
y=-2.35,
131+
text="A field guide to fifteen things, three feelings, and seven overlapping truths",
132+
text_font="serif",
133+
text_font_size="30pt",
134+
text_font_style="italic",
135+
text_color=INK_SOFT,
136+
text_align="center",
137+
text_baseline="middle",
138+
)
139+
)
140+
141+
# Style — gridless editorial chrome
142+
p.background_fill_color = PAGE_BG
143+
p.border_fill_color = PAGE_BG
144+
p.outline_line_color = None
145+
146+
p.title.text_font = "serif"
147+
p.title.text_font_size = "54pt"
148+
p.title.text_font_style = "normal"
149+
p.title.text_color = INK
150+
p.title.align = "center"
151+
152+
p.axis.visible = False
153+
p.grid.visible = False
154+
155+
# Save
156+
export_png(p, filename=f"plot-{THEME}.png")
157+
output_file(f"plot-{THEME}.html")
158+
save(p)

0 commit comments

Comments
 (0)