From 243f3c40bffef701b30eaf13e8be57d51002dfc1 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Fri, 26 Jun 2026 18:02:13 -0700 Subject: [PATCH] add axis labels to return backtest --- src/charts/forecast_chart.zig | 11 +++++++---- src/charts/text.zig | 13 ++++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/charts/forecast_chart.zig b/src/charts/forecast_chart.zig index 57c6ca8..b87cede 100644 --- a/src/charts/forecast_chart.zig +++ b/src/charts/forecast_chart.zig @@ -97,9 +97,12 @@ pub fn renderConvergenceChart( }; } -/// Thin RGB wrapper over `renderBacktestToSurface` for the TUI's -/// kitty path: renders without axis labels, extracts RGB, frees the -/// surface. +/// RGB wrapper over `renderBacktestToSurface` for the inline kitty +/// path (CLI + TUI). Renders WITH axis labels baked in: unlike the +/// projection chart - whose callers stamp their own labels into the +/// cell grid - nothing draws the forecast chart's labels separately, +/// so the percent y-ticks + date x-endpoints go into the surface here +/// (matching the PNG export) to show the divergence magnitude. pub fn renderBacktestChart( io: std.Io, alloc: std.mem.Allocator, @@ -108,7 +111,7 @@ pub fn renderBacktestChart( height_px: u32, th: theme.Theme, ) !ChartResult { - var rendered = try renderBacktestToSurface(io, alloc, anchors, width_px, height_px, th, false); + var rendered = try renderBacktestToSurface(io, alloc, anchors, width_px, height_px, th, true); defer rendered.deinit(alloc); return .{ .rgb_data = try rendered.extractRgb(alloc), diff --git a/src/charts/text.zig b/src/charts/text.zig index f51ecc5..dcdbd89 100644 --- a/src/charts/text.zig +++ b/src/charts/text.zig @@ -11,7 +11,7 @@ //! 2. It keeps a ~hundreds-of-KB TTF (and its license) out of the repo. //! //! The glyph set is intentionally minimal - just what axis labels and -//! chart legends need: digits, `$`, `.`, `,`, `-`, the `T`/`B`/`M` +//! chart legends need: digits, `$`, `.`, `,`, `-`, `%`, the `T`/`B`/`M` //! magnitude suffixes emitted by `format.fmtLargeNum`, and the //! lowercase letters `t`/`h`/`e`/`n`/`o`/`w` for the comparison //! chart's "then"/"now" legend. Space and unknown chars render blank. @@ -57,6 +57,7 @@ const glyph_dollar: Glyph = .{ 0x04, 0x0E, 0x14, 0x0E, 0x05, 0x0E, 0x04 }; const glyph_period: Glyph = .{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06 }; const glyph_comma: Glyph = .{ 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x08 }; const glyph_minus: Glyph = .{ 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00 }; +const glyph_percent: Glyph = .{ 0x19, 0x1A, 0x02, 0x04, 0x08, 0x13, 0x03 }; const glyph_T: Glyph = .{ 0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 }; const glyph_B: Glyph = .{ 0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E }; const glyph_M: Glyph = .{ 0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11 }; @@ -80,6 +81,7 @@ fn glyphFor(ch: u8) Glyph { '.' => glyph_period, ',' => glyph_comma, '-' => glyph_minus, + '%' => glyph_percent, 'T' => glyph_T, 'B' => glyph_B, 'M' => glyph_M, @@ -237,3 +239,12 @@ test "drawText renders the lowercase legend letters (then/now)" { drawText(&sfc, 1, 1, 1, white, "thenow"); try testing.expect(draw.countColor(&sfc, white) > 30); } + +test "drawText renders the percent glyph (for return-rate axis labels)" { + const alloc = testing.allocator; + var sfc = try Surface.init(.image_surface_rgb, alloc, 16, 16); + defer sfc.deinit(alloc); + const white = [3]u8{ 0xFF, 0xFF, 0xFF }; + drawText(&sfc, 1, 1, 1, white, "%"); + try testing.expect(draw.countColor(&sfc, white) > 0); +}