feat(input): live x follow with relative zoom and mouse panning
This commit is contained in:
1 parent
eaae07d8e0
commit
77e861f79e
10 files changed
+971
-28
No files matched your search
@@ -45,12 +45,32 @@ enum class DragMode {
|
||||
/// Both axis-zoom drags read the vertical `dy` in pointer pixels with the
|
||||
/// wheel's sign convention (dragging down zooms in), anchored at the pointer's
|
||||
/// data coordinate on the axis they zoom. The drag has its own per-pixel
|
||||
/// sensitivity -- a pixel is not a wheel notch -- of one decade per 200 px, so
|
||||
/// a 200 px drag scales the span by 10.
|
||||
/// sensitivity -- a pixel is not a wheel notch -- of one decade per 200 **logical**
|
||||
/// px, so a 200 px drag scales the span by 10 whatever the display's scale.
|
||||
///
|
||||
/// A drag that changes no range is harmless: a box-zoom selection under 4 px on
|
||||
/// either side is a click, and leaves the ranges alone.
|
||||
///
|
||||
/// ## Zoom and pan on a live x axis (spec §12)
|
||||
///
|
||||
/// An x axis armed with `XFollow::Live` keeps following through every gesture
|
||||
/// below. Nothing here reads or writes the follow state, and the ranges these
|
||||
/// gestures set are read by `Plot::set_x_range` as a span and an offset behind the
|
||||
/// live edge rather than as absolute positions, so both halves of the requirement
|
||||
/// fall out of the paths that already existed:
|
||||
///
|
||||
/// - the wheel *multiplies* the span, so notches compound and the zoom is
|
||||
/// scale-free. Its anchor is the data coordinate under the pointer while the
|
||||
/// pointer is inside `axes`; outside it there is no such coordinate, and on a
|
||||
/// live axis the live edge is used instead (see `x_zoom_anchor()`), which leaves
|
||||
/// the window ending where it ended. Either way the offset is adjusted to keep
|
||||
/// the anchor still, and following stays armed;
|
||||
/// - a pan shifts the offset by the drag's data delta and leaves the span alone, so
|
||||
/// a user can walk back through the retained window and stops at the live edge
|
||||
/// rather than past it.
|
||||
///
|
||||
/// An `Off` axis -- every axis by default -- behaves exactly as it always has.
|
||||
///
|
||||
/// ## Tolerances are logical, the coordinates they are compared against are not
|
||||
///
|
||||
/// The plot geometry the controller hit-tests in is device pixels (see
|
||||
@@ -65,10 +85,13 @@ enum class DragMode {
|
||||
/// knows what its device pixel is worth, so the controller keeps no copy of the scale
|
||||
/// for a host to keep in step.
|
||||
///
|
||||
/// The middle-drag axis zoom's per-pixel gain is deliberately *not* in that group:
|
||||
/// it is a data-navigation sensitivity rather than a size (the wheel and `+`/`-`
|
||||
/// are scale-free already, being counted in notches), and spec §12 reworks the
|
||||
/// zoom and pan gestures that own it.
|
||||
/// The middle-drag axis zoom's per-pixel gain is logical for the same reason, by a
|
||||
/// different route: it is stated in *logical* pixels (200 of them is one decade),
|
||||
/// and the drag delta arrives in device pixels like every other coordinate here, so
|
||||
/// `axis_zoom()` divides it by `Plot::ui_scale()` before applying the gain. Without
|
||||
/// that the same physical drag would zoom twice as much at 2x. The wheel needs no
|
||||
/// such conversion: a notch is already device-independent, and the wheel path is
|
||||
/// left alone.
|
||||
class Controller {
|
||||
public:
|
||||
explicit Controller(Plot& plot) noexcept : plot_(&plot) {}
|
||||
@@ -115,8 +138,18 @@ class Controller {
|
||||
/// kInvalid when (px, py) is outside the plot axes.
|
||||
u64 cursor_at(f32 px, f32 py) const;
|
||||
|
||||
/// The data x a zoom of `axis` holds fixed, for a gesture at (px, py). The
|
||||
/// coordinate under the pointer while the pointer is inside `axes`, which is what
|
||||
/// makes a zoom feel anchored to the data being pointed at; outside `axes` there
|
||||
/// is no such coordinate, and a live axis (`XFollow::Live`) uses its live edge
|
||||
/// instead, so a zoom made from the label strip or off the plot zooms the window
|
||||
/// about the newest sample and leaves it ending where it ended.
|
||||
f64 x_zoom_anchor(u32 axis, f32 px, f32 py) const;
|
||||
|
||||
/// Zoom one axis about `anchor` (a data coordinate) by `factor`, recomputing
|
||||
/// the range so the anchor keeps its screen position. Leaves `auto_scale` false.
|
||||
/// the range so the anchor keeps its screen position. Leaves `auto_scale` false;
|
||||
/// under `XFollow::Live` the range is read by `Plot::set_x_range` as a span and an
|
||||
/// offset behind the live edge, which is what keeps following armed.
|
||||
void zoom_x_axis(u32 axis, f64 anchor, f64 factor);
|
||||
void zoom_y_axis(u32 axis, f64 anchor, f64 factor);
|
||||
|
||||
|
||||
Reference in new issue
Block a user