Skip to content

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.9 so 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=True plot flags use a local use_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_legend grows the canvas by the legend's height, so the *.png and *_no_legend.png variants have the same axes size.
  • at paper scale the x10^4 offset text collides with the x-axis label, so format_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 on PANEL_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.