Skip to content

Commit de212fc

Browse files
feat(letsplot): implement line-parametric (#5073)
## Implementation: `line-parametric` - letsplot Implements the **letsplot** version of `line-parametric`. **File:** `plots/line-parametric/implementations/letsplot.py` **Parent Issue:** #4424 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/23339035363)* --------- 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 715d8f6 commit de212fc

2 files changed

Lines changed: 312 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
""" pyplots.ai
2+
line-parametric: Parametric Curve Plot
3+
Library: letsplot 4.9.0 | Python 3.14.3
4+
Quality: 90/100 | Created: 2026-03-20
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from lets_plot import *
10+
11+
12+
LetsPlot.setup_html()
13+
14+
# Data - Lissajous figure: x = sin(3t), y = sin(2t)
15+
t_lissajous = np.linspace(0, 2 * np.pi, 1000)
16+
x_lissajous = np.sin(3 * t_lissajous)
17+
y_lissajous = np.sin(2 * t_lissajous)
18+
19+
df_lissajous = pd.DataFrame({"x": x_lissajous, "y": y_lissajous, "t": t_lissajous})
20+
21+
# Data - Spiral: x = t*cos(t), y = t*sin(t)
22+
t_spiral = np.linspace(0, 4 * np.pi, 1000)
23+
x_spiral = t_spiral * np.cos(t_spiral)
24+
y_spiral = t_spiral * np.sin(t_spiral)
25+
26+
df_spiral = pd.DataFrame({"x": x_spiral, "y": y_spiral, "t": t_spiral})
27+
28+
# Refined theme - remove spines, subtle grid, generous whitespace
29+
refined_theme = theme(
30+
axis_text=element_text(size=16, color="#555555"),
31+
axis_title=element_text(size=20, color="#333333"),
32+
plot_title=element_text(size=22, face="bold", color="#1a1a2e"),
33+
legend_text=element_text(size=16),
34+
legend_title=element_text(size=16, face="bold"),
35+
panel_grid_major=element_line(color="#e8e8e8", size=0.5),
36+
panel_grid_minor=element_blank(),
37+
axis_line=element_blank(),
38+
axis_ticks=element_blank(),
39+
plot_background=element_rect(fill="white", color="white"),
40+
panel_background=element_rect(fill="#fcfcfc", color="#e0e0e0", size=0.5),
41+
legend_background=element_rect(fill="white", color="#e0e0e0", size=0.5),
42+
)
43+
44+
# Colorblind-safe markers: dark blue (start) and orange (end)
45+
start_color = "#084C4C"
46+
end_color = "#C0392B"
47+
48+
# Plot - Lissajous figure (closed, self-intersecting - the "complex" curve)
49+
plot_lissajous = (
50+
ggplot(df_lissajous, aes(x="x", y="y", color="t"))
51+
+ geom_path(size=2.0, alpha=0.85, tooltips=layer_tooltips().line("t = @t").format("t", ".2f"))
52+
+ geom_point(data=df_lissajous.iloc[[0]], mapping=aes(x="x", y="y"), color=start_color, size=8, shape=16)
53+
+ geom_point(data=df_lissajous.iloc[[-1]], mapping=aes(x="x", y="y"), color=end_color, size=8, shape=17)
54+
+ scale_color_gradient(low="#0D6E6E", high="#D4A017", name="t (rad)", format=".1f")
55+
+ coord_fixed()
56+
+ labs(x="x(t) = sin(3t)", y="y(t) = sin(2t)", title="Lissajous Figure · closed, self-intersecting")
57+
+ refined_theme
58+
)
59+
60+
# Plot - Archimedean spiral (open, expanding - the "growth" curve)
61+
plot_spiral = (
62+
ggplot(df_spiral, aes(x="x", y="y", color="t"))
63+
+ geom_path(size=1.8, alpha=0.85, tooltips=layer_tooltips().line("t = @t").format("t", ".2f"))
64+
+ geom_point(data=df_spiral.iloc[[0]], mapping=aes(x="x", y="y"), color=start_color, size=8, shape=16)
65+
+ geom_point(data=df_spiral.iloc[[-1]], mapping=aes(x="x", y="y"), color=end_color, size=8, shape=17)
66+
+ scale_color_gradient(low="#0D6E6E", high="#D4A017", name="t (rad)", format=".1f")
67+
+ coord_fixed()
68+
+ scale_x_continuous(limits=[-14, 14])
69+
+ scale_y_continuous(limits=[-14, 14])
70+
+ labs(x="x(t) = t·cos(t)", y="y(t) = t·sin(t)", title="Archimedean Spiral · expanding outward")
71+
+ refined_theme
72+
)
73+
74+
# Combine with gggrid - letsplot's distinctive multi-plot layout
75+
grid_plot = gggrid([plot_lissajous, plot_spiral], ncol=2)
76+
77+
final_plot = (
78+
grid_plot
79+
+ ggsize(1600, 900)
80+
+ ggtitle("line-parametric · letsplot · pyplots.ai")
81+
+ theme(plot_title=element_text(size=24, face="bold", color="#1a1a2e"))
82+
)
83+
84+
# Save
85+
ggsave(final_plot, "plot.png", path=".", scale=3)
86+
ggsave(final_plot, "plot.html", path=".")
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
library: letsplot
2+
specification_id: line-parametric
3+
created: '2026-03-20T10:36:59Z'
4+
updated: '2026-03-20T10:57:06Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 23339035363
7+
issue: 4424
8+
python_version: 3.14.3
9+
library_version: 4.9.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/line-parametric/letsplot/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/line-parametric/letsplot/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/line-parametric/letsplot/plot.html
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- 'Excellent spec compliance with all required features: equal aspect ratio, color
17+
gradient, two curves, start/end markers'
18+
- Strong visual refinement with removed spines, subtle grid, custom teal-to-gold
19+
palette, off-white panel background
20+
- Good data storytelling through descriptive subtitles and contrasting curve types
21+
(closed vs open)
22+
- 'Effective use of letsplot-specific features: gggrid, layer_tooltips, HTML export'
23+
- Clean deterministic code with explicit font sizing throughout
24+
weaknesses:
25+
- Start/end markers on Lissajous curve overlap at origin since the curve is closed
26+
- Spiral panel has slightly more whitespace than ideal due to fixed axis limits
27+
image_description: The plot displays two side-by-side panels on a white background
28+
with an overall title "line-parametric · letsplot · pyplots.ai" at the top. The
29+
left panel shows a Lissajous figure titled "Lissajous Figure · closed, self-intersecting"
30+
with x(t) = sin(3t) on the x-axis and y(t) = sin(2t) on the y-axis. The curve
31+
traces a self-intersecting figure-eight-like pattern with a color gradient from
32+
dark teal (t=0) to golden yellow (t≈6.3). A dark teal circle marks the start and
33+
a red triangle marks the end, both at the origin since the curve is closed. The
34+
right panel shows an "Archimedean Spiral · expanding outward" with x(t) = t·cos(t)
35+
and y(t) = t·sin(t). The spiral expands from center outward, colored from dark
36+
teal to gold. Start (dark circle) is at origin, end (red triangle) at the outer
37+
edge around x≈12. Both panels have light gray subtle grids, no axis spines or
38+
ticks, and separate "t (rad)" color legends. Axis text is gray, titles are bold
39+
dark.
40+
criteria_checklist:
41+
visual_quality:
42+
score: 28
43+
max: 30
44+
items:
45+
- id: VQ-01
46+
name: Text Legibility
47+
score: 8
48+
max: 8
49+
passed: true
50+
comment: 'All font sizes explicitly set: title 22-24pt, axis titles 20pt,
51+
axis text 16pt, legend text 16pt'
52+
- id: VQ-02
53+
name: No Overlap
54+
score: 6
55+
max: 6
56+
passed: true
57+
comment: No overlapping text, legends positioned outside panels
58+
- id: VQ-03
59+
name: Element Visibility
60+
score: 5
61+
max: 6
62+
passed: true
63+
comment: Curves well visible; start/end markers on Lissajous overlap at origin
64+
since curve is closed
65+
- id: VQ-04
66+
name: Color Accessibility
67+
score: 4
68+
max: 4
69+
passed: true
70+
comment: Teal-to-gold gradient is colorblind-safe; markers use both color
71+
and shape differentiation
72+
- id: VQ-05
73+
name: Layout & Canvas
74+
score: 3
75+
max: 4
76+
passed: true
77+
comment: Good two-panel layout; spiral panel has slight extra whitespace from
78+
fixed axis limits
79+
- id: VQ-06
80+
name: Axis Labels & Title
81+
score: 2
82+
max: 2
83+
passed: true
84+
comment: Descriptive labels with mathematical notation
85+
design_excellence:
86+
score: 15
87+
max: 20
88+
items:
89+
- id: DE-01
90+
name: Aesthetic Sophistication
91+
score: 6
92+
max: 8
93+
passed: true
94+
comment: Custom teal-to-gold palette, intentional text color hierarchy, off-white
95+
panel background
96+
- id: DE-02
97+
name: Visual Refinement
98+
score: 5
99+
max: 6
100+
passed: true
101+
comment: Spines removed, subtle grid, minor grid blank, refined panel and
102+
legend borders
103+
- id: DE-03
104+
name: Data Storytelling
105+
score: 4
106+
max: 6
107+
passed: true
108+
comment: Descriptive subtitles provide narrative; contrasting curve types
109+
create natural comparison
110+
spec_compliance:
111+
score: 15
112+
max: 15
113+
items:
114+
- id: SC-01
115+
name: Plot Type
116+
score: 5
117+
max: 5
118+
passed: true
119+
comment: Correct parametric curve plot with two distinct curves
120+
- id: SC-02
121+
name: Required Features
122+
score: 4
123+
max: 4
124+
passed: true
125+
comment: 'All spec requirements met: equal aspect ratio, color gradient, two
126+
curves, start/end markers, 1000 points'
127+
- id: SC-03
128+
name: Data Mapping
129+
score: 3
130+
max: 3
131+
passed: true
132+
comment: X/Y correctly computed from parametric equations
133+
- id: SC-04
134+
name: Title & Legend
135+
score: 3
136+
max: 3
137+
passed: true
138+
comment: Correct title format and descriptive legend labels
139+
data_quality:
140+
score: 14
141+
max: 15
142+
items:
143+
- id: DQ-01
144+
name: Feature Coverage
145+
score: 6
146+
max: 6
147+
passed: true
148+
comment: Shows closed self-intersecting and open expanding curves
149+
- id: DQ-02
150+
name: Realistic Context
151+
score: 4
152+
max: 5
153+
passed: true
154+
comment: Well-known mathematical curves used in physics and engineering
155+
- id: DQ-03
156+
name: Appropriate Scale
157+
score: 4
158+
max: 4
159+
passed: true
160+
comment: Mathematically exact values for both curves
161+
code_quality:
162+
score: 10
163+
max: 10
164+
items:
165+
- id: CQ-01
166+
name: KISS Structure
167+
score: 3
168+
max: 3
169+
passed: true
170+
comment: Clean linear flow with no functions or classes
171+
- id: CQ-02
172+
name: Reproducibility
173+
score: 2
174+
max: 2
175+
passed: true
176+
comment: Fully deterministic trigonometric functions
177+
- id: CQ-03
178+
name: Clean Imports
179+
score: 2
180+
max: 2
181+
passed: true
182+
comment: All imports used
183+
- id: CQ-04
184+
name: Code Elegance
185+
score: 2
186+
max: 2
187+
passed: true
188+
comment: Clean and well-organized, appropriate complexity
189+
- id: CQ-05
190+
name: Output & API
191+
score: 1
192+
max: 1
193+
passed: true
194+
comment: Saves as plot.png with ggsave at scale=3
195+
library_mastery:
196+
score: 8
197+
max: 10
198+
items:
199+
- id: LM-01
200+
name: Idiomatic Usage
201+
score: 4
202+
max: 5
203+
passed: true
204+
comment: Proper ggplot grammar with letsplot idioms
205+
- id: LM-02
206+
name: Distinctive Features
207+
score: 4
208+
max: 5
209+
passed: true
210+
comment: Uses gggrid, layer_tooltips, and HTML export - distinctive letsplot
211+
features
212+
verdict: APPROVED
213+
impl_tags:
214+
dependencies: []
215+
techniques:
216+
- subplots
217+
- layer-composition
218+
- hover-tooltips
219+
- html-export
220+
patterns:
221+
- data-generation
222+
dataprep: []
223+
styling:
224+
- custom-colormap
225+
- alpha-blending
226+
- grid-styling

0 commit comments

Comments
 (0)