fix(plot): a figure row's strip shows labels and title independently, and the shared axis is the bottom row's strip
This commit is contained in:
1 parent
8f1e60a105
commit
770da4c2b2
9 files changed
+415
-175
No files matched your search
+31
-33
@@ -299,28 +299,13 @@ const FigureLayout& Figure::layout() {
|
||||
}
|
||||
|
||||
const f32 figure_h = std::max(0.0f, viewport_.h);
|
||||
// The shared strip's height is what one of these rows' own x label strip would ask
|
||||
// for, and only a plot can measure that: it is the text backend's line height plus
|
||||
// the label padding, clamped against the area it has. So the last row is laid out
|
||||
// against the whole figure once, with its own strip switched back on, and its
|
||||
// answer is what the figure reserves. The probe's viewport is the figure's rather
|
||||
// than the row's on purpose -- the strip belongs to the figure, so the row's own
|
||||
// budget must not shrink it.
|
||||
//
|
||||
// That probe is also why the strip cannot eat the rows, and why there is no second
|
||||
// clamp here to say so: the plot's own chrome cap (`Plot::layout()`'s kMinAxesShare)
|
||||
// already holds a label strip to half of the viewport it was laid out against, and
|
||||
// the viewport it was laid out against is the figure. A figure therefore always has
|
||||
// at least half its height to divide between the rows.
|
||||
f32 strip_h = 0.0f;
|
||||
if (labels_bottom_only_) {
|
||||
Plot& probe = plots_.back();
|
||||
probe.set_viewport(viewport_);
|
||||
probe.set_x_label_strip(true);
|
||||
strip_h = probe.layout().x_axis_height;
|
||||
}
|
||||
|
||||
const f32 content_h = figure_h - strip_h;
|
||||
// The rows tile the figure itself: the shared x axis is the *bottom row's own*, so
|
||||
// nothing is taken off the figure for it. That is what keeps the axis reachable --
|
||||
// its strip is a real `PlotLayout::x_label_area` inside the bottom row's rect, which
|
||||
// is the rect the controller's axis-zoom hit test reads and the strip an x title is
|
||||
// drawn in -- and it is why an interior row reserves no x chrome at all instead of
|
||||
// leaving a band no one owns.
|
||||
const f32 content_h = figure_h;
|
||||
// The gap is dropped, rather than squeezing the rows, when the figure cannot hold
|
||||
// it: a figure one pixel tall with four rows still gives every row a positive
|
||||
// height.
|
||||
@@ -332,11 +317,11 @@ const FigureLayout& Figure::layout() {
|
||||
}
|
||||
// ... and the height to divide is clamped non-negative here, so every height below
|
||||
// is a share of a non-negative total and no row rect can come out negative: a row
|
||||
// rect is handed to a plot, and a plot laid out against a negative one is not a
|
||||
// case that layout arithmetic is defined for. The clamp cannot fire -- the strip is
|
||||
// at most half the figure (above), so there is always at least half a figure of
|
||||
// height to divide -- and it is what keeps that reasoning in the arithmetic rather
|
||||
// than in a comment.
|
||||
// rect is handed to a plot, and a plot laid out against a negative one is not a case
|
||||
// that layout arithmetic is defined for. Nothing above can make it negative -- the
|
||||
// figure's height is clamped at zero and a gap that would eat it is dropped, both
|
||||
// just above -- so the clamp is the arithmetic carrying its own invariant rather
|
||||
// than a comment asking the reader to carry it.
|
||||
usable = std::max(0.0f, usable);
|
||||
|
||||
// The floor, then the weights' share of what is left. Giving every row the floor
|
||||
@@ -363,14 +348,23 @@ const FigureLayout& Figure::layout() {
|
||||
consumed = end;
|
||||
y = layout_.rows[row].bottom() + gap;
|
||||
}
|
||||
// The gaps are only *between* rows: the rows advance by `gap`, but they divide
|
||||
// `usable`, which is already the figure less every gap, so the last row lands on the
|
||||
// figure's bottom edge for any `row_gap_` -- the figure is tiled, not padded at the
|
||||
// end. `row_gap` reports the gap actually left, which is 0 when the figure was too
|
||||
// short to hold the one asked for.
|
||||
layout_.row_gap = gap;
|
||||
|
||||
const u32 bottom_row = rows - 1u;
|
||||
for (u32 row = 0; row < rows; ++row) {
|
||||
Plot& p = plots_[row];
|
||||
p.set_viewport(layout_.rows[row]);
|
||||
// The rows give their x label strip to the figure's one shared axis; with the
|
||||
// shared axis switched off each row keeps its own and draws its own labels.
|
||||
p.set_x_label_strip(!labels_bottom_only_);
|
||||
// One x axis for the whole figure, and it is the bottom row's: every row keeps its
|
||||
// x chrome -- labels, title and the strip the controller's axis zoom is found in --
|
||||
// and every other row gives all of it to that one, so the labels and the title are
|
||||
// not drawn once per row. A figure told to label every row keeps them everywhere,
|
||||
// and then there is no shared axis to speak of: each row has its own.
|
||||
p.set_x_strip_mode(labels_bottom_only_ && row != bottom_row ? XStrip::None : XStrip::Both);
|
||||
}
|
||||
// After the viewports, so a row's ticks and grid -- built from its axes rect -- and
|
||||
// its transforms describe this frame's rects; and before the rows lay themselves
|
||||
@@ -383,9 +377,13 @@ const FigureLayout& Figure::layout() {
|
||||
// is the one that computes its own.
|
||||
for (u32 row = 0; row < rows; ++row) plots_[row].layout();
|
||||
|
||||
// The strip hugs the figure's bottom, directly under the last row (a gap, if any,
|
||||
// separates the two exactly as it separates two rows).
|
||||
layout_.x_axis_strip = Rect{viewport_.x, viewport_.bottom() - strip_h, viewport_.w, strip_h};
|
||||
// The figure's one x axis is the bottom row's, so the strip it is drawn in is that
|
||||
// row's own label area -- the rect the controller hit-tests and the renderer stages
|
||||
// its labels in, read back here rather than derived again, so a host and the frame
|
||||
// cannot disagree about where the shared axis is. It is zero-sized only when there
|
||||
// are no rows at all: with `set_x_labels_only_on_bottom(false)` it is simply the last
|
||||
// row's own strip, one of several.
|
||||
layout_.x_axis_strip = plots_.back().layout().x_label_area;
|
||||
return layout_;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user