fix(render): floor the axes so chrome cannot erase it, one title row per titled axis

Two findings on the pass-1 fix:

- The reserved strips were clamped only to the viewport, so a title wider than a
  narrow panel took the rest of it and the axes collapsed: draw_traces() returned
  on a zero-width rect (no trace drawn, nothing reported) and a zero-height one
  flattened every trace onto a single row. The chrome is now capped at half the
  viewport -- the label strip is served first, the title strip takes what is left
  of that allowance -- an over-long title is clipped to its strip by a walk that
  both measures and stages it, and draw_traces() tests the height as well as the
  width.
- Every titled y axis was staged at the same centred position inside one strip, so
  a Left/Right pair of titles overprinted. Each titled axis now owns a text line:
  the x strip grows one line per titled x axis (x_title_height), and the y runs
  stack down the axes-tall strip, which needs no extra width for it.

Tests: `narrow_panel_with_a_long_y_title_still_draws` and
`short_panel_with_an_x_title_still_draws` pin the floor (the axes keeps half the
panel and the trace still covers the columns, with the title clipped into its
strip), `titled_axis_pairs_get_their_own_rows` pins one text row per titled axis
in both directions, and `layout_stacks_one_title_line_per_titled_axis` pins the
strip sizing. All fail against the pass-1 sources.
This commit is contained in:
Martino Ferrari committed 2026-09-27 23:33:06 +02:00
1 parent 8f45108559
commit 1f5aa11de2
6 files changed
+330 -51

No files matched your search

+24
View File
@@ -99,6 +99,30 @@ PLOT_TEST(layout_derives_label_areas_from_font_metrics) {
PLOT_CHECK_NEAR(l.x_label_area.y, l.axes.bottom(), 1e-4);
}
/// Every titled axis of a direction gets its own text line inside its strip;
/// without that, the second titled axis of an edge is drawn on top of the first.
PLOT_TEST(layout_stacks_one_title_line_per_titled_axis) {
plot::render::BitmapFont font;
plot::Plot p;
p.set_text_backend(&font);
p.set_viewport({0.0f, 0.0f, 800.0f, 400.0f});
const f32 pad = p.style().grid_label_pad;
p.add_x_axis({"seconds", {0.0, 1.0}, false, plot::AxisPlace::Bottom, false});
p.add_x_axis({"samples", {0.0, 1.0}, false, plot::AxisPlace::Top, false});
p.add_y_axis({"volts", {-1.0, 1.0}, false, plot::AxisPlace::Left, false});
p.add_y_axis({"amps", {-1.0, 1.0}, false, plot::AxisPlace::Right, false});
const plot::PlotLayout& l = p.layout();
PLOT_CHECK(p.titled_x_axis_count() == 2u);
PLOT_CHECK(p.titled_y_axis_count() == 2u);
// The x titles stack in rows, so the strip grows by a line per titled axis...
PLOT_CHECK_NEAR(l.x_title_height, 2.0f * font.height() + 2.0f * pad, 1e-4);
// ... while the y titles stack down the axes-tall strip, so the second one costs
// height the axes already has rather than a second column of width.
PLOT_CHECK_NEAR(l.y_title_width,
std::max(font.measure("volts"), font.measure("amps")) + 2.0f * pad, 1e-4);
PLOT_CHECK_NEAR(l.axes.h, 400.0f - (l.x_axis_height + l.x_title_height), 1e-4);
}
/// An axis with no title reserves nothing: the strips exist so that a titled axis
/// has somewhere outside `axes` to draw in, not as a fixed cost per plot.
PLOT_TEST(layout_reserves_no_title_strip_without_a_title) {