Skip to content

Commit a7057ff

Browse files
feat(makie): implement point-basic (#11558)
## Implementation: `point-basic` - julia/makie Implements the **julia/makie** version of `point-basic`. **File:** `plots/point-basic/implementations/julia/makie.jl` **Parent Issue:** #2551 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/33961745832)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent 0382424 commit a7057ff

2 files changed

Lines changed: 354 additions & 0 deletions

File tree

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# anyplot.ai
2+
# point-basic: Point Estimate Plot
3+
# Library: makie 0.21.9 | Julia 1.11.9
4+
# Quality: 89/100 | Created: 2026-09-05
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 BRAND = colorant"#009E73" # Imprint palette position 1 — ALWAYS first series
18+
19+
# --- Data: standardized mean difference (SMD) per clinical trial subgroup ---
20+
subgroups = [
21+
"Age < 40", "Age 40-59", "Age 60+", "Male", "Female",
22+
"Prior therapy", "No prior therapy",
23+
]
24+
n = length(subgroups)
25+
baseline_estimate = [0.42, 0.31, 0.18, 0.28, 0.35, 0.12, 0.46]
26+
baseline_half_width = [0.22, 0.16, 0.22, 0.15, 0.18, 0.24, 0.19]
27+
estimate = baseline_estimate .+ (rand(n) .- 0.5) .* 0.02
28+
half_width = baseline_half_width .+ (rand(n) .- 0.5) .* 0.02
29+
lower_bound = estimate .- half_width
30+
upper_bound = estimate .+ half_width
31+
32+
order = sortperm(estimate)
33+
subgroups = subgroups[order]
34+
estimate = estimate[order]
35+
lower_bound = lower_bound[order]
36+
upper_bound = upper_bound[order]
37+
positions = 1:n
38+
crosses_null = (lower_bound .< 0) .& (upper_bound .> 0)
39+
40+
# --- Plot ---------------------------------------------------------------
41+
fig = Figure(
42+
size = (1600, 900),
43+
fontsize = 14,
44+
backgroundcolor = PAGE_BG,
45+
)
46+
47+
ax = Axis(
48+
fig[1, 1];
49+
title = "point-basic · julia · makie · anyplot.ai",
50+
titlesize = 20,
51+
titlecolor = INK,
52+
xlabel = "Standardized Mean Difference (95% CI)",
53+
ylabel = "Subgroup",
54+
xlabelsize = 15,
55+
ylabelsize = 15,
56+
xlabelcolor = INK,
57+
ylabelcolor = INK,
58+
xticklabelcolor = INK_SOFT,
59+
yticklabelcolor = INK_SOFT,
60+
xticklabelsize = 14,
61+
yticklabelsize = 14,
62+
backgroundcolor = PAGE_BG,
63+
topspinevisible = false,
64+
rightspinevisible = false,
65+
leftspinecolor = INK_SOFT,
66+
bottomspinecolor = INK_SOFT,
67+
xgridcolor = RGBAf(INK.r, INK.g, INK.b, 0.15),
68+
ygridvisible = false,
69+
yticks = (positions, subgroups),
70+
)
71+
72+
vlines!(ax, [0.0]; color = INK_SOFT, linestyle = :dash, linewidth = 1.5)
73+
rangebars!(ax, positions, lower_bound, upper_bound;
74+
direction = :x, color = BRAND, linewidth = 2.5, whiskerwidth = 14)
75+
76+
# Filled markers = CI excludes the null; open (hollow) markers = CI spans it —
77+
# a classic forest-plot convention that reads at a glance without adding a
78+
# second hue (palette stays single-accent).
79+
sig, nonsig = .!crosses_null, crosses_null
80+
scatter!(ax, estimate[sig], positions[sig];
81+
color = BRAND, markersize = 20, strokewidth = 0)
82+
scatter!(ax, estimate[nonsig], positions[nonsig];
83+
color = PAGE_BG, markersize = 20, strokewidth = 2.5, strokecolor = BRAND)
84+
85+
xlims!(ax, -0.1, 0.85)
86+
ylims!(ax, 0.3, n + 0.7)
87+
88+
Label(fig[2, 1], "○ open marker — 95% CI spans the null (SMD = 0)";
89+
color = INK_SOFT, fontsize = 13, halign = :left, padding = (6, 0, 0, 4))
90+
91+
# --- Save -------------------------------------------------------------------
92+
save("plot-$(THEME).png", fig; px_per_unit = 2)
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
library: makie
2+
language: julia
3+
specification_id: point-basic
4+
created: '2026-09-05T10:55:10Z'
5+
updated: '2026-09-05T11:15:45Z'
6+
generated_by: claude-sonnet
7+
workflow_run: 33961745832
8+
issue: 2551
9+
language_version: 1.11.9
10+
library_version: 0.21.9
11+
preview_url_light: https://storage.googleapis.com/anyplot-images/plots/point-basic/julia/makie/plot-light.png
12+
preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/point-basic/julia/makie/plot-dark.png
13+
preview_html_light: null
14+
preview_html_dark: null
15+
quality_score: 89
16+
review:
17+
strengths:
18+
- 'Attempt-1 feedback fully addressed: x/y tick label sizes raised 12→14 for mobile
19+
legibility, x/y axis-label sizes now explicit at 15, and the data-generation seed
20+
is now functional (rand(n) jitter) instead of a no-op Random.seed! call'
21+
- New filled-vs-hollow marker convention (filled = CI excludes the null, open =
22+
CI spans it) adds a real semantic layer and a clear visual hierarchy without introducing
23+
a second hue — the Label caption at fig[2,1] documents the convention cleanly,
24+
replacing the need for a formal legend
25+
- Correct forest/point-estimate plot using rangebars!(direction=:x) + scatter!,
26+
matching the spec's category/estimate/lower_bound/upper_bound structure exactly,
27+
with visible whisker caps (whiskerwidth=14)
28+
- Subgroups sorted by estimate value, creating a natural, easy-to-scan visual hierarchy
29+
from smallest to largest effect, reinforced by the dashed zero-reference line
30+
- 'Theme-adaptive chrome is fully correct: identical brand-green (#009E73) data
31+
color in both renders, with title, axis labels, ticks, spines and grid transparency
32+
correctly following INK/INK_SOFT/PAGE_BG tokens — no dark-on-dark or light-on-light
33+
failures'
34+
- 'Clean, KISS-structured code: flat script, only used imports (CairoMakie, Colors,
35+
Random), title format exactly matches the mandated convention'
36+
weaknesses:
37+
- 'Canvas utilization is unbalanced: the axis block (y-category labels through the
38+
rightmost x=0.8 gridline) only occupies roughly the left ~28-30% of the 3200x1800
39+
canvas width, leaving a large, empty, un-balanced whitespace band on the right
40+
side of the figure with no title, legend, or content to fill it. Fix by widening
41+
xlims! further (or letting the axis stretch to the full column), reducing the
42+
Figure''s implicit right margin, or adding `colsize!(fig.layout, 1, Relative(1))`/checking
43+
why the Axis is not expanding to fill the 1x1 GridLayout cell edge-to-edge.'
44+
- 'Library Mastery is still fairly generic for Makie: rangebars! + scatter! + vlines!
45+
+ a Label caption are all correct but not distinctly showcase-y for Makie specifically
46+
— the same forest plot could be built nearly identically in most other plotting
47+
libraries.'
48+
- DE-01 aesthetic sophistication remains single-hue by (reasonable) domain convention;
49+
there's still room for one more polish touch (e.g., subtle row banding, or a light
50+
background band highlighting the 'no effect' zone around the dashed line) to push
51+
past a well-executed style-guide baseline.
52+
image_description: |-
53+
Light render (plot-light.png):
54+
Background: Warm off-white (#FAF8F1-consistent), not pure white, not dark.
55+
Chrome: Bold dark title "point-basic · julia · makie · anyplot.ai" centered above the axis block. X-axis label "Standardized Mean Difference (95% CI)" and y-axis label "Subgroup" in dark ink, both fully legible. Y-axis category tick labels (No prior therapy, Age < 40, Female, Age 40-59, Male, Age 60+, Prior therapy — sorted descending by estimate) and x-axis numeric ticks (0.0-0.8) render in soft dark gray, all clearly readable. Subtle vertical gridlines only (y-gridlines disabled), a dashed reference line at x=0, top/right spines removed. A small caption below the axis reads "○ open marker — 95% CI spans the null (SMD = 0)" in soft ink, fully visible and not clipped. Note: the axis block (labels + plot area) only spans roughly the left third of the canvas width, leaving a large unbalanced empty band on the right of the figure.
56+
Data: Seven brand-green (#009E73) range-bar error bars with whisker caps; five filled circle markers (CI excludes zero) and two hollow/open circle markers (CI spans zero) — a clean semantic distinction that reads at a glance.
57+
Legibility verdict: PASS.
58+
59+
Dark render (plot-dark.png):
60+
Background: Warm near-black (#1A1A17-consistent), not pure black, not light.
61+
Chrome: Same title, axis labels, tick labels, and caption now rendered in light ink/off-white and soft light-gray tones respectively — all clearly visible against the dark background. No dark-on-dark issues found on title, axis labels, tick labels, or the marker-convention caption. Gridlines and the dashed reference line remain subtle but visible. Spines and overall layout (including the same right-side whitespace imbalance) are identical to the light render.
62+
Data: Same seven brand-green (#009E73) markers and range-bars, with the identical filled/open split — colors confirmed identical to the light render; only chrome (background, text, grid) flipped.
63+
Legibility verdict: PASS.
64+
criteria_checklist:
65+
visual_quality:
66+
score: 27
67+
max: 30
68+
items:
69+
- id: VQ-01
70+
name: Text Legibility
71+
score: 7
72+
max: 8
73+
passed: true
74+
comment: Tick label sizes raised from 12 to 14, axis-label sizes explicit
75+
at 15, titlesize 20 — all readable in both themes; small residual risk at
76+
mobile thumbnail scale keeps this just short of perfect
77+
- id: VQ-02
78+
name: No Overlap
79+
score: 6
80+
max: 6
81+
passed: true
82+
comment: No collisions between text, markers, or error bars in either render
83+
- id: VQ-03
84+
name: Element Visibility
85+
score: 6
86+
max: 6
87+
passed: true
88+
comment: Sparse data (7 points) rendered with prominent markersize=20 and
89+
linewidth=2.5 range-bars
90+
- id: VQ-04
91+
name: Color Accessibility
92+
score: 2
93+
max: 2
94+
passed: true
95+
comment: Single brand-green hue plus filled/open marker distinction avoids
96+
relying on color alone for significance signaling
97+
- id: VQ-05
98+
name: Layout & Canvas
99+
score: 2
100+
max: 4
101+
passed: false
102+
comment: Axis block (labels + plot area) fills only roughly the left ~30%
103+
of the 3200x1800 canvas width; the right side of the figure is empty, unbalanced
104+
whitespace with nothing to fill it
105+
- id: VQ-06
106+
name: Axis Labels & Title
107+
score: 2
108+
max: 2
109+
passed: true
110+
comment: xlabel includes units/CI level, ylabel and title both present
111+
- id: VQ-07
112+
name: Palette Compliance
113+
score: 2
114+
max: 2
115+
passed: true
116+
comment: 'First (only) series uses #009E73; backgrounds match #FAF8F1/#1A1A17;
117+
both renders theme-correct'
118+
design_excellence:
119+
score: 15
120+
max: 20
121+
items:
122+
- id: DE-01
123+
name: Aesthetic Sophistication
124+
score: 6
125+
max: 8
126+
passed: true
127+
comment: Filled/hollow marker convention for null-crossing CIs is a thoughtful,
128+
above-default design choice; still single-hue by domain convention
129+
- id: DE-02
130+
name: Visual Refinement
131+
score: 4
132+
max: 6
133+
passed: true
134+
comment: Spines removed, grid subtle and x-only, but whitespace balance is
135+
undermined by the large empty band on the right of the canvas
136+
- id: DE-03
137+
name: Data Storytelling
138+
score: 5
139+
max: 6
140+
passed: true
141+
comment: Descending sort, dashed zero line, and the new filled/open marker
142+
split let the viewer immediately see which subgroups have a statistically
143+
distinguishable effect
144+
spec_compliance:
145+
score: 15
146+
max: 15
147+
items:
148+
- id: SC-01
149+
name: Plot Type
150+
score: 5
151+
max: 5
152+
passed: true
153+
comment: Correct point-estimate/forest plot using rangebars! + scatter!
154+
- id: SC-02
155+
name: Required Features
156+
score: 4
157+
max: 4
158+
passed: true
159+
comment: Category, estimate, CI, whisker caps, and reference line all present
160+
- id: SC-03
161+
name: Data Mapping
162+
score: 3
163+
max: 3
164+
passed: true
165+
comment: X=estimate/CI, Y=category; axis limits show all data
166+
- id: SC-04
167+
name: Title & Legend
168+
score: 3
169+
max: 3
170+
passed: true
171+
comment: Title matches the exact required format; marker convention documented
172+
via caption in place of a formal legend
173+
data_quality:
174+
score: 15
175+
max: 15
176+
items:
177+
- id: DQ-01
178+
name: Feature Coverage
179+
score: 6
180+
max: 6
181+
passed: true
182+
comment: Shows category, estimate, CI, reference line, caps, and both significant/non-significant
183+
subgroups
184+
- id: DQ-02
185+
name: Realistic Context
186+
score: 5
187+
max: 5
188+
passed: true
189+
comment: Plausible, neutral clinical-trial subgroup SMD scenario
190+
- id: DQ-03
191+
name: Appropriate Scale
192+
score: 4
193+
max: 4
194+
passed: true
195+
comment: SMD values and CI widths are sensible for the domain
196+
code_quality:
197+
score: 10
198+
max: 10
199+
items:
200+
- id: CQ-01
201+
name: KISS Structure
202+
score: 3
203+
max: 3
204+
passed: true
205+
comment: No functions/classes, flat script
206+
- id: CQ-02
207+
name: Reproducibility
208+
score: 2
209+
max: 2
210+
passed: true
211+
comment: Random.seed!(42) now has a functional effect via rand(n) jitter on
212+
estimate/half_width
213+
- id: CQ-03
214+
name: Clean Imports
215+
score: 2
216+
max: 2
217+
passed: true
218+
comment: Only CairoMakie, Colors, Random imported and used
219+
- id: CQ-04
220+
name: Code Elegance
221+
score: 2
222+
max: 2
223+
passed: true
224+
comment: Appropriate complexity, no fake interactivity, comment explains the
225+
marker-convention rationale
226+
- id: CQ-05
227+
name: Output & API
228+
score: 1
229+
max: 1
230+
passed: true
231+
comment: Saves plot-$(THEME).png with px_per_unit=2
232+
library_mastery:
233+
score: 7
234+
max: 10
235+
items:
236+
- id: LM-01
237+
name: Idiomatic Usage
238+
score: 4
239+
max: 5
240+
passed: true
241+
comment: Uses rangebars!, scatter!, vlines!, and a GridLayout Label — idiomatic
242+
Makie composition for a forest plot
243+
- id: LM-02
244+
name: Distinctive Features
245+
score: 3
246+
max: 5
247+
passed: false
248+
comment: rangebars!(direction=:x) plus a second layered scatter! pass for
249+
open/filled markers uses some Makie-specific composition, but the overall
250+
approach could still be replicated fairly directly in other libraries
251+
verdict: APPROVED
252+
impl_tags:
253+
dependencies: []
254+
techniques:
255+
- manual-ticks
256+
- annotations
257+
patterns:
258+
- data-generation
259+
dataprep: []
260+
styling:
261+
- grid-styling
262+
- publication-ready

0 commit comments

Comments
 (0)