Skip to content

Commit bc6097f

Browse files
feat(bokeh): implement streamline-basic (#2914)
## Implementation: `streamline-basic` - bokeh Implements the **bokeh** version of `streamline-basic`. **File:** `plots/streamline-basic/implementations/bokeh.py` **Parent Issue:** #2861 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20608706242)* --------- 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 93d1758 commit bc6097f

2 files changed

Lines changed: 174 additions & 0 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
""" pyplots.ai
2+
streamline-basic: Basic Streamline Plot
3+
Library: bokeh 3.8.1 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
import numpy as np
8+
from bokeh.io import export_png, save
9+
from bokeh.plotting import figure
10+
from bokeh.resources import CDN
11+
from scipy.interpolate import RegularGridInterpolator
12+
13+
14+
# Seed for reproducibility
15+
np.random.seed(42)
16+
17+
# Grid setup
18+
x = np.linspace(-3, 3, 40)
19+
y = np.linspace(-3, 3, 40)
20+
X, Y = np.meshgrid(x, y)
21+
22+
# Vortex flow field: u = -y, v = x (creates circular streamlines)
23+
U = -Y
24+
V = X
25+
26+
# Compute velocity magnitude for coloring
27+
magnitude = np.sqrt(U**2 + V**2)
28+
29+
# Create interpolators for the vector field
30+
u_interp = RegularGridInterpolator((y, x), U, bounds_error=False, fill_value=None)
31+
v_interp = RegularGridInterpolator((y, x), V, bounds_error=False, fill_value=None)
32+
mag_interp = RegularGridInterpolator((y, x), magnitude, bounds_error=False, fill_value=None)
33+
34+
# Seed points for streamlines in a grid pattern
35+
seed_x = np.linspace(-2.5, 2.5, 8)
36+
seed_y = np.linspace(-2.5, 2.5, 8)
37+
38+
# Storage for streamline data
39+
all_xs = []
40+
all_ys = []
41+
all_colors = []
42+
43+
# Compute streamlines from seed points
44+
for sx in seed_x:
45+
for sy in seed_y:
46+
# Trace streamline using Euler integration
47+
xs, ys, mags = [sx], [sy], []
48+
px, py = sx, sy
49+
dt, max_steps = 0.05, 300
50+
51+
# Get initial magnitude
52+
m = mag_interp([[py, px]])[0]
53+
if m is None or np.isnan(m):
54+
continue
55+
mags.append(m)
56+
57+
for _ in range(max_steps):
58+
u_val = u_interp([[py, px]])[0]
59+
v_val = v_interp([[py, px]])[0]
60+
61+
if u_val is None or v_val is None or np.isnan(u_val) or np.isnan(v_val):
62+
break
63+
64+
speed = np.sqrt(u_val**2 + v_val**2)
65+
if speed < 1e-6:
66+
break
67+
68+
# Normalize and step
69+
px += u_val / speed * dt
70+
py += v_val / speed * dt
71+
72+
# Check bounds
73+
if px < x.min() or px > x.max() or py < y.min() or py > y.max():
74+
break
75+
76+
xs.append(px)
77+
ys.append(py)
78+
m = mag_interp([[py, px]])[0]
79+
if m is None or np.isnan(m):
80+
break
81+
mags.append(m)
82+
83+
# Store if streamline is long enough
84+
if len(xs) >= 5:
85+
all_xs.append(np.array(xs))
86+
all_ys.append(np.array(ys))
87+
# Color based on average magnitude (blue to yellow gradient)
88+
avg_mag = np.mean(mags)
89+
t = np.clip(avg_mag / 3.0, 0, 1)
90+
r = int(48 + t * (255 - 48))
91+
g = int(105 + t * (212 - 105))
92+
b = int(152 + t * (59 - 152))
93+
all_colors.append(f"#{r:02x}{g:02x}{b:02x}")
94+
95+
# Create figure
96+
p = figure(
97+
width=4800,
98+
height=2700,
99+
title="streamline-basic · bokeh · pyplots.ai",
100+
x_axis_label="X Position",
101+
y_axis_label="Y Position",
102+
x_range=(-3.5, 3.5),
103+
y_range=(-3.5, 3.5),
104+
)
105+
106+
# Style title and axes for large canvas
107+
p.title.text_font_size = "32pt"
108+
p.xaxis.axis_label_text_font_size = "26pt"
109+
p.yaxis.axis_label_text_font_size = "26pt"
110+
p.xaxis.major_label_text_font_size = "20pt"
111+
p.yaxis.major_label_text_font_size = "20pt"
112+
113+
# Grid styling
114+
p.grid.grid_line_alpha = 0.3
115+
p.grid.grid_line_dash = [6, 4]
116+
117+
# Draw streamlines with direction arrows
118+
for xs, ys, color in zip(all_xs, all_ys, all_colors, strict=True):
119+
p.line(xs, ys, line_width=4, line_color=color, line_alpha=0.85)
120+
121+
# Add arrowhead at the end to show flow direction
122+
if len(xs) >= 2:
123+
dx = xs[-1] - xs[-2]
124+
dy = ys[-1] - ys[-2]
125+
length = np.sqrt(dx**2 + dy**2)
126+
if length > 0:
127+
dx /= length
128+
dy /= length
129+
arrow_size = 0.18
130+
tip_x, tip_y = xs[-1], ys[-1]
131+
wing1_x = tip_x - arrow_size * (dx + 0.5 * dy)
132+
wing1_y = tip_y - arrow_size * (dy - 0.5 * dx)
133+
wing2_x = tip_x - arrow_size * (dx - 0.5 * dy)
134+
wing2_y = tip_y - arrow_size * (dy + 0.5 * dx)
135+
p.patch(
136+
[tip_x, wing1_x, wing2_x], [tip_y, wing1_y, wing2_y], fill_color=color, line_color=color, fill_alpha=0.9
137+
)
138+
139+
# Background
140+
p.background_fill_color = "#fafafa"
141+
142+
# Save outputs
143+
export_png(p, filename="plot.png")
144+
save(p, filename="plot.html", resources=CDN, title="Streamline Plot")
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
library: bokeh
2+
specification_id: streamline-basic
3+
created: '2025-12-31T00:17:11Z'
4+
updated: '2025-12-31T00:21:45Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20608706242
7+
issue: 2861
8+
python_version: 3.13.11
9+
library_version: 3.8.1
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/streamline-basic/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/streamline-basic/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/streamline-basic/bokeh/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent implementation of streamline tracing using Euler integration with proper
17+
handling of bounds and speed thresholds
18+
- Beautiful color gradient from teal to yellow representing velocity magnitude -
19+
visually appealing and informative
20+
- Arrowheads at streamline ends clearly indicate flow direction
21+
- Proper canvas sizing (4800x2700) with appropriately scaled text elements
22+
- Clean, well-structured code that is easy to understand
23+
- Outputs both PNG and HTML for static and interactive viewing
24+
weaknesses:
25+
- Axis labels lack units (could use "X Position (arbitrary units)" or similar)
26+
- Could demonstrate more varied flow patterns to showcase streamline capabilities
27+
beyond simple vortex
28+
- Does not leverage Bokeh's ColumnDataSource for data organization
29+
- No interactive features (hover tooltips showing velocity magnitude would enhance
30+
the Bokeh implementation)

0 commit comments

Comments
 (0)