plot host: rows hold 1..4 cells; split probe scaffold

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino FerrariandClaude Opus 4.6 committed 2026-11-03 17:11:01 +01:00
1 parent 79e69c28fa
commit ae1d262be4
5 files changed
+433 -18

No files matched your search

+51 -8
View File
@@ -155,8 +155,43 @@ class RowSet {
double ruler_b_y = 0.0;
};
/// The number of **plots** (cells over all figure rows), which is what `row(i)` indexes. With no
/// row split it is also the number of figure rows; `figure_row_count()` is the latter.
int row_count() const noexcept { return rows_; }
/// A figure row holds one to `kMaxCellsPerRow` plots side by side, sharing the row's height and
/// the figure's one x window. The flattened plot index (`row(i)`, `Row::plot`, the figure's
/// `plot(i)`) runs row-major: row 0's cells left to right, then row 1's.
static constexpr int kMaxCellsPerRow = 4;
int figure_row_count() const noexcept { return static_cast<int>(cells_per_row_.size()); }
/// How many cells figure row `fr` holds; 0 for a row that does not exist.
int cells_in_row(int fr) const;
/// The flattened index of figure row `fr`'s first cell; `row_count()` for `fr ==
/// figure_row_count()`.
int first_plot_of_row(int fr) const;
/// The figure row plot `plot` is in, or -1 for an index that is not a plot.
int figure_row_of(int plot) const;
/// Plot `plot`'s 0-based position inside its row, or -1.
int cell_of(int plot) const;
/// Figure row `fr`'s cell width shares (empty means equal; always empty for a single-cell row).
const std::vector<float>& cell_weights(int fr) const;
/// What the UI calls a plot: the figure row's number (`"2"`) for a single-cell row, the number
/// and a letter (`"2a"`..`"2d"`) for a split one.
std::string plot_label(int plot) const;
/// Adds an empty cell to figure row `fr` at position `at` (clamped to the row), pushing the
/// cells at and after it right. False at `kMaxCellsPerRow` cells or `kMaxRows` plots.
bool insert_cell(int fr, int at);
/// Removes plot `plot`. A single-cell row removes the row itself (`remove_row()`); false when
/// that would leave no plot.
bool remove_cell(int plot);
/// Figure row `fr`'s cell width shares, normalised by the plotter; empty means equal.
void set_cell_weights(int fr, std::vector<float> w);
/// Replaces the whole arrangement: `cells[r]` plots in figure row `r`, each clamped to
/// `1..kMaxCellsPerRow` and the total to `kMaxRows`. Weights are reset; plot contents stay in
/// their flattened slots.
void set_cells_per_row(std::vector<int> cells);
/// The alpha every trace is drawn with. The *group* views use it: accumulating events wants their
/// traces translucent so that agreement between them reads as brightness, which is the whole point
/// of a persistence display. It is mixed into the palette colour where the trace is created, so it
@@ -169,22 +204,24 @@ class RowSet {
/// trace set or the row count changes, so weights that lived only in the figure would be lost on the
/// first drop or row change after a user had sized their layout.
void set_row_weights(std::vector<float> weights);
/// Sizes the stored weights to the rows that exist and hands them to the figure.
/// Sizes the stored weights to the figure rows that exist and hands them to the figure. Weights
/// are per **figure row**, not per plot.
void apply_row_weights();
const std::vector<float>& row_weights() const noexcept { return row_weights_; }
/// Shows `rows` rows. A changed count rebuilds the figure, since a `Figure`'s row
/// count is fixed at construction.
/// Shows `rows` figure rows of one cell each (so `rows` plots, any split undone). A changed
/// arrangement rebuilds the figure, since a `Figure`'s shape is fixed at construction.
void set_row_count(int rows);
/// Inserts an empty row at `at` (0 = above the first row, `row_count()` = below the
/// last), pushing the rows at and below it down. Returns false at `kMaxRows`.
/// Inserts an empty single-cell figure row at `at` (0 = above the first row,
/// `figure_row_count()` = below the last), pushing the rows at and below it down with their
/// cells. Returns false at `kMaxRows` plots.
///
/// The rows below move as *rows*: their trace assignments, colours and cursors follow
/// them, so inserting above row 1 does not renumber what row 1 was plotting. The
/// figure's `Plot`s are not what moves -- they are rebuilt and re-dealt -- which is
/// the same mechanism a row-count change already used.
bool insert_row(int at);
/// Removes row `at` and pulls the rows below it up. Returns false when only one row
/// is left: a figure with no rows has nothing to draw and no way back.
/// Removes figure row `at` (with all its cells) and pulls the rows below it up. Returns false
/// when only one row is left: a figure with no rows has nothing to draw and no way back.
bool remove_row(int at);
Row& row(int i) { return storage_[static_cast<std::size_t>(i)]; }
const Row& row(int i) const { return storage_[static_cast<std::size_t>(i)]; }
@@ -563,9 +600,15 @@ class RowSet {
std::unique_ptr<plot::Figure> figure_;
/// The one x link group every visible row belongs to. Groups are numbered from 1.
plot::u32 x_group_ = 0;
/// The number of plots; `sum(cells_per_row_) == rows_` always.
int rows_ = 1;
/// Cells per figure row, and each row's cell width shares (empty = equal; kept empty for a
/// single-cell row). Both are state of the `RowSet`, restated to every new figure.
std::vector<int> cells_per_row_{1};
std::vector<std::vector<float>> cell_weights_ = std::vector<std::vector<float>>(1);
void apply_cell_weights();
float trace_alpha_ = 1.0f;
std::vector<float> row_weights_;
std::vector<float> row_weights_; ///< per figure row
double window_s_ = 2.0;
double trigger_marker_x_ = 0.0;
bool trigger_marker_on_ = false;