added auto measuraments

This commit is contained in:
Martino Ferrari committed 2026-10-03 10:34:15 +02:00
1 parent 44fc4cdf36
commit 0eceaf537b
7 files changed
+2182 -55

No files matched your search

+102 -5
View File
@@ -48,9 +48,27 @@ class RowSet {
/// about this many a row is chrome and no plot.
static constexpr int kMaxRows = 8;
static constexpr int kSlotsPerRow = 4;
/// Readout marks per row per orientation: enough for a handful of level and time
/// measurements to be shown at once, and a fixed number because a `Plot` fixes its cursor
/// set at construction.
static constexpr int kMeasureMarks = 6;
RowSet();
/// Which cursor instruments are up, from the toolbar's toggle. The two vertical
/// markers are shared by every row (one stream time each, so they line up down the
/// column); a horizontal ruler is per row, because each row has its own y axis.
///
/// `Mix` is the vertical pair with the *per-trace* readout instead of rulers: the
/// pane then reports where each cursor crosses each trace, which is the value a
/// ruler would have to be dragged to express.
enum class CursorMode {
Vertical, ///< two x markers, shared by all rows
Horizontal, ///< two y rulers per row
Both, ///< both instruments
Mix, ///< the x pair, read off each trace
};
struct Row {
/// The figure's row, borrowed. Null for a row beyond `row_count()`, which is how
/// every loop here and in the UI stays inside the rows that exist. Invalidated by
@@ -82,16 +100,28 @@ class RowSet {
plot::u32 cursor_a = 0;
plot::u32 cursor_b = 0;
plot::u32 ruler = 0;
plot::u32 ruler_b = 0;
/// The vertical line marking the trigger instant, drawn whenever an event is
/// on screen (and parked outside the axis otherwise). Not the user's cursor:
/// it keeps its position whatever the cursor toggle says.
plot::u32 trigger_marker = 0;
/// Off by default: the cursors are switched on from the toolbar, and a rebuild
/// (any drop, any row-count change) must not switch them back off.
bool cursors_visible = false;
double cursor_a_x = 0.0;
double cursor_b_x = 0.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{};
std::array<plot::u32, kMeasureMarks> measure_v{};
/// One marker per trace per cursor: where each vertical cursor crosses that trace's
/// series, drawn only in `mix` mode. Two per slot because the pair crosses a trace
/// at two places, and an annotation cannot be shared between them.
std::array<plot::u32, kSlotsPerRow> crossing_a{};
std::array<plot::u32, kSlotsPerRow> crossing_b{};
/// The two rulers' values. The vertical pair is *not* here: one stream time is
/// shared by every row, so it lives on the `RowSet` where it cannot drift between
/// rows.
double ruler_y = 0.0;
double ruler_b_y = 0.0;
};
int row_count() const noexcept { return rows_; }
@@ -152,6 +182,67 @@ class RowSet {
void set_y_range(int row, double lo, double hi);
void set_cursors_visible(bool on);
/// What the cursors are (see `CursorMode`). Switching parks the instruments the mode
/// does not use rather than removing them: a `Plot` cannot drop a cursor.
void set_cursor_mode(CursorMode mode);
/// Places (or hides) the `mix` mode's crossing markers: for every plotted trace, the
/// point where each cursor meets it. Called from `sync_cursors()` and when the mode
/// changes, so the markers are never a frame behind the markers that cast them.
void place_crossings();
CursorMode cursor_mode() const noexcept { return cursor_mode_; }
/// The two vertical cursors' stream times; shared by every row.
double cursor_a_x() const noexcept { return cursor_a_x_; }
double cursor_b_x() const noexcept { return cursor_b_x_; }
/// One row's ruler values, or 0 for a row that does not exist.
double ruler_y(int row) const;
double ruler_b_y(int row) const;
/// How many `mix` crossing markers are on screen, and the first one's position: the
/// probe's window onto "the marks follow the cursors".
int crossings_visible() const;
bool crossing_at(int row, int slot, bool second, double& x, double& y) const;
/// Whether the cursors are on. One flag for the whole set, not a copy per row: it is one
/// piece of state (the toolbar's toggle), and a per-row copy is something a rebuild, an
/// insert or a carried row can clear behind the user's back -- which is how a row could
/// end up with its instruments parked while every other row showed them.
bool cursors_visible() const noexcept { return cursors_on_; }
/// 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.
struct MeasureMark {
int row = 0;
bool level = false;
double value = 0.0;
plot::u32 color = 0;
};
/// Places the measurements' marks and parks every pool slot they do not use. The marks are
/// created unselectable (`Plot::set_cursor_selectable()`), so the pointer can neither hover
/// nor drag them; this runs every frame because a mark is only correct for the window its
/// measurement was computed over.
void set_measure_marks(const std::vector<MeasureMark>& marks);
/// How many marks the last `set_measure_marks()` placed: the probe's window onto "the
/// measurement is drawn", as opposed to merely computed.
int measure_marks_placed() const noexcept { return measure_marks_placed_; }
/// 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();
/// 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
/// cursors on, so the button cannot look dead.
void reset_cursors();
/// Places the trigger marker at stream time `x` (seconds since the epoch), or
@@ -237,6 +328,12 @@ class RowSet {
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;
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;
double cursor_b_x_ = 0.0;
std::array<bool, static_cast<std::size_t>(kMaxRows)> row_dirty_{};
};