Skip to content

Commit ba2935c

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

2 files changed

Lines changed: 123 additions & 0 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
""" pyplots.ai
2+
streamline-basic: Basic Streamline Plot
3+
Library: altair 6.0.0 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
import altair as alt
8+
import numpy as np
9+
import pandas as pd
10+
11+
12+
# Disable data row limit
13+
alt.data_transformers.disable_max_rows()
14+
15+
# Data - Create a vector field for a vortex flow (u = -y, v = x)
16+
np.random.seed(42)
17+
18+
# Generate streamlines using Euler integration - flat KISS structure
19+
streamlines_data = []
20+
streamline_id = 0
21+
22+
# Starting points at different radii for vortex visualization
23+
radii = [0.4, 0.7, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0]
24+
n_per_radius = 6
25+
dt = 0.03
26+
max_steps = 250
27+
28+
for r in radii:
29+
for i in range(n_per_radius):
30+
angle = 2 * np.pi * i / n_per_radius + (r * 0.1)
31+
x = r * np.cos(angle)
32+
y = r * np.sin(angle)
33+
points = [(x, y)]
34+
35+
# Trace streamline using Euler integration
36+
for _ in range(max_steps):
37+
# Vector field: circular vortex (u = -y, v = x)
38+
u = -y
39+
v = x
40+
mag = np.sqrt(u**2 + v**2)
41+
if mag < 1e-6:
42+
break
43+
# Normalize and step
44+
x_new = x + dt * u / mag
45+
y_new = y + dt * v / mag
46+
# Stop if out of bounds
47+
if abs(x_new) > 3.2 or abs(y_new) > 3.2:
48+
break
49+
x, y = x_new, y_new
50+
points.append((x, y))
51+
52+
# Only include streamlines with enough points
53+
if len(points) > 5:
54+
for j, (px, py) in enumerate(points):
55+
# Velocity magnitude equals distance from center in this vortex
56+
vel = np.sqrt(px**2 + py**2)
57+
streamlines_data.append(
58+
{"x": float(px), "y": float(py), "streamline_id": streamline_id, "order": j, "velocity": float(vel)}
59+
)
60+
streamline_id += 1
61+
62+
df = pd.DataFrame(streamlines_data)
63+
64+
# Compute average velocity per streamline for color encoding
65+
avg_velocity = df.groupby("streamline_id")["velocity"].mean().reset_index()
66+
avg_velocity.columns = ["streamline_id", "avg_velocity"]
67+
df = df.merge(avg_velocity, on="streamline_id")
68+
69+
# Create the streamline chart using line marks
70+
# Color by average velocity (flow speed) for each streamline
71+
chart = (
72+
alt.Chart(df)
73+
.mark_line(strokeWidth=2.5, opacity=0.85)
74+
.encode(
75+
x=alt.X("x:Q", title="X Position (units)", scale=alt.Scale(domain=[-3.5, 3.5])),
76+
y=alt.Y("y:Q", title="Y Position (units)", scale=alt.Scale(domain=[-3.5, 3.5])),
77+
color=alt.Color(
78+
"avg_velocity:Q",
79+
scale=alt.Scale(scheme="viridis"),
80+
title="Flow Speed",
81+
legend=alt.Legend(titleFontSize=18, labelFontSize=16, gradientLength=200),
82+
),
83+
detail="streamline_id:N",
84+
order="order:O",
85+
)
86+
.properties(
87+
width=1600, height=900, title=alt.Title("streamline-basic · altair · pyplots.ai", fontSize=28, anchor="middle")
88+
)
89+
.configure_axis(labelFontSize=18, titleFontSize=22, grid=True, gridColor="#cccccc", gridOpacity=0.4)
90+
.configure_view(strokeWidth=0)
91+
)
92+
93+
# Save as PNG and HTML
94+
chart.save("plot.png", scale_factor=3.0)
95+
chart.save("plot.html")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library: altair
2+
specification_id: streamline-basic
3+
created: '2025-12-31T00:18:39Z'
4+
updated: '2025-12-31T00:28:21Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20608708674
7+
issue: 2861
8+
python_version: 3.13.11
9+
library_version: 6.0.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/streamline-basic/altair/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/streamline-basic/altair/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/streamline-basic/altair/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent implementation of Euler integration to trace streamlines in a declarative
17+
visualization library
18+
- Clean viridis color encoding showing velocity magnitude increases with distance
19+
from center
20+
- Proper use of detail encoding to separate individual streamlines and order encoding
21+
for correct line drawing
22+
- Well-balanced streamline density with multiple radii covering the full flow field
23+
- Title and font sizes meet all readability requirements
24+
weaknesses:
25+
- Axis labels lack units - should be "X Position (units)" or specify dimensionless
26+
- Some inner streamlines appear slightly crowded/overlapping near the center
27+
- Does not leverage Altair interactive features (tooltips, zoom/pan) which would
28+
enhance flow exploration

0 commit comments

Comments
 (0)