feat(plot): vertical y-axis label via a rotated text run

This commit is contained in:
Martino Ferrari committed 2026-09-28 11:14:36 +02:00
1 parent 1cb5970757
commit daeb31b1ea
6 files changed
+453 -80

No files matched your search

+13 -7
View File
@@ -27,7 +27,12 @@ struct PlotLayout {
Rect axes;
Rect y_label_area;
Rect x_label_area;
/// One text line each, just outside the matching label area. Zero-sized (and
/// The y title's strip is a *column*: one text line wide, the axes' height
/// tall, just outside `y_label_area`. The y title is drawn rotated to read
/// bottom-to-top, so what it needs is a line's height rather than the run's own
/// width -- a horizontal run's width is what this strip had to reserve before,
/// and a long title then ate the plot. The x title stays horizontal and gets a
/// row of the same height below `x_label_area`. Both are zero-sized (and
/// unoccupied) when no axis of that direction has a title.
Rect y_title_area;
Rect x_title_area;
@@ -39,10 +44,10 @@ struct PlotLayout {
/// is reserved. Every titled axis of a direction gets its own text line inside
/// its strip -- which is what keeps a Left/Right (or Bottom/Top) pair from
/// drawing two titles over each other -- so `x_title_height` is one line per
/// titled x axis; `y_title_width` is the widest y title plus the padding, since
/// y titles are horizontal runs that stack down the axes-tall strip and need
/// width, not another row of it. With no text backend there is no title to
/// measure and both stay 0.0f.
/// titled x axis and `y_title_width` is one line per titled y axis too: a
/// rotated title occupies length along the axes' height, so a second titled y
/// axis takes a *column* beside the first rather than another row of width. With
/// no text backend there is no title to lay out at all and both stay 0.0f.
f32 y_title_width = 0.0f;
f32 x_title_height = 0.0f;
};
@@ -254,8 +259,9 @@ class Plot {
/// Widest y tick label plus the label padding on both sides: the left strip the
/// text backend asks for. Zero with no backend or no y axis.
f32 y_label_width() const;
/// Widest y axis title plus the label padding on both sides: the vertical strip
/// a horizontal y title fits in. Zero with no backend or no titled y axis.
/// One text line of width per titled y axis, plus the label padding on both
/// sides: the column the rotated y title is drawn in, one column per titled
/// axis. Zero with no backend or no titled y axis.
f32 y_title_strip_width() const;
Rect viewport_{};
+16 -9
View File
@@ -261,15 +261,22 @@ class Renderer {
/// Lays one run of text out at a framebuffer position and stages it, shifting
/// it into viewport space. Returns the pen past the run.
Vec2 stage_text(Vec2 pos_px, const char* utf8);
/// Width of the longest prefix of `utf8` that fits `budget_px`, at the size the
/// text pipeline draws with: exactly what `stage_text_clipped()` stages.
f32 clipped_text_width(const char* utf8, f32 budget_px) const;
/// Stages as much of `utf8` as fits `budget_px` at `pos_px` and returns the
/// width it staged. Titles are the runs that need this: a title longer than the
/// strip the layout reserved for it is cut there, rather than being allowed to
/// grow the strip (which would shrink the plot) or to spill over the tick labels
/// and the data (which is the defect the strips exist to prevent).
f32 stage_text_clipped(Vec2 pos_px, const char* utf8, f32 budget_px);
/// Width of the longest prefix of `utf8` that fits `budget_px` along `axis`, at
/// the size the text pipeline draws with: exactly what `stage_text_clipped()`
/// stages.
f32 clipped_text_width(const char* utf8, f32 budget_px, Vec2 axis) const;
/// Stages as much of `utf8` as fits `budget_px` along `axis` at `pos_px` and
/// returns the length it staged. Titles are the runs that need this: a title
/// longer than the strip the layout reserved for it is cut there, rather than
/// being allowed to grow the strip (which would shrink the plot) or to spill
/// over the tick labels and the data (which is the defect the strips exist to
/// prevent).
///
/// `axis` is the run's own direction and a unit vector: (1, 0) for the
/// horizontal x title, which spends the strip's width, and (0,-1) for the y
/// title, which reads bottom-to-top and spends the strip's height. `pos_px` is
/// the pen -- for the rotated run, the bottom-left corner of its first cell.
f32 stage_text_clipped(Vec2 pos_px, const char* utf8, f32 budget_px, Vec2 axis);
void draw_grid(const Plot& plot);
void draw_axis_lines(const Plot& plot);