Skip to content

Commit 32a5443

Browse files
github-actions[bot]claudeMarkusNeusinger
authored
feat(makie): implement lollipop-basic (#9604)
## Implementation: `lollipop-basic` - julia/makie Implements the **julia/makie** version of `lollipop-basic`. **File:** `plots/lollipop-basic/implementations/julia/makie.jl` **Parent Issue:** #934 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/28497107993)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent 86a0abb commit 32a5443

2 files changed

Lines changed: 371 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# anyplot.ai
2+
# lollipop-basic: Basic Lollipop Chart
3+
# Library: makie 0.21.9 | Julia 1.11.9
4+
# Quality: 90/100 | Created: 2026-07-01
5+
6+
using CairoMakie
7+
using Colors
8+
using Random
9+
10+
Random.seed!(42)
11+
12+
# Theme tokens
13+
const THEME = get(ENV, "ANYPLOT_THEME", "light")
14+
const PAGE_BG = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17"
15+
const INK = THEME == "light" ? colorant"#1A1A17" : colorant"#F0EFE8"
16+
const INK_SOFT = THEME == "light" ? colorant"#4A4A44" : colorant"#B8B7B0"
17+
const IMPRINT_PALETTE = [
18+
colorant"#009E73", # 1 — brand green (always first categorical series)
19+
colorant"#C475FD", # 2 — lavender
20+
colorant"#4467A3", # 3 — blue
21+
colorant"#BD8233", # 4 — ochre
22+
colorant"#AE3030", # 5 — matte red
23+
colorant"#2ABCCD", # 6 — cyan
24+
colorant"#954477", # 7 — rose
25+
colorant"#99B314", # 8 — lime
26+
]
27+
const ANYPLOT_AMBER = colorant"#DDCC77"
28+
29+
# Data — renewable energy share (%) by country
30+
countries = ["Norway", "Iceland", "Austria", "Sweden", "New Zealand",
31+
"Germany", "UK", "Canada", "Australia", "USA"]
32+
pct_renew = [98.0, 85.0, 71.0, 65.0, 58.0, 46.0, 39.0, 32.0, 24.0, 18.0]
33+
34+
# Sort ascending so values rise left-to-right
35+
order = sortperm(pct_renew)
36+
sorted_countries = countries[order]
37+
sorted_pct = pct_renew[order]
38+
n = length(sorted_pct)
39+
40+
# Amber accent on the clear leader (Norway 98%, rightmost after ascending sort)
41+
base_color = IMPRINT_PALETTE[1]
42+
dot_colors = [i == n ? ANYPLOT_AMBER : base_color for i in 1:n]
43+
stem_colors = [c for dc in dot_colors for c in (dc, dc)] # 2 points per stem
44+
45+
# Build stem point pairs for linesegments!
46+
stem_pts = Point2f[]
47+
for (i, val) in enumerate(sorted_pct)
48+
push!(stem_pts, Point2f(i, 0.0))
49+
push!(stem_pts, Point2f(i, val))
50+
end
51+
52+
# Figure
53+
fig = Figure(
54+
size = (1600, 900),
55+
fontsize = 14,
56+
backgroundcolor = PAGE_BG,
57+
)
58+
59+
ax = Axis(
60+
fig[1, 1];
61+
title = "lollipop-basic · julia · makie · anyplot.ai",
62+
titlesize = 20,
63+
titlecolor = INK,
64+
xlabel = "Country",
65+
ylabel = "Renewable Energy Share (%)",
66+
xlabelcolor = INK,
67+
ylabelcolor = INK,
68+
xlabelsize = 14,
69+
ylabelsize = 14,
70+
xticklabelcolor = INK_SOFT,
71+
yticklabelcolor = INK_SOFT,
72+
xticklabelsize = 12,
73+
yticklabelsize = 12,
74+
backgroundcolor = PAGE_BG,
75+
topspinevisible = false,
76+
rightspinevisible = false,
77+
leftspinecolor = INK_SOFT,
78+
bottomspinecolor = INK_SOFT,
79+
xtickcolor = INK_SOFT,
80+
ytickcolor = INK_SOFT,
81+
xgridvisible = false,
82+
ygridcolor = RGBAf(INK.r, INK.g, INK.b, 0.15),
83+
yminorgridvisible = false,
84+
xminorgridvisible = false,
85+
)
86+
87+
ax.xticks = (1:n, sorted_countries)
88+
ax.xticklabelrotation = π / 6
89+
90+
# 50% reference line — "majority renewable" threshold
91+
hlines!(ax, [50.0]; color = RGBAf(INK.r, INK.g, INK.b, 0.35), linestyle = :dash, linewidth = 1.5)
92+
text!(ax, 1.1, 52.0; text = "50% threshold", color = INK_SOFT, fontsize = 10, align = (:left, :bottom))
93+
94+
# Stems
95+
linesegments!(ax, stem_pts; color = stem_colors, linewidth = 2.5)
96+
97+
# Dots
98+
scatter!(ax, collect(1:n), sorted_pct;
99+
color = dot_colors,
100+
markersize = 18,
101+
strokewidth = 2.0,
102+
strokecolor = PAGE_BG,
103+
)
104+
105+
# Value annotations via Makie's native text! — lightweight data labels
106+
for (i, val) in enumerate(sorted_pct)
107+
text!(ax, i, val + 3.0;
108+
text = "$(Int(val))%",
109+
color = INK_SOFT,
110+
fontsize = 10,
111+
align = (:center, :bottom),
112+
)
113+
end
114+
115+
ylims!(ax, 0, 115)
116+
117+
# Save
118+
save("plot-$(THEME).png", fig; px_per_unit = 2)
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
library: makie
2+
language: julia
3+
specification_id: lollipop-basic
4+
created: '2026-07-01T06:09:30Z'
5+
updated: '2026-07-01T06:35:29Z'
6+
generated_by: claude-sonnet
7+
workflow_run: 28497107993
8+
issue: 934
9+
language_version: 1.11.9
10+
library_version: 0.21.9
11+
preview_url_light: https://storage.googleapis.com/anyplot-images/plots/lollipop-basic/julia/makie/plot-light.png
12+
preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/lollipop-basic/julia/makie/plot-dark.png
13+
preview_html_light: null
14+
preview_html_dark: null
15+
quality_score: 90
16+
review:
17+
strengths:
18+
- Renewable energy data by country is real-world compelling and non-controversial
19+
- Amber accent on Norway (98%) creates immediate focal point and clear visual hierarchy
20+
- 50% majority-renewable threshold line adds meaningful analytical context
21+
- Value annotations on every lollipop make each data point readable at a glance
22+
- linesegments! with Point2f[] paired-point idiom is the correct idiomatic Makie
23+
approach for stems
24+
- 'Full Imprint palette compliance: #009E73 first series, correct theme-adaptive
25+
chrome in both renders'
26+
- Top/right spines removed, Y-only subtle grid, dot strokes with PAGE_BG for clean
27+
visual refinement
28+
weaknesses:
29+
- Value annotation fontsize=10 (20px effective) is the smallest text on the canvas
30+
— consider bumping to fontsize=11 or 12 for better mobile readability
31+
- 'ANYPLOT_AMBER semantic role is warning/caution; using it for the top performer
32+
(Norway) is a creative choice that slightly mismatches the anchor''s documented
33+
role — an alternative is a larger dot (markersize=24) or a bold text annotation
34+
to mark Norway as #1 without color-role mismatch'
35+
- DE storytelling is good but stops short of the top tier — adding a direct annotation
36+
arrow or callout for Norway (e.g. text! with arrow-like offset) would push narrative
37+
clarity further
38+
image_description: |-
39+
Light render (plot-light.png):
40+
Background: Warm off-white (#FAF8F1) — correct, not pure white.
41+
Chrome: Title "lollipop-basic · julia · makie · anyplot.ai" in bold dark ink, clearly readable. Y-axis label "Renewable Energy Share (%)" and X-axis label "Country" in dark ink, descriptive and readable. X-tick labels (USA through Norway) rotated ~30°, readable. Y-tick labels (0, 50, 100) readable. "50% threshold" annotation in INK_SOFT grey, readable. Value annotations (18%–98%) at fontsize=10 are the smallest text but legible at canvas resolution.
42+
Data: 9 lollipops in brand green #009E73, Norway (rightmost, 98%) highlighted in amber #DDCC77. Stems are thin lines from baseline to dots; dots have PAGE_BG stroke for definition. A dashed 50% reference line adds analytical context. Data sorted ascending left-to-right.
43+
Legibility verdict: PASS — all text readable against light background; no light-on-light failures.
44+
45+
Dark render (plot-dark.png):
46+
Background: Warm near-black (#1A1A17) — correct, not pure black.
47+
Chrome: Title and axis labels render in light ink (#F0EFE8), clearly readable against dark background. Tick labels in INK_SOFT (#B8B7B0), readable. "50% threshold" annotation visible. Value annotations readable. No dark-on-dark failures observed.
48+
Data: Data colors are identical to light render — 9 green (#009E73) lollipops and 1 amber (#DDCC77) Norway lollipop. Only chrome (background, text, grid, spines) has flipped to dark theme. Dashed reference line and subtle Y-grid visible.
49+
Legibility verdict: PASS — all text readable against dark background; theme adaptation is complete and correct.
50+
criteria_checklist:
51+
visual_quality:
52+
score: 29
53+
max: 30
54+
items:
55+
- id: VQ-01
56+
name: Text Legibility
57+
score: 7
58+
max: 8
59+
passed: true
60+
comment: All font sizes explicitly set; readable in both themes. Value annotations
61+
at fontsize=10 (20px effective) are borderline on mobile — minor deduction.
62+
- id: VQ-02
63+
name: No Overlap
64+
score: 6
65+
max: 6
66+
passed: true
67+
comment: No overlapping elements; Germany 46% annotation and 50% threshold
68+
label are at different x-positions.
69+
- id: VQ-03
70+
name: Element Visibility
71+
score: 6
72+
max: 6
73+
passed: true
74+
comment: Markersize=18 appropriate for 10 lollipops; stems at linewidth=2.5
75+
clearly visible.
76+
- id: VQ-04
77+
name: Color Accessibility
78+
score: 2
79+
max: 2
80+
passed: true
81+
comment: Imprint palette is CVD-safe; green/amber distinction clear; no red-green
82+
as sole signal.
83+
- id: VQ-05
84+
name: Layout & Canvas
85+
score: 4
86+
max: 4
87+
passed: true
88+
comment: Canvas gate passed (3200x1800); good proportions; generous whitespace;
89+
no overflow.
90+
- id: VQ-06
91+
name: Axis Labels & Title
92+
score: 2
93+
max: 2
94+
passed: true
95+
comment: 'Y-axis: ''Renewable Energy Share (%)'' with units; X-axis: ''Country'';
96+
title format correct.'
97+
- id: VQ-07
98+
name: Palette Compliance
99+
score: 2
100+
max: 2
101+
passed: true
102+
comment: 'First series #009E73; amber used as semantic accent; backgrounds
103+
#FAF8F1/#1A1A17 correct in both themes.'
104+
design_excellence:
105+
score: 14
106+
max: 20
107+
items:
108+
- id: DE-01
109+
name: Aesthetic Sophistication
110+
score: 6
111+
max: 8
112+
passed: true
113+
comment: 'Above default 4: amber accent on leader, sorted progression, reference
114+
line, clean palette application. Not quite publication-ready (no callout
115+
annotation for Norway).'
116+
- id: DE-02
117+
name: Visual Refinement
118+
score: 4
119+
max: 6
120+
passed: true
121+
comment: Top/right spines removed; Y-only grid at 0.15 opacity; dot PAGE_BG
122+
strokes; rotated x-ticks. Clearly above default 2.
123+
- id: DE-03
124+
name: Data Storytelling
125+
score: 4
126+
max: 6
127+
passed: true
128+
comment: Sorted ranking + 50% threshold + amber focal point create a clear
129+
narrative. Above default 2; falls short of 6 (no arrow/callout for Norway).
130+
spec_compliance:
131+
score: 15
132+
max: 15
133+
items:
134+
- id: SC-01
135+
name: Plot Type
136+
score: 5
137+
max: 5
138+
passed: true
139+
comment: 'Correct lollipop chart: thin stems (linesegments!) + circular markers
140+
(scatter!).'
141+
- id: SC-02
142+
name: Required Features
143+
score: 4
144+
max: 4
145+
passed: true
146+
comment: Stems from baseline, circular dots, vertical orientation, sorted
147+
data — all present.
148+
- id: SC-03
149+
name: Data Mapping
150+
score: 3
151+
max: 3
152+
passed: true
153+
comment: Countries on x-axis, renewable % on y-axis, all 10 categories visible.
154+
- id: SC-04
155+
name: Title & Legend
156+
score: 3
157+
max: 3
158+
passed: true
159+
comment: 'Title: ''lollipop-basic · julia · makie · anyplot.ai''. No legend
160+
needed (single categorical variable with accent); correct.'
161+
data_quality:
162+
score: 15
163+
max: 15
164+
items:
165+
- id: DQ-01
166+
name: Feature Coverage
167+
score: 6
168+
max: 6
169+
passed: true
170+
comment: 'Shows all lollipop aspects: varied heights, sorted order, clear
171+
baseline, good range 18–98%.'
172+
- id: DQ-02
173+
name: Realistic Context
174+
score: 5
175+
max: 5
176+
passed: true
177+
comment: Renewable energy share by country is a compelling, non-controversial,
178+
real-world scientific topic.
179+
- id: DQ-03
180+
name: Appropriate Scale
181+
score: 4
182+
max: 4
183+
passed: true
184+
comment: Values 18–98% are factually reasonable for these countries in the
185+
renewable energy domain.
186+
code_quality:
187+
score: 10
188+
max: 10
189+
items:
190+
- id: CQ-01
191+
name: KISS Structure
192+
score: 3
193+
max: 3
194+
passed: true
195+
comment: 'Linear: constants → data → sort → figure → axis → plot elements
196+
→ save. No functions or classes.'
197+
- id: CQ-02
198+
name: Reproducibility
199+
score: 2
200+
max: 2
201+
passed: true
202+
comment: Random.seed!(42) set; data is hardcoded so deterministic.
203+
- id: CQ-03
204+
name: Clean Imports
205+
score: 2
206+
max: 2
207+
passed: true
208+
comment: CairoMakie (plot), Colors (colorant""), Random (seed) — all three
209+
are used.
210+
- id: CQ-04
211+
name: Code Elegance
212+
score: 2
213+
max: 2
214+
passed: true
215+
comment: Clean paired-point construction for linesegments!; no over-engineering;
216+
no fake UI.
217+
- id: CQ-05
218+
name: Output & API
219+
score: 1
220+
max: 1
221+
passed: true
222+
comment: Saves as plot-$(THEME).png; uses size= (correct CairoMakie 0.21+
223+
API, not deprecated resolution=).
224+
library_mastery:
225+
score: 7
226+
max: 10
227+
items:
228+
- id: LM-01
229+
name: Idiomatic Usage
230+
score: 4
231+
max: 5
232+
passed: true
233+
comment: Correct Figure/Axis/primitive pattern; proper colorant"", RGBAf,
234+
Point2f[] types; theme-adaptive chrome all wired correctly.
235+
- id: LM-02
236+
name: Distinctive Features
237+
score: 3
238+
max: 5
239+
passed: true
240+
comment: linesegments! with paired Point2f array is distinctly Makie (vs.
241+
matplotlib vlines); RGBAf alpha on grid/reference is Makie-native.
242+
verdict: APPROVED
243+
impl_tags:
244+
dependencies: []
245+
techniques:
246+
- annotations
247+
- manual-ticks
248+
patterns:
249+
- data-generation
250+
- iteration-over-groups
251+
dataprep: []
252+
styling:
253+
- edge-highlighting

0 commit comments

Comments
 (0)