Skip to content

Commit b4e8d31

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

2 files changed

Lines changed: 352 additions & 0 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
""" pyplots.ai
2+
line-parametric: Parametric Curve Plot
3+
Library: plotnine 0.15.3 | Python 3.14.3
4+
Quality: 92/100 | Created: 2026-03-20
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from plotnine import (
10+
aes,
11+
coord_equal,
12+
element_blank,
13+
element_line,
14+
element_rect,
15+
element_text,
16+
facet_wrap,
17+
geom_path,
18+
geom_point,
19+
ggplot,
20+
guide_colorbar,
21+
guide_legend,
22+
guides,
23+
labs,
24+
scale_color_gradientn,
25+
scale_fill_manual,
26+
scale_shape_manual,
27+
theme,
28+
theme_void,
29+
)
30+
31+
32+
# Data — normalize t to [0, 1] per curve so both panels use the full color gradient
33+
n_points = 1000
34+
t_lissajous = np.linspace(0, 2 * np.pi, n_points)
35+
x_lissajous = np.sin(3 * t_lissajous)
36+
y_lissajous = np.sin(2 * t_lissajous)
37+
t_norm_liss = np.linspace(0, 1, n_points)
38+
39+
t_spiral = np.linspace(0, 4 * np.pi, n_points)
40+
x_spiral = t_spiral * np.cos(t_spiral) / (4 * np.pi)
41+
y_spiral = t_spiral * np.sin(t_spiral) / (4 * np.pi)
42+
t_norm_spiral = np.linspace(0, 1, n_points)
43+
44+
df = pd.concat(
45+
[
46+
pd.DataFrame(
47+
{"x": x_lissajous, "y": y_lissajous, "t_norm": t_norm_liss, "curve": "Lissajous · x = sin(3t), y = sin(2t)"}
48+
),
49+
pd.DataFrame(
50+
{"x": x_spiral, "y": y_spiral, "t_norm": t_norm_spiral, "curve": "Spiral · x = t·cos(t), y = t·sin(t)"}
51+
),
52+
],
53+
ignore_index=True,
54+
)
55+
56+
# Start and end markers
57+
start_pts = pd.DataFrame(
58+
{
59+
"x": [x_lissajous[0], x_spiral[0]],
60+
"y": [y_lissajous[0], y_spiral[0]],
61+
"t_norm": [0.0, 0.0],
62+
"curve": ["Lissajous · x = sin(3t), y = sin(2t)", "Spiral · x = t·cos(t), y = t·sin(t)"],
63+
"endpoint": ["Start (t = 0)", "Start (t = 0)"],
64+
}
65+
)
66+
67+
end_pts = pd.DataFrame(
68+
{
69+
"x": [x_lissajous[-1], x_spiral[-1]],
70+
"y": [y_lissajous[-1], y_spiral[-1]],
71+
"t_norm": [1.0, 1.0],
72+
"curve": ["Lissajous · x = sin(3t), y = sin(2t)", "Spiral · x = t·cos(t), y = t·sin(t)"],
73+
"endpoint": ["End (t = tmax)", "End (t = tmax)"],
74+
}
75+
)
76+
77+
markers = pd.concat([start_pts, end_pts], ignore_index=True)
78+
79+
# Gradient palette — deep navy through teal, gold, to vivid rose
80+
gradient_colors = ["#0d1b2a", "#1b4965", "#5fa8d3", "#bee9e8", "#ffd166", "#ef476f"]
81+
82+
# Plot
83+
plot = (
84+
ggplot(df, aes(x="x", y="y", color="t_norm"))
85+
+ geom_path(aes(group="curve"), size=2.5, alpha=0.94)
86+
+ geom_point(
87+
aes(shape="endpoint", fill="endpoint"), data=markers, color="#1a1a2e", size=8, stroke=1.2, show_legend=True
88+
)
89+
+ scale_shape_manual(name="Direction", values={"Start (t = 0)": "o", "End (t = tmax)": "D"})
90+
+ scale_fill_manual(name="Direction", values={"Start (t = 0)": "#306998", "End (t = tmax)": "#ef476f"})
91+
+ facet_wrap("curve", scales="free")
92+
+ scale_color_gradientn(
93+
name="Parameter t",
94+
colors=gradient_colors,
95+
guide=guide_colorbar(nbin=200),
96+
labels=["0", "¼", "½", "¾", "tmax"],
97+
breaks=[0.0, 0.25, 0.5, 0.75, 1.0],
98+
)
99+
+ coord_equal()
100+
+ labs(title="line-parametric · plotnine · pyplots.ai", x="Horizontal Position x(t)", y="Vertical Position y(t)")
101+
+ guides(shape=guide_legend(order=2), fill=guide_legend(order=2))
102+
+ theme_void()
103+
+ theme(
104+
figure_size=(16, 9),
105+
plot_title=element_text(size=26, weight="bold", color="#0d1b2a", margin={"b": 14}),
106+
axis_title_x=element_text(size=20, color="#333333", margin={"t": 10}),
107+
axis_title_y=element_text(size=20, color="#333333", margin={"r": 10}),
108+
axis_text=element_text(size=16, color="#555555"),
109+
axis_ticks=element_blank(),
110+
legend_title=element_text(size=17, weight="bold", color="#0d1b2a"),
111+
legend_text=element_text(size=16, color="#333333"),
112+
legend_key=element_rect(fill="white", color="white"),
113+
legend_background=element_rect(fill="#fafafa", color="#e0e0e0", size=0.5),
114+
legend_box_margin=6,
115+
strip_text=element_text(size=17, weight="bold", color="#0d1b2a", margin={"b": 8}),
116+
strip_background=element_rect(fill="#f0f4f8", color="none"),
117+
panel_spacing_x=0.15,
118+
panel_grid_major=element_line(color="#e8e8e8", size=0.3, linetype="dashed"),
119+
panel_grid_minor=element_blank(),
120+
panel_background=element_rect(fill="white", color="none"),
121+
plot_background=element_rect(fill="white", color="none"),
122+
plot_margin=0.01,
123+
)
124+
)
125+
126+
# Save
127+
plot.save("plot.png", dpi=300, verbose=False)
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
library: plotnine
2+
specification_id: line-parametric
3+
created: '2026-03-20T10:36:50Z'
4+
updated: '2026-03-20T10:58:58Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 23339035321
7+
issue: 4424
8+
python_version: 3.14.3
9+
library_version: 0.15.3
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/line-parametric/plotnine/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/line-parametric/plotnine/plot_thumb.png
12+
preview_html: null
13+
quality_score: 92
14+
review:
15+
strengths:
16+
- 'Excellent spec compliance: all required features (equal aspect ratio, color gradient,
17+
two curves, start/end markers) present and correct'
18+
- Custom gradient palette creates visually appealing and informative progression
19+
from navy to rose
20+
- Idiomatic plotnine usage with proper grammar of graphics layering
21+
- Clean deterministic code with no unnecessary complexity
22+
weaknesses:
23+
- 'Minor canvas utilization: spiral panel has some wasted whitespace due to coord_equal
24+
with spiral smaller extent versus endpoint position'
25+
image_description: The plot displays two parametric curves in a side-by-side faceted
26+
layout on a white background. The left panel shows a Lissajous figure (x=sin(3t),
27+
y=sin(2t)) — a self-intersecting figure-eight-like pattern spanning [-1, 1] on
28+
both axes. The right panel shows a spiral curve (x=t·cos(t), y=t·sin(t)) that
29+
winds outward from the origin. Both curves use a continuous color gradient from
30+
deep navy (t=0) through teal, light cyan, gold/yellow, to vivid pink/rose (t=tmax)
31+
to indicate the direction of parameter progression. Start points are marked with
32+
dark blue filled circles, end points with pink diamonds. The title reads "line-parametric
33+
· plotnine · pyplots.ai" in bold dark text. Axis labels are "Horizontal Position
34+
x(t)" and "Vertical Position y(t)". A "Direction" legend on the right shows the
35+
start/end marker shapes, and a "Parameter t" colorbar displays the full gradient
36+
scale with labels 0, ¼, ½, ¾, tmax. Facet strip labels show the curve equations.
37+
Subtle dashed grid lines appear on a clean white background.
38+
criteria_checklist:
39+
visual_quality:
40+
score: 29
41+
max: 30
42+
items:
43+
- id: VQ-01
44+
name: Text Legibility
45+
score: 8
46+
max: 8
47+
passed: true
48+
comment: 'All font sizes explicitly set: title=26pt, axis_title=20pt, axis_text=16pt,
49+
legend_title=17pt, strip_text=17pt'
50+
- id: VQ-02
51+
name: No Overlap
52+
score: 6
53+
max: 6
54+
passed: true
55+
comment: No overlapping text anywhere
56+
- id: VQ-03
57+
name: Element Visibility
58+
score: 6
59+
max: 6
60+
passed: true
61+
comment: Curves at size=2.5 with alpha=0.94, markers at size=8 with stroke
62+
clearly visible
63+
- id: VQ-04
64+
name: Color Accessibility
65+
score: 4
66+
max: 4
67+
passed: true
68+
comment: Navy-teal-gold-rose gradient is colorblind-friendly
69+
- id: VQ-05
70+
name: Layout & Canvas
71+
score: 3
72+
max: 4
73+
passed: true
74+
comment: Good two-panel layout, minor whitespace in spiral panel due to coord_equal
75+
- id: VQ-06
76+
name: Axis Labels & Title
77+
score: 2
78+
max: 2
79+
passed: true
80+
comment: Descriptive labels with mathematical notation appropriate for domain
81+
design_excellence:
82+
score: 15
83+
max: 20
84+
items:
85+
- id: DE-01
86+
name: Aesthetic Sophistication
87+
score: 6
88+
max: 8
89+
passed: true
90+
comment: Custom 6-color gradient palette, thoughtful typography, intentional
91+
color hierarchy
92+
- id: DE-02
93+
name: Visual Refinement
94+
score: 5
95+
max: 6
96+
passed: true
97+
comment: theme_void base with subtle dashed grid, custom legend styling, clean
98+
backgrounds
99+
- id: DE-03
100+
name: Data Storytelling
101+
score: 4
102+
max: 6
103+
passed: true
104+
comment: Color gradient tells progression story, start/end markers guide viewer,
105+
equations in facet labels
106+
spec_compliance:
107+
score: 15
108+
max: 15
109+
items:
110+
- id: SC-01
111+
name: Plot Type
112+
score: 5
113+
max: 5
114+
passed: true
115+
comment: Correct parametric curve plot with two example curves
116+
- id: SC-02
117+
name: Required Features
118+
score: 4
119+
max: 4
120+
passed: true
121+
comment: Equal aspect ratio, color gradient, two curves, start/end markers,
122+
sufficient density
123+
- id: SC-03
124+
name: Data Mapping
125+
score: 3
126+
max: 3
127+
passed: true
128+
comment: x(t) and y(t) correctly computed and mapped
129+
- id: SC-04
130+
name: Title & Legend
131+
score: 3
132+
max: 3
133+
passed: true
134+
comment: Correct title format, clear legend labels
135+
data_quality:
136+
score: 14
137+
max: 15
138+
items:
139+
- id: DQ-01
140+
name: Feature Coverage
141+
score: 6
142+
max: 6
143+
passed: true
144+
comment: Self-intersecting closed curve and open expanding spiral demonstrate
145+
key parametric behaviors
146+
- id: DQ-02
147+
name: Realistic Context
148+
score: 4
149+
max: 5
150+
passed: true
151+
comment: Well-known mathematical curves, neutral scientific context
152+
- id: DQ-03
153+
name: Appropriate Scale
154+
score: 4
155+
max: 4
156+
passed: true
157+
comment: Mathematically correct values, appropriate t ranges
158+
code_quality:
159+
score: 10
160+
max: 10
161+
items:
162+
- id: CQ-01
163+
name: KISS Structure
164+
score: 3
165+
max: 3
166+
passed: true
167+
comment: Clean Imports-Data-Plot-Save structure, no functions/classes
168+
- id: CQ-02
169+
name: Reproducibility
170+
score: 2
171+
max: 2
172+
passed: true
173+
comment: Fully deterministic mathematical functions
174+
- id: CQ-03
175+
name: Clean Imports
176+
score: 2
177+
max: 2
178+
passed: true
179+
comment: All imports used
180+
- id: CQ-04
181+
name: Code Elegance
182+
score: 2
183+
max: 2
184+
passed: true
185+
comment: Clean, well-organized, appropriate complexity
186+
- id: CQ-05
187+
name: Output & API
188+
score: 1
189+
max: 1
190+
passed: true
191+
comment: Saves as plot.png with dpi=300, current API
192+
library_mastery:
193+
score: 9
194+
max: 10
195+
items:
196+
- id: LM-01
197+
name: Idiomatic Usage
198+
score: 5
199+
max: 5
200+
passed: true
201+
comment: 'Expert grammar of graphics usage: aes, geom_path, facet_wrap, scale_color_gradientn,
202+
coord_equal'
203+
- id: LM-02
204+
name: Distinctive Features
205+
score: 4
206+
max: 5
207+
passed: true
208+
comment: ggplot-specific layering, facet_wrap, guide_colorbar, scale_shape_manual
209+
+ scale_fill_manual
210+
verdict: APPROVED
211+
impl_tags:
212+
dependencies: []
213+
techniques:
214+
- faceting
215+
- layer-composition
216+
- colorbar
217+
patterns:
218+
- data-generation
219+
dataprep:
220+
- normalization
221+
styling:
222+
- minimal-chrome
223+
- custom-colormap
224+
- alpha-blending
225+
- grid-styling

0 commit comments

Comments
 (0)