improved measuraments ,cursors, zooming and user interactions
This commit is contained in:
1 parent
0eceaf537b
commit
02620ca946
12 files changed
+1488
-171
No files matched your search
+101
-8
@@ -106,6 +106,11 @@ class RowSet {
|
||||
/// it keeps its position whatever the cursor toggle says.
|
||||
plot::u32 trigger_marker = 0;
|
||||
|
||||
/// The caption naming the window's start while the x labels are relative: an annotation
|
||||
/// inside the axes, so its text can change every frame without moving the layout (an axis
|
||||
/// *title* would, and it would be a different width each time).
|
||||
plot::u32 time_origin_note = 0;
|
||||
|
||||
/// The measurements' readout marks: a pool created with the plot (a `Plot` cannot add or
|
||||
/// drop a cursor after the fact) and parked unless a measurement is using the slot.
|
||||
std::array<plot::u32, kMeasureMarks> measure_h{};
|
||||
@@ -208,6 +213,19 @@ class RowSet {
|
||||
/// end up with its instruments parked while every other row showed them.
|
||||
bool cursors_visible() const noexcept { return cursors_on_; }
|
||||
|
||||
/// Whether the x labels are currently written relative to the window's start, and what that
|
||||
/// start was. The mode is decided by the span (see `update_time_labels()`), which is why it
|
||||
/// is state rather than a per-frame expression: the switch has hysteresis, so a span
|
||||
/// hovering on the boundary cannot make the labels flicker between two forms.
|
||||
bool time_labels_relative() const noexcept { return relative_x_; }
|
||||
/// The value the x labels are relative to (meaningless when `time_labels_relative()` is
|
||||
/// false). The host prints it beside the axis so the reader can add it to a tick.
|
||||
double time_labels_origin() const noexcept { return x_origin_; }
|
||||
/// Chooses the x label form for the window the rows are showing and applies it: relative
|
||||
/// with the window's own start as the origin when the span is sub-second, absolute
|
||||
/// otherwise. Called once per frame from the tick, and returns true when the form changed.
|
||||
bool update_time_labels();
|
||||
|
||||
/// One measurement's readout mark on a row: a *level* (drawn as a horizontal ruler) or a
|
||||
/// *time* (a vertical marker). Which one a measurement needs is the measurement's own
|
||||
/// business; the row is the one that plots its signal.
|
||||
@@ -227,18 +245,75 @@ class RowSet {
|
||||
/// measurement is drawn", as opposed to merely computed.
|
||||
int measure_marks_placed() const noexcept { return measure_marks_placed_; }
|
||||
|
||||
/// Whether every instrument this app *computed* is out of the pointer's reach: the trigger
|
||||
/// marker and the measurement marks. They are readouts -- where the scope put the edge, what a
|
||||
/// measurement measured -- and a user must not be able to drag them; the report in the dump is
|
||||
/// how that is checked, since a drag is not something a probe can perform.
|
||||
bool readouts_locked() const noexcept;
|
||||
|
||||
/// How many times `clamp_to_data()` re-framed the view. A clamp that fires every frame is a
|
||||
/// *loop*, not a clamp -- and `span > window_s_` can stay true after clamping to exactly
|
||||
/// `window_s_`, because the group's span is the range's span through f64 and lands a ULP over.
|
||||
/// Reported in the dump so a flickering view has a number rather than a description.
|
||||
int clamp_fires() const noexcept { return clamp_fires_; }
|
||||
|
||||
/// The window the rows are actually on: the *group's* range, which is what the gestures and the
|
||||
/// clamps write. A member's own range only catches up at the next layout, so a probe that reads
|
||||
/// a member to judge a clamp sees the previous frame's window.
|
||||
plot::Range view_range() const noexcept {
|
||||
return figure_ ? figure_->x_link_range(x_group_) : plot::Range{0.0, 0.0};
|
||||
}
|
||||
|
||||
/// Re-states the pinned window, so a gesture cannot move it.
|
||||
///
|
||||
/// In trigger mode the group follows nothing: `clamp_to_data()` is live-only by design, and the
|
||||
/// library's own pan bound lives in the *following* path (a `Live` group's offset is clamped
|
||||
/// against what its members retain), so with the follow off nothing pulled a drag back -- a pan
|
||||
/// or a zoom-out walked the frozen capture off its axis and left an empty plot over data the
|
||||
/// scope never had. The view is kept inside the event instead: the span is capped at the event's
|
||||
/// and the position clamped into it, so a **zoom in is allowed** (less of the same event is
|
||||
/// still the event) while leaving it is not. Doing it by re-stating the pin -- this function's
|
||||
/// first version -- made the wheel a no-op, i.e. an acquired event could not be examined at all.
|
||||
/// Does nothing when no window is pinned.
|
||||
void hold_pinned_window();
|
||||
|
||||
|
||||
/// Pins the axes where they are for the pause's frozen view: the snapshot is not a streaming
|
||||
/// series, so live follow has nothing to derive a window from, and the frame the pause froze
|
||||
/// has to stay the frame on screen. `unpin_pause_window()` restores following and re-frames on
|
||||
/// the newest data, which is what a resume should show.
|
||||
/// `lo`/`hi` are the *snapshot's* own stamps -- the data the frozen view actually holds, which
|
||||
/// may be narrower than the window that was requested when the pause began.
|
||||
void pin_pause_window(double lo, double hi);
|
||||
|
||||
/// The window the bound **event data** spans: the union of the event series the rows hold. What
|
||||
/// a `Held` view may be navigated *within* -- the snapshot's stamps, never the recorded
|
||||
/// `pre_s`/`post_s` split, which is the *request* and can exceed what was captured. False when
|
||||
/// no row holds an event.
|
||||
bool event_data_window(double& lo, double& hi) const;
|
||||
|
||||
/// Sets the pin without touching what is on screen: the bound a gesture is clamped into. The
|
||||
/// `Held` tick re-states it every frame (the snapshot does not move, so it is idempotent) while
|
||||
/// a user's zoom -- which is a narrower window *inside* the pin -- survives.
|
||||
void set_data_pin(double lo, double hi);
|
||||
void unpin_pause_window();
|
||||
|
||||
/// Frames the x window on the data row 0 holds, leaving every y axis alone. The x
|
||||
/// counterpart of `fit_all()`, for the one thing the y axes cannot say: how wide the
|
||||
/// window should be.
|
||||
void fit_x();
|
||||
|
||||
/// Adopts the instruments a gesture moved. The vertical pair is one time for the whole
|
||||
/// figure, so a drag on any row's marker moves every row's; a ruler belongs to its own
|
||||
/// row and is only read back. Either way the plot is where a drag writes, so this is the
|
||||
/// one place the app can see it -- and the pane reads what is stored here, which is why a
|
||||
/// ruler that was not read back showed a stale value while its marker on the plot moved.
|
||||
/// Called once a frame from the tick.
|
||||
void sync_cursors();
|
||||
/// Adopts the instruments a gesture is moving, on `drag_row` (the row whose controller holds
|
||||
/// a cursor), and nothing otherwise.
|
||||
///
|
||||
/// Reading the plots is only meaningful *while a drag runs*: `cursor(id).x` reports the free
|
||||
/// pointer position then, and the snapped one otherwise -- which is what this very code wrote
|
||||
/// a frame earlier (or the snap of it). Adopting outside a drag therefore adopts our own echo
|
||||
/// back and a row's stale copy of the shared time, which is what made a dragged cursor spring
|
||||
/// to the position it had before the gesture. The vertical pair is one time for the whole
|
||||
/// figure, so a drag on any row moves every row's; a ruler belongs to its own row alone.
|
||||
/// Called once a frame from the tick, with `drag_row` < 0 when no cursor is held.
|
||||
void sync_cursors(int drag_row);
|
||||
|
||||
/// Puts both vertical cursors at a quarter and three quarters of the x window, and
|
||||
/// the two rulers at a third and two thirds of each row's y range. Also switches the
|
||||
@@ -309,7 +384,13 @@ class RowSet {
|
||||
static void clear_row(Row& r);
|
||||
|
||||
void install(Row& r, int row_index, plot::Plot& p);
|
||||
void place_cursors(Row& r);
|
||||
/// Writes a row's instruments from the stored values. `placement` is passed through to the
|
||||
/// plot: a *programmatic* placement (reset, a mode change) wants the default `Snapped`, so
|
||||
/// the marker lands on a sample; echoing a value a *drag* just produced wants `Free`, because
|
||||
/// snapping it again is what made a dragged cursor spring back -- the library snaps a cursor
|
||||
/// at the end of the gesture (`Controller::end_drag()`), and a value read out of the plot
|
||||
/// mid-drag has already been through that act.
|
||||
void place_cursors(Row& r, plot::CursorPlacement placement = plot::CursorPlacement::Snapped);
|
||||
/// The newest x any row's live traces hold, which is what the group follows.
|
||||
bool live_edge(double& newest) const;
|
||||
|
||||
@@ -325,11 +406,23 @@ class RowSet {
|
||||
double pinned_pre_ = 0.0;
|
||||
double pinned_post_ = 0.0;
|
||||
bool pinned_ = false;
|
||||
int clamp_fires_ = 0;
|
||||
/// The window the pinned clamp last stated. A row's own axis that differs from this is a
|
||||
/// gesture to be honoured; one that matches it is just the clamp's own answer arriving back.
|
||||
plot::Range pinned_stated_{0.0, 0.0};
|
||||
double y_gutter_px_ = 0.0;
|
||||
bool y_gutter_reserved_ = false;
|
||||
std::array<Row, static_cast<std::size_t>(kMaxRows)> storage_{};
|
||||
CursorMode cursor_mode_ = CursorMode::Vertical;
|
||||
bool cursors_on_ = false;
|
||||
bool relative_x_ = false;
|
||||
double x_origin_ = 0.0;
|
||||
/// What was last *written* to each row's own vertical cursors. A row is only evidence of a
|
||||
/// gesture when its value differs from its own last write: the pair's value is shared, so a
|
||||
/// row that has not been re-written yet still holds the previous shared value, and adopting
|
||||
/// *that* would undo the row the user actually dragged -- which is what made a drag spring
|
||||
/// back, one row's stale echo at a time.
|
||||
|
||||
int measure_marks_placed_ = 0;
|
||||
/// The one stream time each vertical cursor is at, whatever row's copy was last moved.
|
||||
double cursor_a_x_ = 0.0;
|
||||
|
||||
Reference in new issue
Block a user