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
@@ -1,6 +1,7 @@
|
||||
#include "harness.hpp"
|
||||
|
||||
#include <plot/input/controller.hpp>
|
||||
#include <plot/plot/figure.hpp>
|
||||
#include <plot/plot/plot.hpp>
|
||||
#include <plot/data/array_series.hpp>
|
||||
#include <plot/data/stream_series.hpp>
|
||||
@@ -1306,3 +1307,84 @@ PLOT_TEST(hot_handles_follow_a_gesture_that_moves_the_geometry) {
|
||||
PLOT_CHECK(f.plot.hot_handles().zone == f.controller.region_grab(rid));
|
||||
PLOT_CHECK(f.plot.hot_handles().zone != plot::RegionGrab::Left);
|
||||
}
|
||||
|
||||
/// A `Figure`'s shared x axis is the **bottom row's own**, so the axis zoom is
|
||||
/// reachable there through the one hit test the controller has always used: the press
|
||||
/// that lands in the bottom row's x label strip starts `AxisZoomX` exactly as it does on
|
||||
/// a stand-alone plot, and because the rows share one x link the zoom moves all of them.
|
||||
///
|
||||
/// A figure that put its shared axis *outside* every row would leave this gesture
|
||||
/// unreachable on every row at once: `x_label_area` would be zero-height, `contains()`
|
||||
/// would never be true, and a middle drag would fall through to `DragMode::None`.
|
||||
///
|
||||
/// The other half is what makes that a statement about *which* row: the same drag on an
|
||||
/// interior row -- at the point a label strip would land if that row had one -- is
|
||||
/// deliberately not the gesture. The row reserved no x chrome, so there is no strip for
|
||||
/// the press to be inside, and the zoom the bottom row's strip can start cannot be
|
||||
/// started from a row that has none. If that were not so the shared axis would be one
|
||||
/// strip among several, each of which could zoom the one range they all share.
|
||||
PLOT_TEST(a_middle_drag_on_a_figures_bottom_row_zooms_the_shared_x_axis) {
|
||||
plot::Figure figure(3u);
|
||||
figure.set_viewport({0, 0, 800, 600});
|
||||
for (u32 row = 0; row < 3u; ++row) {
|
||||
plot::Plot& p = figure.plot(row);
|
||||
p.add_x_axis({"t", {0.0, 100.0}, false, plot::AxisPlace::Bottom, false});
|
||||
p.add_y_axis({"v", {-1.0, 1.0}, false, plot::AxisPlace::Left, false});
|
||||
}
|
||||
const u32 group = figure.add_x_link();
|
||||
for (u32 row = 0; row < 3u; ++row) PLOT_CHECK(figure.link_x(row, group));
|
||||
const plot::Range before = figure.x_link_range(group);
|
||||
|
||||
figure.layout();
|
||||
const plot::PlotLayout& bottom = figure.plot(2u).layout();
|
||||
PLOT_CHECK(bottom.x_label_area.h > 0.0f);
|
||||
const f32 strip_x = bottom.axes.x + 0.5f * bottom.axes.w;
|
||||
const f32 strip_y = bottom.x_label_area.y + 0.5f * bottom.x_label_area.h;
|
||||
PLOT_CHECK(bottom.x_label_area.contains(strip_x, strip_y));
|
||||
|
||||
plot::input::Controller c(figure.plot(2u));
|
||||
c.set_viewport(figure.viewport());
|
||||
c.on_pointer({strip_x, strip_y, 0, 0, plot::input::Button::Middle, 0, true, true, false});
|
||||
PLOT_CHECK(c.drag_mode() == plot::input::DragMode::AxisZoomX);
|
||||
// Same sign and magnitude as on a single plot: a positive dy zooms in, 20 px being a
|
||||
// tenth of a decade.
|
||||
c.on_pointer({strip_x, strip_y + 20, 0, 20, plot::input::Button::Middle, 0, true, false, false});
|
||||
c.on_pointer({strip_x, strip_y + 20, 0, 0, plot::input::Button::Middle, 0, false, false, true});
|
||||
PLOT_CHECK(c.drag_mode() == plot::input::DragMode::None);
|
||||
|
||||
// The gesture reached the row's own axis...
|
||||
PLOT_CHECK_NEAR(figure.plot(2u).x_range(0u).span(), 100.0 * std::pow(10.0, -20.0 / 200.0), 1e-6);
|
||||
// ... and, through the link, every other row with it: one x axis, zoomed once.
|
||||
figure.layout();
|
||||
for (u32 row = 0; row < 3u; ++row) {
|
||||
PLOT_CHECK_NEAR(figure.plot(row).x_range(0u).span(), figure.x_link_range(group).span(), 1e-9);
|
||||
PLOT_CHECK_NEAR(figure.plot(row).x_range(0u).span(), 100.0 * std::pow(10.0, -20.0 / 200.0),
|
||||
1e-6);
|
||||
}
|
||||
PLOT_CHECK(figure.x_link_range(group).span() < before.span());
|
||||
|
||||
// An interior row is not the same gesture. A press at the point a label strip *would*
|
||||
// land in -- the band this row's chrome would occupy if the figure gave it one -- is
|
||||
// not inside anything: the row reserved no x chrome, so its `x_label_area` is
|
||||
// zero-height and no press can be inside it. The middle drag therefore falls through
|
||||
// to `DragMode::None`, and because `update_drag()` does nothing in that mode the
|
||||
// group's window -- the one range every row shares -- is untouched by it.
|
||||
const plot::PlotLayout& interior = figure.plot(0u).layout();
|
||||
PLOT_CHECK(interior.x_label_area.h == 0.0f);
|
||||
const f32 interior_x = interior.axes.x + 0.5f * interior.axes.w;
|
||||
const f32 interior_y = figure.layout().rows[0].bottom() - 0.5f * bottom.x_label_area.h;
|
||||
PLOT_CHECK(!interior.x_label_area.contains(interior_x, interior_y));
|
||||
const plot::Range shared_before = figure.x_link_range(group);
|
||||
|
||||
plot::input::Controller interior_controller(figure.plot(0u));
|
||||
interior_controller.on_pointer(
|
||||
{interior_x, interior_y, 0, 0, plot::input::Button::Middle, 0, true, true, false});
|
||||
PLOT_CHECK(interior_controller.drag_mode() == plot::input::DragMode::None);
|
||||
interior_controller.on_pointer(
|
||||
{interior_x, interior_y + 20, 0, 20, plot::input::Button::Middle, 0, true, false, false});
|
||||
interior_controller.on_pointer(
|
||||
{interior_x, interior_y + 20, 0, 0, plot::input::Button::Middle, 0, false, false, true});
|
||||
PLOT_CHECK(interior_controller.drag_mode() == plot::input::DragMode::None);
|
||||
PLOT_CHECK_NEAR(figure.plot(0u).x_range(0u).span(), shared_before.span(), 1e-12);
|
||||
PLOT_CHECK_NEAR(figure.x_link_range(group).span(), shared_before.span(), 1e-12);
|
||||
}
|
||||
Reference in new issue
Block a user