Visualisation Styling Alignment¶
src.visualisation intentionally mirrors the plotting style used in the
quartz/com and topaz/rl-skeleton visualisation packages.
What is mirrored¶
- palette aliases for the main algorithms
- paper-style matplotlib
rcParams - panel background, grid, and spine colors
- zero-length ticks and scientific step-axis formatting
- shared top legend and figure save padding
- shared header layout now measures the rendered legend height before calling
tight_layout, so wrapped two-row legends do not overlap subplot titles - moderate paper-scale font sizing, lighter default line strokes, tight title/label padding, and capped major x-axis ticks so scaled-down exports stay readable
- PDF and PostScript output embeds TrueType fonts, and axes rasterize artists
below z-order
1.9so background bands and event markers stay compact while the learning curves remain vector graphics - translucent shared-legend handles are composited against the figure background before export, avoiding unstable transparency in vector viewers
paper_panel=Trueplot flags use a localuse_paper_panel_style()preset (heavier grid, spine, and line strokes) and compact axis formatting.
Paper scale (October 2026)¶
The paper includes the learning-curve PNGs directly, so their canvas is chosen
from the printed size rather than for notebook viewing. A paper figure (the
two-panel cumulative and episodic learning curves, and each floor-progression
panel) is drawn on PAPER_FIGSIZE = (5.8, 2.3) inches and printed at
0.45\textwidth (3.15 in of AAMAS's 7 in), a 0.54 scale. The typography in
use_paper_style() (12.5 pt titles, 12 pt labels and legends, 11 pt ticks)
therefore prints at 6-7 pt, the size used by the quartz/com paper panels.
Until October 2026 these figures used an 11.5 x 4.1 in canvas, which printed
their text at about 3 pt.
Consequences that keep the figures consistent:
- the paper includes standalone legends with
\includegraphics[scale=0.54](matplotlib records 300 dpi in the PNG, which pdfTeX honours), so legend text matches the axis labels; a legend PNG's width therefore sets its printed width. finalize_shared_legendgrows the canvas by the legend's height, so the*.pngand*_no_legend.pngvariants have the same axes size.- at paper scale the
x10^4offset text collides with the x-axis label, soformat_step_axis(paper_panel=True)writes the factor into the label ("Timestep (x10^4)") and labels ticks in units of 10^4. Ticks use the steps 1, 2, 2.5, 5, 10 so they fall on round numbers. - shield-level labels sit right of their dashed lines and the world-model label
left of its line, so neither covers the y tick labels or the other; raises
closer than a label's width share one label (
L1–2,draw_level_change_lines). - titles and y-labels are short ("Safety (per episode)", "Violations") because a panel is only 2.9 in of canvas wide at 12 pt.
- per-seed lines use
SEED_PALETTE: tab10 hues other than the IPPO and OmSh colours, chosen so every pair passes the dataviz validator's CVD and normal-vision checks onPANEL_FACE.
scripts/export_paper_figures.py re-renders all of them from a campaign's
histories. The notebooks' visualisation cells produce the same learning curves;
notebooks/utils/experiments.export_learning_curves holds their shared
arguments for scripts.
Why this stays local¶
omsh keeps a local copy of the styling helpers instead of importing from the
sibling rl-engine checkout at runtime.
This keeps notebook usage self-contained, avoids a cross-repo path dependency,
and lets omsh preserve its local plotting behavior such as world-model
boundary markers, shield-level overlays, and shield telemetry-specific layouts.
Expected maintenance flow¶
When the shared graph styling changes in a meaningful way, update
src/visualisation/styling.py and the small presentation helpers in
src/visualisation/common.py to match the quartz/com and
topaz/rl-skeleton reference implementations.
Behavioral plotting logic in omsh does not need to match those repositories
unless we explicitly decide to consolidate the plotting packages more broadly.
Module Placement¶
The plotting package lives under src/ rather than notebooks/ because the
helpers are imported by both marimo notebooks and normal Python test modules.
Keeping the package under src.visualisation makes that shared usage explicit
and avoids treating notebook plotting helpers as notebook-only code.