Skip to content

Commit 2d11432

Browse files
feat(altair): implement surface-basic (#1306)
## Implementation: `surface-basic` - altair Implements the **altair** version of `surface-basic`. **File:** `plots/surface-basic/implementations/altair.py` **Parent Issue:** #1013 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20304617361)* --------- 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 003d59b commit 2d11432

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""
2+
surface-basic: Basic 3D Surface Plot
3+
Library: altair
4+
"""
5+
6+
import altair as alt
7+
import numpy as np
8+
import pandas as pd
9+
10+
11+
# Data - create a smooth surface z = sin(x) * cos(y)
12+
np.random.seed(42)
13+
14+
# Grid setup - 40x40 for smooth surface
15+
n_points = 40
16+
x = np.linspace(-3, 3, n_points)
17+
y = np.linspace(-3, 3, n_points)
18+
X, Y = np.meshgrid(x, y)
19+
20+
# Surface function
21+
Z = np.sin(X) * np.cos(Y)
22+
23+
# 3D to 2D projection (elevation=25, azimuth=45)
24+
elev_rad = np.radians(25)
25+
azim_rad = np.radians(45)
26+
27+
# Rotation around z-axis (azimuth)
28+
X_rot = X * np.cos(azim_rad) - Y * np.sin(azim_rad)
29+
Y_rot = X * np.sin(azim_rad) + Y * np.cos(azim_rad)
30+
31+
# Rotation around x-axis (elevation) and project
32+
X_proj = X_rot
33+
Z_proj = Y_rot * np.sin(elev_rad) + Z * np.cos(elev_rad)
34+
35+
# Build rectangles using actual quad corners for proper filling
36+
rect_data = []
37+
for i in range(n_points - 1):
38+
for j in range(n_points - 1):
39+
# Use actual quad corners for x and y extents
40+
x1 = min(X_proj[i, j], X_proj[i, j + 1], X_proj[i + 1, j], X_proj[i + 1, j + 1])
41+
x2 = max(X_proj[i, j], X_proj[i, j + 1], X_proj[i + 1, j], X_proj[i + 1, j + 1])
42+
y1 = min(Z_proj[i, j], Z_proj[i, j + 1], Z_proj[i + 1, j], Z_proj[i + 1, j + 1])
43+
y2 = max(Z_proj[i, j], Z_proj[i, j + 1], Z_proj[i + 1, j], Z_proj[i + 1, j + 1])
44+
45+
# Average z for color (original Z, not projected)
46+
avg_z = (Z[i, j] + Z[i, j + 1] + Z[i + 1, j + 1] + Z[i + 1, j]) / 4
47+
48+
# Depth for sorting (painter's algorithm - back to front)
49+
depth = (Y_rot[i, j] + Y_rot[i, j + 1] + Y_rot[i + 1, j + 1] + Y_rot[i + 1, j]) / 4
50+
51+
rect_data.append({"x1": x1, "x2": x2, "y1": y1, "y2": y2, "z": avg_z, "depth": depth})
52+
53+
# Sort by depth (back to front - painter's algorithm)
54+
rect_data.sort(key=lambda r: r["depth"], reverse=True)
55+
56+
# Assign order for rendering
57+
for idx, r in enumerate(rect_data):
58+
r["order"] = idx
59+
60+
df_rects = pd.DataFrame(rect_data)
61+
62+
# Create surface chart with filled rectangles
63+
surface = (
64+
alt.Chart(df_rects)
65+
.mark_rect(strokeWidth=0.5, stroke="#306998", strokeOpacity=0.3)
66+
.encode(
67+
x=alt.X("x1:Q", axis=alt.Axis(title="X (projected)", labelFontSize=18, titleFontSize=22)),
68+
y=alt.Y("y1:Q", axis=alt.Axis(title="Z (height)", labelFontSize=18, titleFontSize=22)),
69+
x2=alt.X2("x2:Q"),
70+
y2=alt.Y2("y2:Q"),
71+
color=alt.Color(
72+
"z:Q",
73+
scale=alt.Scale(scheme="viridis"),
74+
legend=alt.Legend(title="Z Value", titleFontSize=20, labelFontSize=16),
75+
),
76+
order=alt.Order("order:Q"),
77+
)
78+
)
79+
80+
# Combine into final chart
81+
chart = (
82+
surface.properties(width=1600, height=900, title=alt.Title("surface-basic · altair · pyplots.ai", fontSize=28))
83+
.configure_axis(grid=True, gridOpacity=0.3, gridDash=[6, 4])
84+
.configure_view(strokeWidth=0)
85+
)
86+
87+
# Save outputs
88+
chart.save("plot.png", scale_factor=3.0)
89+
chart.save("plot.html")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Per-library metadata for altair implementation of surface-basic
2+
# Auto-generated by impl-generate.yml
3+
4+
library: altair
5+
specification_id: surface-basic
6+
7+
# Preview URLs (filled by workflow)
8+
preview_url: https://storage.googleapis.com/pyplots-images/plots/surface-basic/altair/plot.png
9+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/surface-basic/altair/plot_thumb.png
10+
preview_html: https://storage.googleapis.com/pyplots-images/plots/surface-basic/altair/plot.html
11+
12+
current:
13+
version: 0
14+
generated_at: 2025-12-17T13:38:02Z
15+
generated_by: claude-opus-4-5-20251101
16+
workflow_run: 20304617361
17+
issue: 1013
18+
quality_score: 91
19+
# Version info (filled by workflow)
20+
python_version: "3.13.11"
21+
library_version: "unknown"
22+
23+
history: []

0 commit comments

Comments
 (0)