figure: x axes follow the column structure; split rows keep the right gutter on the last cell only
This commit is contained in:
1 parent
69f0083fc0
commit
2cb31cbb91
6 files changed
+366
-53
No files matched your search
+250
-2
@@ -1257,10 +1257,11 @@ PLOT_TEST(figure_cells_share_x_link_and_crosshair) {
|
||||
}
|
||||
|
||||
PLOT_TEST(figure_cells_x_labels_bottom_row_only) {
|
||||
plot::Figure figure = split_figure({1, 2});
|
||||
// Rows with the same columns share the bottom one's axis: {1,1,1} labels the last row only.
|
||||
plot::Figure figure = split_figure({1, 1, 1});
|
||||
const plot::FigureLayout& fl = figure.layout();
|
||||
PLOT_CHECK(figure.plot(0u).layout().x_label_area.h == 0.0f);
|
||||
PLOT_CHECK(figure.plot(1u).layout().x_label_area.h > 0.0f);
|
||||
PLOT_CHECK(figure.plot(1u).layout().x_label_area.h == 0.0f);
|
||||
PLOT_CHECK(figure.plot(2u).layout().x_label_area.h > 0.0f);
|
||||
PLOT_CHECK_NEAR(fl.x_axis_strip.h, figure.plot(2u).layout().x_label_area.h, 0.001);
|
||||
}
|
||||
@@ -1408,3 +1409,250 @@ PLOT_TEST(figure_row_y_sharing_off_still_gives_every_cell_a_gutter) {
|
||||
figure.layout();
|
||||
PLOT_CHECK(figure.plot(1u).layout().y_axis_width > 0.0f);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- x axes follow the columns
|
||||
|
||||
namespace {
|
||||
|
||||
/// A split figure with a text backend and a y axis of a real magnitude on every plot (so the
|
||||
/// shared y gutter is non-zero), the host's configuration: row-shared y, a 6 px column gap and
|
||||
/// a 35 px right gutter.
|
||||
plot::Figure host_like_figure(const std::vector<plot::u32>& cells, bool share_y = true) {
|
||||
plot::Figure figure = split_figure(cells, false);
|
||||
for (plot::u32 i = 0; i < figure.plot_count(); ++i) give_magnitude_row(figure.plot(i), i);
|
||||
figure.set_col_gap(6.0f);
|
||||
figure.set_right_gutter(35.0f);
|
||||
figure.set_share_row_y(share_y);
|
||||
return figure;
|
||||
}
|
||||
|
||||
bool shows_x_axis(plot::Figure& figure, plot::u32 plot_index) {
|
||||
const plot::PlotLayout& l = figure.plot(plot_index).layout();
|
||||
return l.x_axis_height > 0.0f && l.x_label_area.h > 0.0f;
|
||||
}
|
||||
|
||||
bool hides_x_axis(plot::Figure& figure, plot::u32 plot_index) {
|
||||
const plot::PlotLayout& l = figure.plot(plot_index).layout();
|
||||
return l.x_axis_height == 0.0f && l.x_label_area.h == 0.0f;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PLOT_TEST(figure_x_axis_a_split_row_over_an_unsplit_one_keeps_its_own) {
|
||||
plot::Figure figure = host_like_figure({2, 1});
|
||||
figure.layout();
|
||||
PLOT_CHECK(shows_x_axis(figure, 0u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 1u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 2u));
|
||||
// ... and the other way round.
|
||||
plot::Figure other = host_like_figure({1, 2});
|
||||
other.layout();
|
||||
PLOT_CHECK(shows_x_axis(other, 0u));
|
||||
PLOT_CHECK(shows_x_axis(other, 1u));
|
||||
PLOT_CHECK(shows_x_axis(other, 2u));
|
||||
}
|
||||
|
||||
PLOT_TEST(figure_x_axis_rows_with_the_same_columns_share_the_bottom_axis) {
|
||||
plot::Figure figure = host_like_figure({2, 2});
|
||||
figure.layout();
|
||||
PLOT_CHECK(hides_x_axis(figure, 0u));
|
||||
PLOT_CHECK(hides_x_axis(figure, 1u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 2u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 3u));
|
||||
// The hidden row's cells have exactly the x extents of the cells below, so the axis below
|
||||
// serves them.
|
||||
const plot::FigureLayout& fl = figure.layout();
|
||||
for (plot::u32 c = 0; c < 2u; ++c) {
|
||||
PLOT_CHECK_NEAR(fl.cells[c].x, fl.cells[2u + c].x, 1e-4);
|
||||
PLOT_CHECK_NEAR(fl.cells[c].w, fl.cells[2u + c].w, 1e-4);
|
||||
const plot::PlotLayout& up = figure.plot(c).layout();
|
||||
const plot::PlotLayout& down = figure.plot(2u + c).layout();
|
||||
PLOT_CHECK_NEAR(up.axes.x, down.axes.x, 1e-4);
|
||||
PLOT_CHECK_NEAR(up.axes.w, down.axes.w, 1e-4);
|
||||
PLOT_CHECK_NEAR(up.y_axis_width, down.y_axis_width, 1e-4);
|
||||
}
|
||||
// The same without row-shared y (every cell has its own y strip).
|
||||
plot::Figure own_y = host_like_figure({2, 2}, false);
|
||||
own_y.layout();
|
||||
PLOT_CHECK(hides_x_axis(own_y, 0u) && hides_x_axis(own_y, 1u));
|
||||
PLOT_CHECK(shows_x_axis(own_y, 2u) && shows_x_axis(own_y, 3u));
|
||||
for (plot::u32 c = 0; c < 2u; ++c) {
|
||||
PLOT_CHECK_NEAR(own_y.plot(c).layout().axes.x, own_y.plot(2u + c).layout().axes.x, 1e-4);
|
||||
PLOT_CHECK_NEAR(own_y.plot(c).layout().axes.w, own_y.plot(2u + c).layout().axes.w, 1e-4);
|
||||
}
|
||||
}
|
||||
|
||||
PLOT_TEST(figure_x_axis_matching_compares_normalised_weights) {
|
||||
const std::vector<plot::f32> one_three{1.0f, 3.0f};
|
||||
const std::vector<plot::f32> two_six{2.0f, 6.0f};
|
||||
const std::vector<plot::f32> halves{1.0f, 1.0f};
|
||||
{ // 1:3 over 1:3
|
||||
plot::Figure figure = host_like_figure({2, 2});
|
||||
PLOT_CHECK(figure.set_cell_weights(0u, one_three));
|
||||
PLOT_CHECK(figure.set_cell_weights(1u, one_three));
|
||||
figure.layout();
|
||||
PLOT_CHECK(hides_x_axis(figure, 0u) && hides_x_axis(figure, 1u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 2u) && shows_x_axis(figure, 3u));
|
||||
const plot::FigureLayout& fl = figure.layout();
|
||||
PLOT_CHECK_NEAR(fl.cells[0].w, fl.cells[2].w, 1e-3);
|
||||
PLOT_CHECK_NEAR(figure.plot(1u).layout().axes.x, figure.plot(3u).layout().axes.x, 1e-3);
|
||||
}
|
||||
{ // 1:3 over 2:6 -- normalised, so the same columns
|
||||
plot::Figure figure = host_like_figure({2, 2});
|
||||
PLOT_CHECK(figure.set_cell_weights(0u, one_three));
|
||||
PLOT_CHECK(figure.set_cell_weights(1u, two_six));
|
||||
figure.layout();
|
||||
PLOT_CHECK(hides_x_axis(figure, 0u) && hides_x_axis(figure, 1u));
|
||||
}
|
||||
{ // 1:3 over 1:1
|
||||
plot::Figure figure = host_like_figure({2, 2});
|
||||
PLOT_CHECK(figure.set_cell_weights(0u, one_three));
|
||||
PLOT_CHECK(figure.set_cell_weights(1u, halves));
|
||||
figure.layout();
|
||||
PLOT_CHECK(shows_x_axis(figure, 0u) && shows_x_axis(figure, 1u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 2u) && shows_x_axis(figure, 3u));
|
||||
}
|
||||
{ // 1:3 over (no weights = equal)
|
||||
plot::Figure figure = host_like_figure({2, 2});
|
||||
PLOT_CHECK(figure.set_cell_weights(0u, one_three));
|
||||
figure.layout();
|
||||
PLOT_CHECK(shows_x_axis(figure, 0u) && shows_x_axis(figure, 1u));
|
||||
}
|
||||
{ // explicit 1:1 over (no weights = equal)
|
||||
plot::Figure figure = host_like_figure({2, 2});
|
||||
PLOT_CHECK(figure.set_cell_weights(0u, halves));
|
||||
figure.layout();
|
||||
PLOT_CHECK(hides_x_axis(figure, 0u) && hides_x_axis(figure, 1u));
|
||||
}
|
||||
{ // a different number of cells never matches, whatever the weights
|
||||
plot::Figure figure = host_like_figure({3, 2});
|
||||
figure.layout();
|
||||
PLOT_CHECK(shows_x_axis(figure, 0u) && shows_x_axis(figure, 2u));
|
||||
}
|
||||
}
|
||||
|
||||
PLOT_TEST(figure_x_axis_unsplit_rows_are_unchanged) {
|
||||
plot::Figure figure = host_like_figure({1, 1, 1});
|
||||
figure.layout();
|
||||
PLOT_CHECK(hides_x_axis(figure, 0u));
|
||||
PLOT_CHECK(hides_x_axis(figure, 1u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 2u));
|
||||
PLOT_CHECK_NEAR(figure.layout().x_axis_strip.h, figure.plot(2u).layout().x_label_area.h, 1e-4);
|
||||
}
|
||||
|
||||
PLOT_TEST(figure_x_axis_follows_a_chain_of_matching_rows) {
|
||||
{ // [2]/[2]/[1]: row 0 matches row 1 and hides; row 1 does not match row 2 and shows.
|
||||
plot::Figure figure = host_like_figure({2, 2, 1});
|
||||
figure.layout();
|
||||
PLOT_CHECK(hides_x_axis(figure, 0u) && hides_x_axis(figure, 1u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 2u) && shows_x_axis(figure, 3u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 4u));
|
||||
}
|
||||
{ // [2]/[2]/[2]: transitive -- only the last row carries the axis.
|
||||
plot::Figure figure = host_like_figure({2, 2, 2});
|
||||
figure.layout();
|
||||
for (plot::u32 i = 0; i < 4u; ++i) PLOT_CHECK(hides_x_axis(figure, i));
|
||||
PLOT_CHECK(shows_x_axis(figure, 4u) && shows_x_axis(figure, 5u));
|
||||
}
|
||||
{ // [1]/[2]/[2]: the unsplit row matches nothing below it.
|
||||
plot::Figure figure = host_like_figure({1, 2, 2});
|
||||
figure.layout();
|
||||
PLOT_CHECK(shows_x_axis(figure, 0u));
|
||||
PLOT_CHECK(hides_x_axis(figure, 1u) && hides_x_axis(figure, 2u));
|
||||
PLOT_CHECK(shows_x_axis(figure, 3u) && shows_x_axis(figure, 4u));
|
||||
}
|
||||
{ // the figure's x_axis_strip is still the last plot's label area
|
||||
plot::Figure figure = host_like_figure({2, 1});
|
||||
const plot::FigureLayout& fl = figure.layout();
|
||||
PLOT_CHECK(fl.x_axis_strip.h > 0.0f);
|
||||
PLOT_CHECK_NEAR(fl.x_axis_strip.h, figure.plot(2u).layout().x_label_area.h, 1e-4);
|
||||
}
|
||||
}
|
||||
|
||||
PLOT_TEST(figure_x_axis_labels_on_every_row_when_asked) {
|
||||
plot::Figure figure = host_like_figure({2, 2, 1});
|
||||
figure.set_x_labels_only_on_bottom(false);
|
||||
figure.layout();
|
||||
for (plot::u32 i = 0; i < figure.plot_count(); ++i) PLOT_CHECK(shows_x_axis(figure, i));
|
||||
}
|
||||
|
||||
// -------------------------------------------- the gap between cells of a row
|
||||
|
||||
PLOT_TEST(figure_split_row_gap_between_plot_frames_is_the_column_gap) {
|
||||
for (const plot::u32 n : {2u, 3u, 4u}) {
|
||||
plot::Figure figure = host_like_figure({n});
|
||||
const plot::FigureLayout& fl = figure.layout();
|
||||
PLOT_CHECK_NEAR(fl.col_gap, 6.0, 1e-4);
|
||||
for (plot::u32 k = 0; k + 1u < n; ++k) {
|
||||
PLOT_CHECK_NEAR(figure.plot(k + 1u).layout().axes.x - figure.plot(k).layout().axes.right(),
|
||||
6.0, 1e-3);
|
||||
PLOT_CHECK_NEAR(fl.cells[k + 1u].x - fl.cells[k].right(), 6.0, 1e-3);
|
||||
// Only the last cell holds the right gutter.
|
||||
PLOT_CHECK_NEAR(figure.plot(k).layout().right_gutter, 0.0, 1e-6);
|
||||
}
|
||||
// Equal weights give equal axes widths, the first cell included.
|
||||
for (plot::u32 k = 1; k < n; ++k) {
|
||||
PLOT_CHECK_NEAR(figure.plot(k).layout().axes.w, figure.plot(0u).layout().axes.w, 0.6);
|
||||
}
|
||||
// The cells tile the row exactly.
|
||||
PLOT_CHECK_NEAR(fl.cells[0].x, fl.rows[0].x, 1e-4);
|
||||
PLOT_CHECK_NEAR(fl.cells[n - 1u].right(), fl.rows[0].right(), 1e-3);
|
||||
// The right gutter is still there, on the last cell.
|
||||
const plot::PlotLayout& last = figure.plot(n - 1u).layout();
|
||||
PLOT_CHECK_NEAR(fl.cells[n - 1u].right() - last.axes.right(), 35.0, 1e-3);
|
||||
PLOT_CHECK_NEAR(last.right_gutter, 35.0, 1e-3);
|
||||
// The y gutter still sits on the first cell alone.
|
||||
PLOT_CHECK(figure.plot(0u).layout().axes.x > fl.cells[0].x);
|
||||
PLOT_CHECK_NEAR(figure.plot(1u).layout().axes.x, fl.cells[1].x, 1e-4);
|
||||
}
|
||||
}
|
||||
|
||||
PLOT_TEST(figure_split_row_gap_with_weights_and_a_y_gutter) {
|
||||
plot::Figure figure = host_like_figure({1, 2});
|
||||
const std::vector<plot::f32> w{3.0f, 1.0f};
|
||||
PLOT_CHECK(figure.set_cell_weights(1u, w));
|
||||
const plot::FigureLayout& fl = figure.layout();
|
||||
const plot::Rect a0 = figure.plot(1u).layout().axes;
|
||||
const plot::Rect a1 = figure.plot(2u).layout().axes;
|
||||
PLOT_CHECK_NEAR(a1.x - a0.right(), 6.0, 1e-3);
|
||||
PLOT_CHECK_NEAR(a0.w - 64.0, (a1.w - 64.0) * 3.0, 1.0);
|
||||
PLOT_CHECK_NEAR(fl.cells[2].right(), fl.rows[1].right(), 1e-3);
|
||||
PLOT_CHECK_NEAR(fl.cells[1].x, fl.rows[1].x, 1e-4);
|
||||
PLOT_CHECK_NEAR(fl.cells[2].right() - a1.right(), 35.0, 1e-3);
|
||||
// The unsplit row above keeps its own right gutter on its one plot.
|
||||
PLOT_CHECK_NEAR(fl.cells[0].right() - figure.plot(0u).layout().axes.right(), 35.0, 1e-3);
|
||||
}
|
||||
|
||||
PLOT_TEST(figure_split_row_gutters_degrade_on_a_narrow_row) {
|
||||
plot::Figure figure = host_like_figure({3});
|
||||
figure.set_viewport(plot::Rect{0.0f, 0.0f, 60.0f, 100.0f});
|
||||
const plot::FigureLayout& fl = figure.layout();
|
||||
double sum = 0.0;
|
||||
for (const plot::Rect& c : fl.cells) {
|
||||
PLOT_CHECK(c.w >= 0.0f && c.h >= 0.0f);
|
||||
sum += c.w;
|
||||
}
|
||||
PLOT_CHECK_NEAR(sum + fl.col_gap * 2.0, 60.0, 0.05);
|
||||
PLOT_CHECK_NEAR(fl.cells[2].right(), 60.0, 0.05);
|
||||
for (plot::u32 i = 0; i < 3u; ++i) PLOT_CHECK(figure.plot(i).layout().axes.w >= 0.0f);
|
||||
}
|
||||
|
||||
PLOT_TEST(figure_row_gutter_without_sharing_is_the_old_layout) {
|
||||
plot::Figure figure = host_like_figure({2}, false);
|
||||
const plot::FigureLayout& fl = figure.layout();
|
||||
// Plain cells: equal halves of the row less the gap, every plot with its own right gutter.
|
||||
PLOT_CHECK_NEAR(fl.cells[0].w, (fl.rows[0].w - 6.0) / 2.0, 1e-3);
|
||||
PLOT_CHECK_NEAR(fl.cells[1].w, (fl.rows[0].w - 6.0) / 2.0, 1e-3);
|
||||
PLOT_CHECK_NEAR(fl.cells[1].x - fl.cells[0].right(), 6.0, 1e-4);
|
||||
for (plot::u32 i = 0; i < 2u; ++i) {
|
||||
PLOT_CHECK_NEAR(figure.plot(i).layout().right_gutter, 35.0, 1e-3);
|
||||
PLOT_CHECK_NEAR(fl.cells[i].right() - figure.plot(i).layout().axes.right(), 35.0, 1e-3);
|
||||
}
|
||||
// An unsplit row is unchanged by sharing being on.
|
||||
plot::Figure unsplit = host_like_figure({1, 1});
|
||||
const plot::FigureLayout& ul = unsplit.layout();
|
||||
for (plot::u32 i = 0; i < 2u; ++i) {
|
||||
PLOT_CHECK_NEAR(ul.cells[i].w, ul.rows[i].w, 1e-4);
|
||||
PLOT_CHECK_NEAR(ul.cells[i].right() - unsplit.plot(i).layout().axes.right(), 35.0, 1e-3);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user