Skip to content

Commit 5bb7fcd

Browse files
update(arc-basic): bokeh — comprehensive quality review
Comprehensive review and update of bokeh implementation for arc-basic.
1 parent b1e8c8c commit 5bb7fcd

2 files changed

Lines changed: 103 additions & 71 deletions

File tree

plots/arc-basic/implementations/bokeh.py

Lines changed: 98 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
""" pyplots.ai
1+
"""pyplots.ai
22
arc-basic: Basic Arc Diagram
3-
Library: bokeh 3.8.1 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: bokeh 3.8.2 | Python 3.14.3
4+
Quality: /100 | Updated: 2026-02-23
55
"""
66

77
import numpy as np
88
from bokeh.io import export_png, save
9-
from bokeh.models import ColumnDataSource, Label, Legend, LegendItem
9+
from bokeh.models import ColumnDataSource, HoverTool, Label, Legend, LegendItem
1010
from bokeh.plotting import figure
1111

1212

1313
# Data - Character interactions in a story chapter
1414
nodes = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry"]
15-
# Edges as (source_idx, target_idx, weight) - character conversation connections
1615
edges = [
1716
(0, 1, 3), # Alice-Bob: frequent
1817
(0, 2, 2), # Alice-Carol: moderate
@@ -30,95 +29,128 @@
3029

3130
# Node positions along horizontal axis
3231
n_nodes = len(nodes)
33-
x_positions = np.linspace(0, 10, n_nodes)
32+
x_positions = np.linspace(0.5, 10.5, n_nodes)
3433
y_baseline = 0
3534

35+
# Weight-based styling: darker and thicker for stronger connections
36+
weight_colors = {1: "#93B5CF", 2: "#306998", 3: "#1A3F5C"}
37+
weight_labels = {1: "Brief", 2: "Moderate", 3: "Frequent"}
38+
3639
# Create figure
3740
p = figure(
3841
width=4800,
3942
height=2700,
40-
title="arc-basic · bokeh · pyplots.ai",
41-
x_axis_label="Characters",
42-
y_axis_label="",
43-
x_range=(-0.5, 10.5),
44-
y_range=(-1.5, 4.5),
43+
title="arc-basic \u00b7 bokeh \u00b7 pyplots.ai",
44+
x_range=(-0.2, 11.2),
45+
y_range=(-0.8, 3.3),
46+
toolbar_location=None,
4547
)
4648

47-
# Style the figure
49+
# Style
4850
p.title.text_font_size = "28pt"
49-
p.xaxis.axis_label_text_font_size = "22pt"
50-
p.yaxis.axis_label_text_font_size = "22pt"
51-
p.xaxis.major_label_text_font_size = "18pt"
52-
p.yaxis.major_label_text_font_size = "18pt"
53-
54-
# Hide y-axis (not meaningful for arc diagram)
51+
p.title.text_color = "#333333"
52+
p.xaxis.visible = False
5553
p.yaxis.visible = False
54+
p.xgrid.visible = False
5655
p.ygrid.visible = False
56+
p.outline_line_color = None
5757

58-
# Draw arcs as bezier curves, collect renderers for legend
59-
long_range_renderers = []
60-
short_range_renderers = []
58+
# Draw arcs using Bokeh's native bezier glyph
59+
arc_renderers_by_weight = {1: [], 2: [], 3: []}
6160

6261
for src_idx, tgt_idx, weight in edges:
6362
x_src = x_positions[src_idx]
6463
x_tgt = x_positions[tgt_idx]
6564

66-
# Arc height proportional to distance between nodes
6765
distance = abs(x_tgt - x_src)
68-
arc_height = distance * 0.5
66+
arc_height = distance * 0.35
6967

70-
# Generate arc points using quadratic bezier
71-
t = np.linspace(0, 1, 50)
72-
# Control point at midpoint, elevated by arc_height
73-
cx = (x_src + x_tgt) / 2
68+
# Split control points at 1/3 and 2/3 for smooth, rounded arcs
69+
cx0 = x_src + (x_tgt - x_src) / 3
70+
cx1 = x_src + 2 * (x_tgt - x_src) / 3
7471
cy = arc_height
7572

76-
# Quadratic bezier: B(t) = (1-t)^2*P0 + 2*(1-t)*t*P1 + t^2*P2
77-
arc_x = (1 - t) ** 2 * x_src + 2 * (1 - t) * t * cx + t**2 * x_tgt
78-
arc_y = (1 - t) ** 2 * y_baseline + 2 * (1 - t) * t * cy + t**2 * y_baseline
79-
80-
# Line width based on weight
81-
line_width = weight * 2
82-
83-
# Color based on connection type (long-range vs short-range)
84-
if distance > 5:
85-
color = "#FFD43B" # Python Yellow for long-range
86-
alpha = 0.7
87-
else:
88-
color = "#306998" # Python Blue for short-range
89-
alpha = 0.5
90-
91-
arc_source = ColumnDataSource(data={"x": arc_x, "y": arc_y})
92-
renderer = p.line(x="x", y="y", source=arc_source, line_width=line_width, line_color=color, line_alpha=alpha)
93-
94-
# Collect renderers for legend
95-
if distance > 5:
96-
long_range_renderers.append(renderer)
97-
else:
98-
short_range_renderers.append(renderer)
73+
line_width = weight * 2.5
74+
color = weight_colors[weight]
75+
alpha = 0.35 + weight * 0.15
76+
77+
arc_source = ColumnDataSource(
78+
data={
79+
"x0": [x_src],
80+
"y0": [y_baseline],
81+
"x1": [x_tgt],
82+
"y1": [y_baseline],
83+
"cx0": [cx0],
84+
"cy0": [cy],
85+
"cx1": [cx1],
86+
"cy1": [cy],
87+
"source_name": [nodes[src_idx]],
88+
"target_name": [nodes[tgt_idx]],
89+
"weight_label": [weight_labels[weight]],
90+
}
91+
)
92+
renderer = p.bezier(
93+
x0="x0",
94+
y0="y0",
95+
x1="x1",
96+
y1="y1",
97+
cx0="cx0",
98+
cy0="cy0",
99+
cx1="cx1",
100+
cy1="cy1",
101+
source=arc_source,
102+
line_width=line_width,
103+
line_color=color,
104+
line_alpha=alpha,
105+
)
106+
arc_renderers_by_weight[weight].append(renderer)
107+
108+
# HoverTool for edge details (Bokeh-distinctive interactivity)
109+
hover = HoverTool(
110+
tooltips=[("Connection", "@source_name \u2194 @target_name"), ("Frequency", "@weight_label")], line_policy="interp"
111+
)
112+
p.add_tools(hover)
99113

100-
# Draw nodes along baseline
114+
# Draw nodes
101115
node_source = ColumnDataSource(data={"x": x_positions, "y": [y_baseline] * n_nodes, "name": nodes})
102-
p.scatter(x="x", y="y", source=node_source, size=25, fill_color="#306998", line_color="white", line_width=2)
116+
p.scatter(x="x", y="y", source=node_source, size=30, fill_color="#306998", line_color="white", line_width=3)
117+
118+
# Node hover
119+
node_hover = HoverTool(tooltips=[("Character", "@name")], renderers=[p.renderers[-1]])
120+
p.add_tools(node_hover)
103121

104-
# Add node labels below the baseline
122+
# Node labels
105123
for i, name in enumerate(nodes):
106-
label = Label(x=x_positions[i], y=-0.5, text=name, text_font_size="20pt", text_align="center", text_baseline="top")
124+
label = Label(
125+
x=x_positions[i],
126+
y=-0.25,
127+
text=name,
128+
text_font_size="20pt",
129+
text_align="center",
130+
text_baseline="top",
131+
text_color="#333333",
132+
)
107133
p.add_layout(label)
108134

109-
# Add subtle grid only for x
110-
p.xgrid.grid_line_alpha = 0.3
111-
p.xgrid.grid_line_dash = [6, 4]
112-
113-
# Add legend for connection types
135+
# Legend by interaction frequency
114136
legend_items = []
115-
if long_range_renderers:
116-
legend_items.append(LegendItem(label="Long-range (distance > 5)", renderers=[long_range_renderers[0]]))
117-
if short_range_renderers:
118-
legend_items.append(LegendItem(label="Short-range (distance ≤ 5)", renderers=[short_range_renderers[0]]))
119-
120-
legend = Legend(items=legend_items, location="top_right", label_text_font_size="18pt")
121-
p.add_layout(legend, "right")
137+
for weight, label_text in [(3, "Frequent"), (2, "Moderate"), (1, "Brief")]:
138+
if arc_renderers_by_weight[weight]:
139+
legend_items.append(LegendItem(label=label_text, renderers=[arc_renderers_by_weight[weight][0]]))
140+
141+
legend = Legend(
142+
items=legend_items,
143+
location="top_right",
144+
label_text_font_size="20pt",
145+
label_text_color="#444444",
146+
border_line_color=None,
147+
background_fill_alpha=0.85,
148+
glyph_width=40,
149+
glyph_height=8,
150+
spacing=12,
151+
padding=18,
152+
)
153+
p.add_layout(legend)
122154

123155
# Save outputs
124156
export_png(p, filename="plot.png")

plots/arc-basic/metadata/bokeh.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
library: bokeh
22
specification_id: arc-basic
33
created: '2025-12-23T08:49:26Z'
4-
updated: '2025-12-23T09:11:15Z'
5-
generated_by: claude-opus-4-5-20251101
4+
updated: '2026-02-23T12:00:00+00:00'
5+
generated_by: claude-opus-4-6
66
workflow_run: 20455962917
77
issue: 0
8-
python_version: 3.13.11
9-
library_version: 3.8.1
8+
python_version: '3.14.3'
9+
library_version: 3.8.2
1010
preview_url: https://storage.googleapis.com/pyplots-images/plots/arc-basic/bokeh/plot.png
1111
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/arc-basic/bokeh/plot_thumb.png
1212
preview_html: https://storage.googleapis.com/pyplots-images/plots/arc-basic/bokeh/plot.html
13-
quality_score: 91
13+
quality_score: null
1414
impl_tags:
1515
dependencies: []
1616
techniques:

0 commit comments

Comments
 (0)