Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions plots/horizon-basic/implementations/julia/makie.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# anyplot.ai
# horizon-basic: Horizon Chart
# Library: makie 0.21.9 | Julia 1.11.9
# Quality: 89/100 | Created: 2026-08-18

using CairoMakie
using Colors
using ColorSchemes
using Random
using Statistics

Random.seed!(42)

# --- Theme tokens -------------------------------------------------------
THEME = get(ENV, "ANYPLOT_THEME", "light")
PAGE_BG = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17"
INK = THEME == "light" ? colorant"#1A1A17" : colorant"#F0EFE8"
INK_SOFT = THEME == "light" ? colorant"#4A4A44" : colorant"#B8B7B0"

# Imprint diverging colormap (red -> midpoint -> blue). Horizon bands use its
# two endpoints: blue for positive deviation, red for negative deviation.
midpoint = THEME == "light" ? colorant"#FAF8F1" : colorant"#1A1A17"
ANYPLOT_DIV = cgrad([colorant"#AE3030", midpoint, colorant"#4467A3"])
POS_COLOR = get(ANYPLOT_DIV, 1.0)
NEG_COLOR = get(ANYPLOT_DIV, 0.0)

# --- Data -----------------------------------------------------------------
# Simulated CPU-load deviation (percentage points from a 50% baseline) for a
# small fleet of servers, sampled hourly.
servers = ["web-1", "web-2", "web-3", "api-1", "api-2", "api-3",
"db-1", "db-2", "cache-1", "cache-2", "queue-1", "lb-1"]
n_series = length(servers)
n_points = 200
hours = collect(0:(n_points - 1))

deviations = Matrix{Float64}(undef, n_points, n_series)
for j in 1:n_series
scale = 5.0 + 4.0 * rand()
walk = cumsum(randn(n_points) .* scale .* 0.18)
walk .-= mean(walk)
deviations[:, j] = clamp.(walk, -45.0, 45.0)
end

n_bands = 3
max_abs = maximum(abs.(deviations))
band_step = max_abs / n_bands
band_alphas = [0.35, 0.65, 1.0]

row_pitch = 1.0
row_h = 0.78

# --- Plot -------------------------------------------------------------------
fig = Figure(
resolution = (1600, 900),
fontsize = 14,
backgroundcolor = PAGE_BG,
)

Label(
fig[1, 1],
"Server CPU-load deviation from baseline · 3 intensity bands per polarity — darker = larger swing";
fontsize = 13,
color = INK_SOFT,
halign = :left,
tellwidth = false,
)

ax = Axis(
fig[2, 1];
title = "horizon-basic · julia · makie · anyplot.ai",
titlesize = 20,
titlecolor = INK,
xlabel = "Time (hours)",
xlabelcolor = INK,
xlabelsize = 14,
xticklabelcolor = INK_SOFT,
xticklabelsize = 12,
yticklabelcolor = INK_SOFT,
yticklabelsize = 12,
backgroundcolor = PAGE_BG,
topspinevisible = false,
rightspinevisible = false,
leftspinevisible = false,
bottomspinecolor = INK_SOFT,
xgridvisible = false,
ygridvisible = false,
yticksvisible = false,
xtickcolor = INK_SOFT,
)

for i in 1:n_series
row_index = n_series - i + 1
row_base = (row_index - 1) * row_pitch
v = deviations[:, i]

for b in 1:n_bands
lower = (b - 1) * band_step
upper = b * band_step

pos_frac = (clamp.(v, lower, upper) .- lower) ./ band_step
y_top = row_base .+ pos_frac .* row_h
y_bottom = fill(row_base, n_points)
band!(ax, hours, y_bottom, y_top; color = (POS_COLOR, band_alphas[b]))

neg_frac = (clamp.(.-v, lower, upper) .- lower) ./ band_step
y_bottom2 = row_base .+ row_h .- neg_frac .* row_h
y_top2 = fill(row_base + row_h, n_points)
band!(ax, hours, y_bottom2, y_top2; color = (NEG_COLOR, band_alphas[b]))
end
end

for i in 0:n_series
y = i * row_pitch
hlines!(ax, [y]; color = RGBAf(INK.r, INK.g, INK.b, 0.12), linewidth = 1)
end

row_centers = [(n_series - i) * row_pitch + row_h / 2 for i in 1:n_series]
ax.yticks = (row_centers, servers)

xlims!(ax, 0, n_points - 1)
ylims!(ax, 0, n_series * row_pitch)

rowsize!(fig.layout, 1, Auto(false, 0.05))

# --- Save -------------------------------------------------------------------
save("plot-$(THEME).png", fig; px_per_unit = 2)
264 changes: 264 additions & 0 deletions plots/horizon-basic/metadata/julia/makie.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
library: makie
language: julia
specification_id: horizon-basic
created: '2026-08-18T01:08:53Z'
updated: '2026-08-18T01:15:08Z'
generated_by: claude-sonnet
workflow_run: 32086405520
issue: 1877
language_version: 1.11.9
library_version: 0.21.9
preview_url_light: https://storage.googleapis.com/anyplot-images/plots/horizon-basic/julia/makie/plot-light.png
preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/horizon-basic/julia/makie/plot-dark.png
preview_html_light: null
preview_html_dark: null
quality_score: 89
review:
strengths:
- Excellent horizon-chart folding technique via band! with 3 alpha-graded intensity
bands per polarity (0.35/0.65/1.0), cleanly compacting 12 servers' CPU-load deviation
into a compact, readable panel.
- 'Correct, theme-adaptive use of the Imprint diverging colormap endpoints (blue
#4467A3 / red #AE3030) for positive/negative deviation, matching the spec''s suggested
mirrored polarity coloring, with data colors identical across light and dark themes.'
- 'Clean, minimal chrome: all spines removed except a soft bottom line, no grid,
subtle theme-adaptive row dividers (alpha 0.12), generous whitespace, and explicit
font sizing throughout (title/subtitle/axis/tick all set).'
- Realistic, neutral server-monitoring dataset with per-server volatility variation
(scale = 5-9), giving genuinely different visual behavior per row rather than
repeating the same shape — good feature coverage of both polarities and all three
intensity levels.
- Idiomatic Makie usage of the Figure grid layout system (Label + Axis across fig[1,1]/fig[2,1]
with rowsize!) for a custom two-row composition — distinctive to Makie's layout
model.
weaknesses:
- No legend or annotation clarifies which color represents positive vs. negative
deviation (blue = above baseline, red = below baseline) — a viewer unfamiliar
with horizon-chart conventions cannot decode polarity from the chart alone. The
subtitle only explains 'darker = larger swing', not the color-to-polarity mapping.
Add a compact 2-swatch legend (or extend the subtitle) stating the mapping explicitly.
- The intensity bands communicate only relative magnitude ('darker = larger swing')
with no indication of the underlying scale (percentage points per band) — consider
a small band-scale legend or annotation so the shading is quantitatively interpretable.
- 'Layout order is slightly unconventional: the small gray caption (Label in fig[1,1])
renders above the bold mandated title (Axis title in fig[2,1]) rather than below
it as a typical subtitle would — consider swapping the visual hierarchy so the
mandated title reads first.'
image_description: |-
Light render (plot-light.png):
Background: Warm off-white, consistent with #FAF8F1 — not pure white.
Chrome: Small gray caption at top-left ("Server CPU-load deviation from baseline · 3 intensity bands per polarity — darker = larger swing") in soft ink; bold dark title "horizon-basic · julia · makie · anyplot.ai" centered above the plot area; 12 server-name row labels (web-1/2/3, api-1/2/3, db-1/2, cache-1/2, queue-1, lb-1) in soft gray on the left; x-axis "Time (hours)" with tick labels 0/50/100/150 in soft gray. All text renders dark-on-light and is clearly legible.
Data: Each row folds a single time series into stacked, alpha-graded bands — blue (#4467A3, the Imprint diverging colormap's positive endpoint) for above-baseline deviation, red (#AE3030, the negative endpoint) for below-baseline, with 3 increasing-alpha bands (0.35/0.65/1.0) per polarity so darker = larger swing. Thin light-gray horizontal dividers separate rows. No legend explains the color-to-polarity mapping.
Legibility verdict: PASS — all chrome text is clearly readable against the light background; no dark-on-dark or light-on-light issues.

Dark render (plot-dark.png):
Background: Warm near-black, consistent with #1A1A17 — not pure black.
Chrome: Same caption and title, now rendered in light gray / off-white; row labels and axis tick labels in light gray; all text is clearly legible against the dark background — no dark-on-dark failures observed.
Data: Identical blue (#4467A3) and red (#AE3030) band colors and alpha levels as the light render — confirms the diverging colormap endpoints are theme-independent as required; only the chrome (background, text, row-divider color) flips between themes.
Legibility verdict: PASS — all text remains readable in the dark theme, and the data colors are confirmed identical to the light render.
criteria_checklist:
visual_quality:
score: 29
max: 30
items:
- id: VQ-01
name: Text Legibility
score: 7
max: 8
passed: true
comment: All font sizes explicitly set (titlesize=20, xlabelsize=14, tick
sizes=12), readable in both themes; 12 dense rows make the 12pt row/tick
labels tight for mobile legibility.
- id: VQ-02
name: No Overlap
score: 6
max: 6
passed: true
comment: No overlap between row labels, ticks, title, or data bands.
- id: VQ-03
name: Element Visibility
score: 6
max: 6
passed: true
comment: Band fills are clearly visible and well-adapted to 200 points x 12
series.
- id: VQ-04
name: Color Accessibility
score: 2
max: 2
passed: true
comment: Blue/red diverging pair gives strong hue+lightness contrast, not
red-green-only.
- id: VQ-05
name: Layout & Canvas
score: 4
max: 4
passed: true
comment: Chart fills the vast majority of the canvas with balanced margins;
nothing cut off.
- id: VQ-06
name: Axis Labels & Title
score: 2
max: 2
passed: true
comment: Title format correct; x-axis 'Time (hours)' has units; y-axis uses
descriptive server names.
- id: VQ-07
name: Palette Compliance
score: 2
max: 2
passed: true
comment: Band colors are the Imprint imprint_div diverging colormap's endpoints
(blue/red), a legitimate fit for genuinely diverging deviation data; theme-adaptive
chrome and PAGE_BG are correct in both renders; data colors identical across
themes.
design_excellence:
score: 15
max: 20
items:
- id: DE-01
name: Aesthetic Sophistication
score: 6
max: 8
passed: true
comment: Thoughtful alpha-graded diverging color mapping and minimal typography
hierarchy, clearly above library defaults.
- id: DE-02
name: Visual Refinement
score: 5
max: 6
passed: true
comment: Spines removed, no grid, subtle theme-adaptive row dividers, generous
whitespace.
- id: DE-03
name: Data Storytelling
score: 4
max: 6
passed: true
comment: Color-intensity hierarchy lets a viewer spot anomalous/volatile servers
at a glance, though no explicit annotation calls one out.
spec_compliance:
score: 13
max: 15
items:
- id: SC-01
name: Plot Type
score: 5
max: 5
passed: true
comment: 'Correct horizon chart: many series folded into color-coded bands.'
- id: SC-02
name: Required Features
score: 3
max: 4
passed: true
comment: 3 bands, mirrored polarity coloring, and a meaningful (zero) baseline
are all present, but the polarity mapping is never communicated to the viewer.
- id: SC-03
name: Data Mapping
score: 3
max: 3
passed: true
comment: Time on x, series as rows, deviation folded correctly; all data visible.
- id: SC-04
name: Title & Legend
score: 2
max: 3
passed: true
comment: Title format is exactly correct; no legend exists to confirm color/polarity
mapping.
data_quality:
score: 15
max: 15
items:
- id: DQ-01
name: Feature Coverage
score: 6
max: 6
passed: true
comment: Both polarities and all 3 intensity levels shown; per-server volatility
varies so rows are visually distinct.
- id: DQ-02
name: Realistic Context
score: 5
max: 5
passed: true
comment: Neutral, realistic server-fleet CPU monitoring scenario.
- id: DQ-03
name: Appropriate Scale
score: 4
max: 4
passed: true
comment: Deviation range (±45pp around a 50% baseline) is plausible for CPU
load monitoring.
code_quality:
score: 10
max: 10
items:
- id: CQ-01
name: KISS Structure
score: 3
max: 3
passed: true
comment: Sequential imports -> data -> plot -> save, no functions/classes.
- id: CQ-02
name: Reproducibility
score: 2
max: 2
passed: true
comment: Random.seed!(42) set.
- id: CQ-03
name: Clean Imports
score: 2
max: 2
passed: true
comment: All imports (CairoMakie, Colors, ColorSchemes, Random, Statistics)
are used.
- id: CQ-04
name: Code Elegance
score: 2
max: 2
passed: true
comment: Nested band/polarity loops are appropriate complexity for a horizon
chart; no fake functionality.
- id: CQ-05
name: Output & API
score: 1
max: 1
passed: true
comment: Saves plot-$(THEME).png with current save/px_per_unit API.
library_mastery:
score: 7
max: 10
items:
- id: LM-01
name: Idiomatic Usage
score: 4
max: 5
passed: true
comment: Idiomatic use of band!, hlines!, Axis, and the Figure grid layout
system.
- id: LM-02
name: Distinctive Features
score: 3
max: 5
passed: true
comment: band! fill-between plus Figure grid layout (Label + rowsize!) for
a custom two-row composition are distinctive to Makie.
verdict: REJECTED
impl_tags:
dependencies: []
techniques:
- manual-ticks
- layer-composition
patterns:
- data-generation
- iteration-over-groups
- matrix-construction
dataprep:
- cumulative-sum
- normalization
- time-series
styling:
- alpha-blending
- minimal-chrome