diff --git a/plots/waterfall-basic/implementations/julia/makie.jl b/plots/waterfall-basic/implementations/julia/makie.jl new file mode 100644 index 0000000000..77a084cd96 --- /dev/null +++ b/plots/waterfall-basic/implementations/julia/makie.jl @@ -0,0 +1,148 @@ +# anyplot.ai +# waterfall-basic: Basic Waterfall Chart +# Library: makie 0.21.9 | Julia 1.11.9 +# Quality: 93/100 | Created: 2026-08-04 + +using CairoMakie +using Colors +using Printf + +# --- Theme tokens ------------------------------------------------------------- +const THEME = get(ENV, "ANYPLOT_THEME", "light") +const PAGE_BG = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17" +const INK = THEME == "light" ? colorant"#1A1A17" : colorant"#F0EFE8" +const INK_SOFT = THEME == "light" ? colorant"#4A4A44" : colorant"#B8B7B0" + +const IMPRINT_PALETTE = [ + colorant"#009E73", colorant"#C475FD", colorant"#4467A3", colorant"#BD8233", + colorant"#AE3030", colorant"#2ABCCD", colorant"#954477", colorant"#99B314", +] +const BRAND = IMPRINT_PALETTE[1] # Imprint palette position 1 — increases +const LOSS = IMPRINT_PALETTE[5] # Imprint semantic anchor for loss — decreases +const NEUTRAL = INK # theme-adaptive neutral — start/end totals + +# --- Data: quarterly profit bridge, $ millions -------------------------------- +categories = ["Starting\nBalance", "Product\nSales", "Service\nRevenue", + "Operating\nCosts", "Marketing\nSpend", "Taxes", "Net\nProfit"] +deltas = [12.4, 8.5, 4.3, -5.4, -3.1, -1.85, 0.0] +is_total = [true, false, false, false, false, false, true] +n = length(categories) + +running = zeros(Float64, n) +running[1] = deltas[1] +for i in 2:(n - 1) + running[i] = running[i - 1] + deltas[i] +end +running[n] = running[n - 1] + +bottoms = zeros(Float64, n) +tops = zeros(Float64, n) +bar_colors = Vector{typeof(BRAND)}(undef, n) +for i in 1:n + if is_total[i] + bottoms[i] = 0.0 + tops[i] = running[i] + bar_colors[i] = NEUTRAL + else + lo, hi = extrema((running[i - 1], running[i])) + bottoms[i] = lo + tops[i] = hi + bar_colors[i] = deltas[i] >= 0 ? BRAND : LOSS + end +end + +# --- Title (fontsize scaled to length — see plot-generator.md) ---------------- +title_str = "Quarterly Profit Bridge · waterfall-basic · julia · makie · anyplot.ai" +title_fontsize = round(Int, 20 * min(1.0, 67 / length(title_str))) + +# --- Plot ----------------------------------------------------------------------- +fig = Figure( + resolution = (1600, 900), + fontsize = 14, + backgroundcolor = PAGE_BG, +) + +ax = Axis( + fig[1, 1]; + title = title_str, + titlesize = title_fontsize, + titlecolor = INK, + ylabel = "Amount (\$M)", + ylabelsize = 14, + ylabelcolor = INK, + xticklabelcolor = INK_SOFT, + yticklabelcolor = INK_SOFT, + xticklabelsize = 12, + yticklabelsize = 12, + backgroundcolor = PAGE_BG, + topspinevisible = false, + rightspinevisible = false, + leftspinecolor = INK_SOFT, + bottomspinecolor = INK_SOFT, + xgridvisible = false, + ygridcolor = RGBAf(INK.r, INK.g, INK.b, 0.15), + xticks = (1:n, categories), + xticksvisible = false, +) + +bar_width = 0.62 +barplot!(ax, 1:n, tops; fillto = bottoms, color = bar_colors, width = bar_width, strokewidth = 0) + +# Connecting lines linking each bar's cumulative level to the next bar's start +half = bar_width / 2 +for i in 1:(n - 1) + level = running[i] + lines!(ax, [i + half, i + 1 - half], [level, level]; + color = INK_SOFT, linewidth = 1.8, linestyle = :dot) +end + +# The largest single decrease gets a bolder, slightly larger label to call +# out the focal point of the bridge. +decrease_idxs = findall(i -> !is_total[i] && deltas[i] < 0, 1:n) +biggest_idx = decrease_idxs[argmax(abs.(deltas[decrease_idxs]))] +biggest_label = replace(categories[biggest_idx], "\n" => " ") + +# Running total labels — above the bar for increases/totals, below for decreases +label_offset = maximum(tops) * 0.035 +for i in 1:n + label = @sprintf("\$%.2fM", running[i]) + emphasize = i == biggest_idx + lbl_fontsize = emphasize ? 17 : 15 + lbl_font = emphasize ? :bold : :regular + if is_total[i] || deltas[i] >= 0 + text!(ax, i, tops[i] + label_offset; text = label, + align = (:center, :bottom), color = INK, fontsize = lbl_fontsize, font = lbl_font) + else + text!(ax, i, bottoms[i] - label_offset; text = label, + align = (:center, :top), color = INK, fontsize = lbl_fontsize, font = lbl_font) + end +end + +# Subtitle annotation summarizing the bridge's focal point, set in the axis's +# relative-space headroom above the tallest bar — a Makie `rich()` touch that +# mixes weight/color within a single text object to call out the two key figures. +subtitle_rich = rich( + "Net profit reaches ", + rich(@sprintf("\$%.2fM", running[end]); color = BRAND, font = :bold), + " despite a ", + rich(@sprintf("\$%.2fM", abs(deltas[biggest_idx])); color = LOSS, font = :bold), + " $biggest_label pullback — the bridge's largest single decrease", +) +text!(ax, 0.5, 0.97; space = :relative, text = subtitle_rich, + align = (:center, :top), fontsize = 13, color = INK_SOFT) + +ylims!(ax, -maximum(tops) * 0.08, maximum(tops) * 1.18) + +elements = [PolyElement(color = BRAND), PolyElement(color = LOSS), PolyElement(color = NEUTRAL)] +Legend(fig[1, 1], elements, ["Increase", "Decrease", "Total"]; + tellwidth = false, tellheight = false, + halign = :left, valign = :top, + orientation = :horizontal, + framevisible = false, + labelcolor = INK_SOFT, + labelsize = 12, + backgroundcolor = :transparent, +) + +# --- Save ----------------------------------------------------------------------- +save("plot-$(THEME).png", fig; px_per_unit = 2) diff --git a/plots/waterfall-basic/metadata/julia/makie.yaml b/plots/waterfall-basic/metadata/julia/makie.yaml new file mode 100644 index 0000000000..4886e099a5 --- /dev/null +++ b/plots/waterfall-basic/metadata/julia/makie.yaml @@ -0,0 +1,232 @@ +library: makie +language: julia +specification_id: waterfall-basic +created: '2026-08-04T11:29:00Z' +updated: '2026-08-04T11:51:05Z' +generated_by: claude-sonnet +workflow_run: 30904486628 +issue: 777 +language_version: 1.11.9 +library_version: 0.21.9 +preview_url_light: https://storage.googleapis.com/anyplot-images/plots/waterfall-basic/julia/makie/plot-light.png +preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/waterfall-basic/julia/makie/plot-dark.png +preview_html_light: null +preview_html_dark: null +quality_score: 93 +review: + strengths: + - Correct, fully-verified cumulative math (running totals match all displayed labels + exactly) + - Dotted connector lines and the bold/larger label on the largest single decrease + create real visual hierarchy and a clear narrative focal point + - rich() mixed-format subtitle is a Makie-idiomatic storytelling touch that ties + the headline numbers back into the chart + - Theme-adaptive total-bar color (INK) keeps start/end bars distinct from data bars + in both themes without introducing an off-palette color + - Canvas lands exactly on the 3200x1800 target with no clipping in either render + weaknesses: + - Total bars (Starting Balance, Net Profit) are colored via the ink/chrome token + rather than a dedicated categorical tone; visually distinct and legible, but leans + on a chrome color for a data-encoding role + - Design Excellence is solid but still close to the documented style-guide skeleton; + a bit more customization (e.g. a distinct total-bar hue, subtle bar corner rounding) + would push DE-01 further + image_description: |- + Light render (plot-light.png): + Background: Warm off-white, consistent with #FAF8F1 + Chrome: Title "Quarterly Profit Bridge · waterfall-basic · julia · makie · anyplot.ai", rich-text subtitle, axis ticks/labels, legend (Increase/Decrease/Total) all in dark ink — all clearly readable + Data: First series (Increase) is Imprint #009E73 green; Decrease bars use semantic anchor #AE3030 red; Total bars (Starting Balance, Net Profit) are ink/near-black; dotted connector lines link cumulative levels between bars + Legibility verdict: PASS + + Dark render (plot-dark.png): + Background: Warm near-black, consistent with #1A1A17 + Chrome: Same title/subtitle/axis/legend elements now rendered in light ink against the dark surface — no dark-on-dark issues; Total bars flip to warm off-white/cream + Data: Green (#009E73) and red (#AE3030) data bars are pixel-identical to the light render; only chrome (background, ink, total-bar color) adapts + Legibility verdict: PASS + criteria_checklist: + visual_quality: + score: 29 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 7 + max: 8 + passed: true + comment: All text explicitly sized and readable in both themes + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No collisions between labels, connectors, subtitle, or legend + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: 7 bars well-sized and separated for sparse dataset + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Green/red reinforced by legend and value labels, not sole signal + - id: VQ-05 + name: Layout & Canvas + score: 4 + max: 4 + passed: true + comment: Confirmed exact 3200x1800 canvas, no overflow + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Y-axis carries units, title matches mandated format + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: 'First series #009E73, semantic exception for loss, correct theme + backgrounds' + design_excellence: + score: 15 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: Custom palette, theme-adaptive total color, rich() subtitle + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Spines removed, subtle grid, generous whitespace + - id: DE-03 + name: Data Storytelling + score: 5 + max: 6 + passed: true + comment: Connector lines and emphasized biggest-decrease label create a clear + focal point + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct waterfall/bridge chart + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Color coding, connector lines, total bars, running-total labels all + present + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X=category, Y=cumulative amount, all 7 steps shown + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title format matches, legend labels match encoding + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Increases, decreases, totals, connectors, labels all shown + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Plausible neutral quarterly profit-bridge scenario + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: Sensible $M-range financial values + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Flat top-level script + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Deterministic hardcoded data + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: All imports used + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Appropriate complexity, no fake UI + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves plot-$(THEME).png with current API + library_mastery: + score: 9 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 5 + max: 5 + passed: true + comment: Idiomatic Axis/barplot!/lines!/text!/Legend composition + - id: LM-02 + name: Distinctive Features + score: 4 + max: 5 + passed: true + comment: Uses rich() to mix weight/color inline for subtitle + verdict: APPROVED +impl_tags: + dependencies: [] + techniques: + - waterfall-connectors + - value-labels + - rich-text-annotation + - conditional-emphasis + patterns: + - cumulative-calculation + - conditional-coloring + dataprep: [] + styling: + - publication-ready + - theme-adaptive-chrome