fix(plot): a figure gives every row the same y gutter so the axes line up
This commit is contained in:
1 parent
663b57c679
commit
cae672bc99
6 files changed
+348
-5
No files matched your search
+31
-2
@@ -393,12 +393,41 @@ const FigureLayout& Figure::layout() {
|
||||
// its transforms describe this frame's rects; and before the rows lay themselves
|
||||
// out below, so a following group's window is the range they lay out with.
|
||||
sync_x_links();
|
||||
// Every row is laid out against its own rect here, so after this call each row's
|
||||
// Every row is laid out against its own rect, so after this call each row's
|
||||
// chrome is current: `plot(i).layout()` reads back what the figure computed rather
|
||||
// than what the row last held. That is the contract `Renderer::draw(Figure&)`
|
||||
// relies on -- it draws each row with the layout this produced, and `draw(Plot&)`
|
||||
// is the one that computes its own.
|
||||
for (u32 row = 0; row < rows; ++row) plots_[row].layout();
|
||||
//
|
||||
// The rows' axes have to line up (spec §16), so this is where the figure's **common
|
||||
// left gutter** is decided -- a row cannot see its siblings' tick labels, so the one
|
||||
// width they can agree on has to be handed to them from here.
|
||||
//
|
||||
// Two passes, and no more. The first lays every row out with no floor, so each
|
||||
// reports the gutter its own labels and title column need; the figure keeps the
|
||||
// maximum. The second re-lays every row with that maximum imposed on its
|
||||
// `Plot::set_min_y_gutter()`, which widens a row's strip to fill it, so all the rows'
|
||||
// `axes` start and end on the same columns. A fixed-point loop -- re-measuring until
|
||||
// the rows stop moving -- is the tempting wrong answer and is not needed: the gutter
|
||||
// is a *maximum* over rows, and a row's own strip width does not depend on any other
|
||||
// row's or on the width imposed on it (y tick labels are chosen from the row's own
|
||||
// range and height, and a title column is one text line per titled axis), so one read
|
||||
// per row settles the answer exactly. Measuring with a floor from the previous frame
|
||||
// would be the only reason to iterate, and clearing it before the measurement pass is
|
||||
// what removes it.
|
||||
f32 gutter = 0.0f;
|
||||
for (u32 row = 0; row < rows; ++row) {
|
||||
plots_[row].set_min_y_gutter(0.0f);
|
||||
const PlotLayout& row_layout = plots_[row].layout();
|
||||
gutter = std::max(gutter, row_layout.y_axis_width + row_layout.y_title_width);
|
||||
}
|
||||
// A one-row figure imposes exactly what that row measured, so it lays out as the plot
|
||||
// would on its own; a figure whose rows all need the same strip imposes that, which is
|
||||
// also what each would have done alone.
|
||||
for (u32 row = 0; row < rows; ++row) {
|
||||
plots_[row].set_min_y_gutter(gutter);
|
||||
plots_[row].layout();
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
Reference in new issue
Block a user