docs: split rows share one y axis
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
b8177cc25a
commit
fa9f0c6da9
8 files changed
+86
-40
No files matched your search
@@ -2580,7 +2580,8 @@ the only place it is visible. Outside a gesture the plot is not read at all: `cu
|
|||||||
snapped value then, which is what the app wrote itself a frame earlier, and adopting that back
|
snapped value then, which is what the app wrote itself a frame earlier, and adopting that back
|
||||||
is what made a dragged cursor spring to where it had been. A ruler is per row: each row has its own y axis, so two rows' rulers are
|
is what made a dragged cursor spring to where it had been. A ruler is per row: each row has its own y axis, so two rows' rulers are
|
||||||
two different values, and the pane names the row. In a split row (see "Splitting a row") the vertical pair is drawn in
|
two different values, and the pane names the row. In a split row (see "Splitting a row") the vertical pair is drawn in
|
||||||
every plot and a ruler belongs to one plot, which the pane names `plot 2a`.
|
every plot, and so is the horizontal pair: the plots of a split row share one y axis, so the pane lists one ruler
|
||||||
|
line for the whole row, and dragging a ruler in any plot moves it in all of them.
|
||||||
|
|
||||||
`mix` needs no rulers: it reports the value the trace actually has at each marker, read
|
`mix` needs no rulers: it reports the value the trace actually has at each marker, read
|
||||||
through the library's `Plot::crosshair_value()` — **the nearer of the two bracketing
|
through the library's `Plot::crosshair_value()` — **the nearer of the two bracketing
|
||||||
@@ -2826,11 +2827,15 @@ reach every position the two-per-row reading offers.
|
|||||||
A row can be split into **two to four plots side by side**, up to **eight plots in all**. A split is
|
A row can be split into **two to four plots side by side**, up to **eight plots in all**. A split is
|
||||||
only an arrangement of more plots on the screen: **every plot shares the one time window** -- one x
|
only an arrangement of more plots on the screen: **every plot shares the one time window** -- one x
|
||||||
link group, so a pan or zoom in any plot moves them all; one crosshair, drawn at the same time in every
|
link group, so a pan or zoom in any plot moves them all; one crosshair, drawn at the same time in every
|
||||||
plot; and one pair of vertical cursors, at the same two times in every plot. What each plot keeps is its
|
plot; and one pair of vertical cursors, at the same two times in every plot. The plots of a row
|
||||||
own **y axis** (auto or fixed), its own horizontal rulers and its own fit; a wheel or an axis drag acts on
|
also share **one y range**, the way the rows share one x window: the row has one y axis (auto or fixed),
|
||||||
the plot under the pointer. Only the **bottom** row's plots carry the time labels, and the left margin
|
drawn with its labels at the left of the first plot only, one pair of horizontal rulers drawn at the same
|
||||||
|
value in every plot, and one fit. A wheel, a drag, a box zoom, **Fit all** and the Auto/Fixed dialog act on
|
||||||
|
the whole row whichever plot they land in, and an auto-scaled row fits the union of its plots' traces (a plot
|
||||||
|
with none is ignored). Only the **bottom** row's plots carry the time labels, and the left margin
|
||||||
holding the y labels is one width for the whole figure, so the plots of a row line up with the rows
|
holding the y labels is one width for the whole figure, so the plots of a row line up with the rows
|
||||||
above and below it; a plot whose labels are narrower simply has more margin.
|
above and below it; a plot whose labels are narrower simply has more margin. Because only the first plot
|
||||||
|
has a margin, the plots of a row get equal plot areas at equal weights.
|
||||||
|
|
||||||
In the **Layout editor** (`l`):
|
In the **Layout editor** (`l`):
|
||||||
|
|
||||||
@@ -2847,8 +2852,8 @@ In the **Layout editor** (`l`):
|
|||||||
|
|
||||||
Plots are named by their row and, in a split row, a letter: **`1`**, **`2a`**..**`2d`**. The names are
|
Plots are named by their row and, in a split row, a letter: **`1`**, **`2a`**..**`2d`**. The names are
|
||||||
what the signals page's *Plot on* buttons (tooltip "row 2, cell 1 of 2"), the status notices ("plotting
|
what the signals page's *Plot on* buttons (tooltip "row 2, cell 1 of 2"), the status notices ("plotting
|
||||||
'sine' on plot 2b") and the cursors pane for a split row (`plot 2a`; a plot of an unsplit row stays
|
'sine' on plot 2b") and the cursors pane (one ruler line per figure row, `row 2` counted from 1 for a split row; a plot of an
|
||||||
`row N`, counted from 0 as before) show. A signal dragged from its badge lands on the plot under the
|
unsplit row stays `row N`, counted from 0 as before) show. A signal dragged from its badge lands on the plot under the
|
||||||
pointer, with the same free-slot rule as a row. The session file keeps the arrangement (`row` and `width`
|
pointer, with the same free-slot rule as a row. The session file keeps the arrangement (`row` and `width`
|
||||||
on each `[[plot]]`, see "The session layout file").
|
on each `[[plot]]`, see "The session layout file").
|
||||||
|
|
||||||
|
|||||||
+6
-4
@@ -87,10 +87,12 @@ up to **4** plots, and the figure up to **8** in all.
|
|||||||
of one plot it removes the row.
|
of one plot it removes the row.
|
||||||
|
|
||||||
Every plot shares **one time window**: panning or zooming in any of them moves them all, the crosshair
|
Every plot shares **one time window**: panning or zooming in any of them moves them all, the crosshair
|
||||||
sits at the same time in each, and the vertical cursors are the same two times in each. Each plot has
|
sits at the same time in each, and the vertical cursors are the same two times in each. The plots of a split
|
||||||
its **own y axis**, its own horizontal rulers and its own fit, and only the bottom row shows the time
|
row also share **one y range**: the y labels are drawn at the left of the first plot only, the
|
||||||
|
horizontal rulers are the same two values in every plot, and a y zoom, pan, fit or fixed range in any of
|
||||||
|
them applies to the whole row. Only the bottom row shows the time
|
||||||
labels. A plot of a split row is named by its row and a letter, `2a` to `2d`: that is what the signals
|
labels. A plot of a split row is named by its row and a letter, `2a` to `2d`: that is what the signals
|
||||||
page's **Plot on** buttons, the status line and the cursors readout call it, and a signal dragged
|
page's **Plot on** buttons and the status line call it, and a signal dragged
|
||||||
from its badge goes onto the plot under the pointer. The arrangement is saved per source, and a session
|
from its badge goes onto the plot under the pointer. The arrangement is saved per source, and a session
|
||||||
saved before rows could be split opens with one plot per row, as it was.
|
saved before rows could be split opens with one plot per row, as it was.
|
||||||
|
|
||||||
@@ -153,7 +155,7 @@ does not walk it off its snapshot.
|
|||||||
The cursors toggle in the header bar (`c`) switches on two instruments per row:
|
The cursors toggle in the header bar (`c`) switches on two instruments per row:
|
||||||
|
|
||||||
- **vertical** — two times, shared by every row (and every plot of a split row);
|
- **vertical** — two times, shared by every row (and every plot of a split row);
|
||||||
- **horizontal** — two rulers per plot;
|
- **horizontal** — two rulers per row (every plot of a split row shows the same two values);
|
||||||
- **both** — the two together;
|
- **both** — the two together;
|
||||||
- **mix** — the two times, read off each *trace* where they cross it.
|
- **mix** — the two times, read off each *trace* where they cross it.
|
||||||
|
|
||||||
|
|||||||
@@ -266,7 +266,8 @@ Layers to keep straight:
|
|||||||
`figure_row_count()` counts the rows. The figure owns
|
`figure_row_count()` counts the rows. The figure owns
|
||||||
everything the plots have to agree on: the x window (one x link group every plot joins),
|
everything the plots have to agree on: the x window (one x link group every plot joins),
|
||||||
the live follow (the *group's* — a member's own axis must be `XFollow::Off`, since
|
the live follow (the *group's* — a member's own axis must be `XFollow::Off`, since
|
||||||
`Figure::link_x()` refuses a `Live` member), the left gutter and the crosshair.
|
`Figure::link_x()` refuses a `Live` member), the y range of each figure row's cells (`set_share_row_y`: a split
|
||||||
|
row has one y axis, one ruler pair and one fit), the left gutter and the crosshair.
|
||||||
|
|
||||||
- `ExportTable` (`src/export.hpp`) — the visible window as columns, collected from the
|
- `ExportTable` (`src/export.hpp`) — the visible window as columns, collected from the
|
||||||
snapshot or the live rings and handed to the `.npy`/`.csv` writers. Always the stored
|
snapshot or the live rings and handed to the `.npy`/`.csv` writers. Always the stored
|
||||||
|
|||||||
@@ -281,6 +281,28 @@ Cell widths use the height scheme: a floor of **64 px logical** per cell, the we
|
|||||||
the last cell closes the row exactly, and a figure too narrow for the floors shrinks the cells
|
the last cell closes the row exactly, and a figure too narrow for the floors shrinks the cells
|
||||||
proportionally and drops the column gap. Only the bottom row's cells carry x labels and the x title
|
proportionally and drops the column gap. Only the bottom row's cells carry x labels and the x title
|
||||||
(every other plot gets `XStrip::None`), and the left y gutter stays one maximum over the whole figure.
|
(every other plot gets `XStrip::None`), and the left y gutter stays one maximum over the whole figure.
|
||||||
|
|
||||||
|
The cells of a figure row can **share one y range**, symmetric to the x link
|
||||||
|
(`docs/superpowers/specs/2026-11-03-split-shared-y-design.md`):
|
||||||
|
|
||||||
|
- `Figure::set_share_row_y(bool)`, default off so every existing figure and `tests/test_figure.cpp`
|
||||||
|
read as before; the host turns it on in `RowSet::rebuild_all()` before the cells are populated. When
|
||||||
|
on, the cells of each figure row form an implicit y link group, rebuilt with the cells.
|
||||||
|
- Link semantics: `layout()` runs a `sync_y_links()` that adopts a member's changed `y_range` as the
|
||||||
|
group's and pushes it to the others, so a wheel, pan, box zoom or axis drag (all `set_y_range`) needs
|
||||||
|
no controller change; a manual `set_y_range` on a member clears the group's auto flag. A member's own
|
||||||
|
autoscale is suppressed and the group owns the flag: it fits to the union of its members' visible
|
||||||
|
traces (cells with no trace are ignored; a log axis takes the union in axis space), and `fit_all` and
|
||||||
|
a double-click fit the group, not one cell. The group only follows on a laid-out frame, so a caller
|
||||||
|
that sets a range or a fit and reads it back needs one `layout()` in between.
|
||||||
|
- `YStrip {Both, None}` and `Plot::set_y_strip_mode`, mirroring `XStrip`: `None` forces the y label
|
||||||
|
width, title width and reserved floor to 0. `Figure::layout` sets `None` on every cell but the first
|
||||||
|
of its row, structurally from the column index and never from content, and skips `None` cells in the
|
||||||
|
common-gutter maximum. A y axis drag can therefore start only in the leftmost cell.
|
||||||
|
- **Cell placement is by plot width**: the row's y gutter comes off the row width first, then the cells
|
||||||
|
share the rest by the existing `floor + extra * w / total` rule, the floors applying to plot widths.
|
||||||
|
Equal weights give equal plot areas; the first cell's rect is gutter plus its plot width and the rects
|
||||||
|
still tile the row exactly.
|
||||||
In the GTK4 and GLFW adapters the pointer is routed by **cell rect** (`row_in(rows, x, y)`: the
|
In the GTK4 and GLFW adapters the pointer is routed by **cell rect** (`row_in(rows, x, y)`: the
|
||||||
containing cell, else the nearest by squared distance), one `input::Controller` per plot. The tests are
|
containing cell, else the nearest by squared distance), one `input::Controller` per plot. The tests are
|
||||||
the `figure_cell*` cases in `tests/test_figure.cpp` and `glfw_host_routes_by_cell`.
|
the `figure_cell*` cases in `tests/test_figure.cpp` and `glfw_host_routes_by_cell`.
|
||||||
|
|||||||
@@ -123,27 +123,40 @@ Part of [CLAUDE.md](../../CLAUDE.md). These are rules that cost a defect to lear
|
|||||||
y), and the **vertical cursor pair is one shared time drawn in every cell** (`cursors_aligned`).
|
y), and the **vertical cursor pair is one shared time drawn in every cell** (`cursors_aligned`).
|
||||||
A split is an arrangement of more plots, not a second time scale: nothing about the time axis changes
|
A split is an arrangement of more plots, not a second time scale: nothing about the time axis changes
|
||||||
(see [invariants-time-axis-and-window.md](invariants-time-axis-and-window.md)).
|
(see [invariants-time-axis-and-window.md](invariants-time-axis-and-window.md)).
|
||||||
- **Per-cell y.** Each cell has its own y axis (auto or fixed), its own horizontal rulers and its own
|
- **One y per figure row.** The cells of a figure row share one y range (the plotter's
|
||||||
fit; a wheel or axis drag acts on the cell under the pointer, an x gesture on any bottom-row cell
|
`Figure::set_share_row_y(true)`, which `rebuild_all()` sets before the cells are populated): one y
|
||||||
moves the shared window. The cursors pane's ruler label is `plot 2a` for a plot of a split row and
|
axis (auto or fixed), one pair of horizontal rulers, one fit. A wheel or axis drag in any cell moves
|
||||||
`row N` (the plot index) for one of an unsplit row, so the pane reads the same as before until a row
|
the row, an x gesture on any bottom-row cell moves the shared window. The host therefore acts on the
|
||||||
is split.
|
**figure row** of a plot it is given (`set_y_auto`, `set_y_range`, `fit_all`, the Auto/Fixed dialog,
|
||||||
|
`row_unit`, which joins the units of every cell and reads "" if they differ). The rulers are stored on
|
||||||
|
the row's first plot index, copied to the other cells by `sync_cursors()` / `insert_cell()` /
|
||||||
|
`rebuild_all()` and placed in every cell by `place_cursors()`. The cursors pane lists **one ruler line
|
||||||
|
per figure row**: `row N` with N the 1-based figure row for a split row, and the 0-based plot index
|
||||||
|
for an unsplit row (the pane read that before splitting existed). Readouts and measurements stay per
|
||||||
|
trace and per plot index; only the axis is shared.
|
||||||
- **Bottom-row x labels only.** The plotter gives every plot that is not in the last figure row
|
- **Bottom-row x labels only.** The plotter gives every plot that is not in the last figure row
|
||||||
`XStrip::None`; the cells of the bottom row each keep their strip, so a middle-drag on the axis
|
`XStrip::None`; the cells of the bottom row each keep their strip, so a middle-drag on the axis
|
||||||
works in any of them. The `t0` caption goes on those cells too.
|
works in any of them. The `t0` caption goes on those cells too.
|
||||||
- **One left gutter.** The y gutter is one maximum for the whole figure with the same reserved floor
|
- **One left gutter, on the first cell only.** The y gutter is one maximum for the whole figure with the
|
||||||
as before (`Figure::set_min_y_gutter()`), **not a per-column one**: a per-column gutter would make a
|
same reserved floor as before (`Figure::set_min_y_gutter()`). It is carried by the first cell of each
|
||||||
cell's width depend on its labels, which "layout must not depend on content" forbids. A cell whose y
|
row alone: `Figure::layout` gives every other cell `YStrip::None` from its column index (structurally,
|
||||||
labels are narrower than the gutter simply has a wider margin.
|
never from what it holds), which forces its y label width, title width and reserved floor to 0. A
|
||||||
- **Widths are weights with a floor.** The plotter keeps 64 px logical per cell and a column gap
|
`None` cell contributes 0 to the common maximum, so a split row's non-first cells cannot widen the
|
||||||
(`kColGap`, 6 px); the editor's drag is stricter (below). A weight is a share of what is
|
gutter and a row's gutter does not depend on how many cells it has. A per-column gutter sized by the
|
||||||
left *after* the floors, so cells of `{3, 1}` read 533:220 in a 756 px figure, not 3:1, and the
|
labels would make a cell's width depend on its content, which "layout must not depend on content"
|
||||||
editor's 96 px drag floor is a floor on the *width*, not on the share. Because a cell's width is
|
forbids. A row whose labels are narrower than the gutter simply has a wider margin. The weights apply
|
||||||
|
to **plot widths**, not cell rects (below).
|
||||||
|
- **Widths are weights with a floor.** The plotter keeps 64 px logical per plot and a column gap
|
||||||
|
(`kColGap`, 6 px); the editor's drag is stricter (below). The floors and the weights act on **plot widths**: the row's y gutter (carried by the first
|
||||||
|
cell) is taken off the row width first, then the cells share the rest, so equal weights give equal plot
|
||||||
|
areas and the first cell's rect is its gutter plus its plot width. A weight is a share of what is
|
||||||
|
left *after* the gutter and the floors, so cells of `{3, 1}` read 546:207 (rects) in a 756 px
|
||||||
|
figure, not 3:1, and the editor's 96 px drag floor is a floor on the plot *width*, not on the share. Because a cell's width is
|
||||||
`64 + extra * weight / total` and not proportional to its weight, `layout_col_apply()` does not scale
|
`64 + extra * weight / total` and not proportional to its weight, `layout_col_apply()` does not scale
|
||||||
the pair's weights by the width ratio: it keeps the pair's weight sum `S` from the drag's *start*
|
the pair's weights by the width ratio: it keeps the pair's weight sum `S` from the drag's *start*
|
||||||
(`layout_col_drag_start`) and gives the left cell `S * (want_left - 64) / (pair - 128)`, clamped to
|
(`layout_col_drag_start`) and gives the left cell `S * (want_left - 64) / (pair - 128)`, clamped to
|
||||||
`[0.001, 0.999]` (the plotter refuses non-positive weights; `0.5` if the pair is not wider than 128).
|
`[0.001, 0.999]` (the plotter refuses non-positive weights; `0.5` if the pair is not wider than 128).
|
||||||
`want_left` is the pointer's width clamped to `[96, pair - 96]` logical px. The divider therefore
|
`want_left` and the pair are plot widths (`layout_col_drag_begin` subtracts the first cell's `y_axis_width + y_title_width` from its rect); `want_left` is the pointer's width clamped to `[96, pair - 96]` logical px. The divider therefore
|
||||||
follows the pointer, the shrinking cell reads 96 px at the extreme and the pair keeps its total width
|
follows the pointer, the shrinking cell reads 96 px at the extreme and the pair keeps its total width
|
||||||
(the split probe's `col-drag` line asserts all three). `kPlotterMinCellPx` (64) mirrors the plotter's
|
(the split probe's `col-drag` line asserts all three). `kPlotterMinCellPx` (64) mirrors the plotter's
|
||||||
`kMinCellWidthLogical`; both are logical px (the plotter scales it by `ui_scale`, which is
|
`kMinCellWidthLogical`; both are logical px (the plotter scales it by `ui_scale`, which is
|
||||||
|
|||||||
@@ -2853,19 +2853,19 @@ inside the panel does not close it), `click_outside_closes=1` (a click on the pl
|
|||||||
|
|
||||||
```
|
```
|
||||||
[split-probe] start plots=1 frows=1 cells=[1] widths=[756] tile=1 x_equal=1 cursors_aligned=1 weights=[-] traces=[1:sine,square,ramp,noise]
|
[split-probe] start plots=1 frows=1 cells=[1] widths=[756] tile=1 x_equal=1 cursors_aligned=1 weights=[-] traces=[1:sine,square,ramp,noise]
|
||||||
[split-probe] split plots=2 frows=1 cells=[2] widths=[376,376] tile=1 x_equal=1 cursors_aligned=1 weights=[-] traces=[1a:sine|1b:square]
|
[split-probe] split plots=2 frows=1 cells=[2] widths=[403,350] tile=1 x_equal=1 cursors_aligned=1 weights=[-] traces=[1a:sine|1b:square]
|
||||||
[split-probe] weights 3:1 plots=2 frows=1 cells=[2] widths=[533,220] tile=1 x_equal=1 cursors_aligned=1 weights=[3,1] traces=[1a:sine|1b:square]
|
[split-probe] weights 3:1 plots=2 frows=1 cells=[2] widths=[546,207] tile=1 x_equal=1 cursors_aligned=1 weights=[3,1] traces=[1a:sine|1b:square]
|
||||||
[split-probe] caps-calls insert_cell=[1,1,0] refused_cell=1 insert_row=[1,1,0] refused_row=1 plots=8
|
[split-probe] caps-calls insert_cell=[1,1,0] refused_cell=1 insert_row=[1,1,0] refused_row=1 plots=8
|
||||||
[split-probe] roundtrip rt=1 expect=r0(h=2.0000)[w=1.0000:sine] r1(h=1.0000)[w=3.0000:square][w=1.0000:ramp] got=r0(h=2.0000)[w=1.0000:sine] r1(h=1.0000)[w=3.0000:square][w=1.0000:ramp]
|
[split-probe] roundtrip rt=1 expect=r0(h=2.0000)[w=1.0000:sine] r1(h=1.0000)[w=3.0000:square][w=1.0000:ramp] got=r0(h=2.0000)[w=1.0000:sine] r1(h=1.0000)[w=3.0000:square][w=1.0000:ramp]
|
||||||
[split-probe] ruler-labels split=[row 1,row 2] mixed=[row 0,row 2]
|
[split-probe] ruler-labels split=[row 1,row 2] mixed=[row 0,row 2]
|
||||||
[split-probe] shared-y y_shared=1 y_gutter=1 y_fit=1 ruler_mirror=1 ranges=[A=-3.000000..7.000000 B=-3.000000..7.000000 | fit A=-1.04..1.04 B=-1.04..1.04 data=-1..1] axes_w=[642.4,642.4] auto=1
|
[split-probe] shared-y y_shared=1 y_gutter=1 y_fit=1 ruler_mirror=1 ranges=[A=-3.000000..7.000000 B=-3.000000..7.000000 | fit A=-1.04..1.04 B=-1.04..1.04 data=-1..1] axes_w=[642.4,642.4] auto=1
|
||||||
```
|
```
|
||||||
|
|
||||||
`UDPSCOPE_PROBE_SPLIT_MS` (stderr; see [verification.md](verification.md)). Nineteen steps, 250 ms apart;
|
`UDPSCOPE_PROBE_SPLIT_MS` (stderr; see [verification.md](verification.md)). 23 steps, 250 ms apart;
|
||||||
each restructures and the *next* step reports the frame that was laid out with it, so a line's label
|
each restructures and the *next* step reports the frame that was laid out with it, so a line's label
|
||||||
names the arrangement the line **reads**, not the call that made it. The state lines share one format:
|
names the arrangement the line **reads**, not the call that made it. The state lines share one format:
|
||||||
`plots` (`row_count()`), `frows` (`figure_row_count()`), `cells=[..]` (per figure row), `widths=[..]`
|
`plots` (`row_count()`), `frows` (`figure_row_count()`), `cells=[..]` (per figure row), `widths=[..]`
|
||||||
(logical pixels of each cell, `|` between rows), `weights=[..]` (the stored cell weights, `-` for a row with
|
(logical pixels of each cell rect, `|` between rows; the first cell's rect includes the row's y gutter, ~52 px here, and the others have none, so equal weights read `[403,350]` -- equal *plot* widths, not equal rects), `weights=[..]` (the stored cell weights, `-` for a row with
|
||||||
none), `traces=[label:signals|..]` with the labels `plot_label()` gives (`1`, `1a`, `2b`). Every state line
|
none), `traces=[label:signals|..]` with the labels `plot_label()` gives (`1`, `1a`, `2b`). Every state line
|
||||||
carries the same three assertions:
|
carries the same three assertions:
|
||||||
|
|
||||||
@@ -2879,22 +2879,22 @@ The other lines, in order:
|
|||||||
|
|
||||||
| line | value it must read | the defect a wrong reading names |
|
| line | value it must read | the defect a wrong reading names |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `start` / `split` | `cells=[1]` then `[2]` with `widths=[376,376]` (equal halves of 756) and the traces on `1a`/`1b`; a second cell starts **empty**, the probe assigns it | a split that moved or dropped the first cell's traces, or unequal default cells |
|
| `start` / `split` | `cells=[1]` then `[2]` with `widths=[403,350]` (equal plot widths of 350.4 px; the first rect is 52 px wider by the y gutter it alone carries) and the traces on `1a`/`1b`; a second cell starts **empty**, the probe assigns it | a split that moved or dropped the first cell's traces, or unequal default plot widths (`[376,376]` is the pre-sharing reading: a second cell that still has its own gutter) |
|
||||||
| `weights 3:1` | `widths=[533,220]`, **not** 3:1, and `tile=1` | not a defect: the plotter's 64 px floor per cell is taken off first and the weights share the remainder, so the shares read compressed; a result of exactly 3:1 would be the floor missing |
|
| `weights 3:1` | `widths=[546,207]`, **not** 3:1, and `tile=1` | not a defect: the row's y gutter and the plotter's 64 px floor per plot are taken off first and the weights share the remainder, so the shares read compressed; a result of exactly 3:1 would be the floor missing |
|
||||||
| `row+cell` | `cells=[2,2]`, `weights=[3,1\|-]` | row 0's cell weights lost or moved when a row and a cell were inserted below it (they belong to the figure row) |
|
| `row+cell` | `cells=[2,2]`, `weights=[3,1\|-]` | row 0's cell weights lost or moved when a row and a cell were inserted below it (they belong to the figure row) |
|
||||||
| `caps-calls` | `insert_cell=[1,1,0] refused_cell=1 insert_row=[1,1,0] refused_row=1 plots=8` | a cap that is not reached or not held: row 0 starts with two cells and takes two more, refusing the third call at 4; two rows then fit before the plot total reaches 8 and the third is refused; `plots` must stop at **8** |
|
| `caps-calls` | `insert_cell=[1,1,0] refused_cell=1 insert_row=[1,1,0] refused_row=1 plots=8` | a cap that is not reached or not held: row 0 starts with two cells and takes two more, refusing the third call at 4; two rows then fit before the plot total reaches 8 and the third is refused; `plots` must stop at **8** |
|
||||||
| `caps` | `plots=8 frows=4 cells=[4,2,1,1]`, `tile=1` | a ninth plot, or a fifth cell in a row |
|
| `caps` | `plots=8 frows=4 cells=[4,2,1,1]`, `tile=1` | a ninth plot, or a fifth cell in a row |
|
||||||
| `remove` | `plots=7 cells=[3,2,1,1]` | `remove_cell()` on a multi-cell row that removed the row, or that dropped a neighbour's traces |
|
| `remove` | `plots=7 cells=[3,2,1,1]` | `remove_cell()` on a multi-cell row that removed the row, or that dropped a neighbour's traces |
|
||||||
| `roundtrip-io` | `wrote=1 read=1 plots_in_file=3` | a save that was refused, or a file that lost a `[[plot]]` table |
|
| `roundtrip-io` | `wrote=1 read=1 plots_in_file=3` | a save that was refused, or a file that lost a `[[plot]]` table |
|
||||||
| `roundtrip` (state line) | `cells=[1,2] widths=[756\|533,220] weights=[-\|3,1]` and the three traces on `1`, `2a`, `2b` | the arrangement did not survive `collect_layout()` -> file -> `apply_layout()` |
|
| `roundtrip` (state line) | `cells=[1,2] widths=[756\|546,207] weights=[-\|3,1]` and the three traces on `1`, `2a`, `2b` | the arrangement did not survive `collect_layout()` -> file -> `apply_layout()` |
|
||||||
| `roundtrip rt=` | **`rt=1`** with `got=` equal to `expect=` (row weights `h=`, cell weights `w=` and each cell's trace, read back at the file's `%.4f`) | **`rt=0`** names the difference: a weight written per plot index instead of per figure row, widths applied before the arrangement (which resets them), or a trace that moved to another cell |
|
| `roundtrip rt=` | **`rt=1`** with `got=` equal to `expect=` (row weights `h=`, cell weights `w=` and each cell's trace, read back at the file's `%.4f`) | **`rt=0`** names the difference: a weight written per plot index instead of per figure row, widths applied before the arrangement (which resets them), or a trace that moved to another cell |
|
||||||
| `legacy-io` | `read=1 plots_in_file=3 row_unset=1` | the stripped file still carrying a `row`, which would make the next line test nothing |
|
| `legacy-io` | `read=1 plots_in_file=3 row_unset=1` | the stripped file still carrying a `row`, which would make the next line test nothing |
|
||||||
| `legacy` | `cells=[1,1,1]`, `frows=3`, `widths=[756\|756\|756]`, `traces=[1:sine\|2:square\|3:ramp]` | a session from before the split that did not open as one cell per row, or lost its signals |
|
| `legacy` | `cells=[1,1,1]`, `frows=3`, `widths=[756\|756\|756]`, `traces=[1:sine\|2:square\|3:ramp]` | a session from before the split that did not open as one cell per row, or lost its signals |
|
||||||
| `invalid-rows` | `ok=1 frows/plots=[5/5 3/3 3/3 3/3]` | one of four unusable `row` sequences (five cells in a row; a skipped row; a row that goes back; a first row that is not 0) that was refused, or kept as a split instead of loading one cell per row |
|
| `invalid-rows` | `ok=1 frows/plots=[5/5 3/3 3/3 3/3]` | one of four unusable `row` sequences (five cells in a row; a skipped row; a row that goes back; a first row that is not 0) that was refused, or kept as a split instead of loading one cell per row |
|
||||||
| `editor-on` -> `cell-split` | `cells=[1]` then `cell-split-ck cells=2` and `cell-split` with `widths=[376,376]`, the new `1b` empty | the editor's cell `+` not reaching `insert_cell()`, or the figure not re-pointed after it |
|
| `editor-on` -> `cell-split` | `cells=[1]` then `cell-split-ck cells=2` and `cell-split` with `widths=[403,350]`, the new `1b` empty | the editor's cell `+` not reaching `insert_cell()`, or the figure not re-pointed after it |
|
||||||
| `cell-assigned` | `1a:sine\|1b:square` | an assignment into the new cell that landed on another plot |
|
| `cell-assigned` | `1a:sine\|1b:square` | an assignment into the new cell that landed on another plot |
|
||||||
| `col-drag` | `ok=1 left_gt_right=1 follows_80=1 relative=1 sum_80=1 right_floor=1 left_floor=1 sum_ext=1 both_above_min=1 tile=1`, then `d80=[376.5,376.5]->[456.5,296.5]` and `ext=[657.0,96.0\|96.0,657.0]` (all logical px, 753 px pair) | `ok` is the AND of the checks. `follows_80=0`: the drag of +80 did not put the left cell within 1 px of `start + 80` (the weights were taken as proportional to widths and the plotter's 64 px floor ignored -- an old reading was `[442.9,310.1]`); `relative=0`: three updates (+30, +60, +80) from one begin did not land at +80, i.e. the drag read the live weights instead of the begin snapshot `layout_col_drag_start`; `sum_80=0` / `sum_ext=0`: the pair's total changed instead of being redistributed; `right_floor=0` / `left_floor=0`: after +10000 / -10000 the shrinking cell does not read `kMinCellPx` (96) within 1 px; `both_above_min=0`: a cell below 96 px; `tile=1` is `split_probe_tile()`, the same check the state lines print. `left_gt_right` is `d80.l > d80.r` (after +80 from equal halves the left is the wider). The probe goes through `layout_col_drag_begin()` + `layout_col_apply()`, the pair the gesture calls |
|
| `col-drag` | `ok=1 left_gt_right=1 follows_80=1 relative=1 sum_80=1 right_floor=1 left_floor=1 sum_ext=1 both_above_min=1 tile=1`, then `d80=[350.4,350.4]->[430.4,270.4]`, `ext=[604.8,96.0\|96.0,604.8]` and `gutter0=52.2` (all logical **plot** widths, `axes.w` plus the right gutter of the two plots, measured after a `layout()` on the new weights; 700.8 px pair) | `ok` is the AND of the checks. The compared widths are plot widths and not cell rects, because only the first cell carries the y gutter (`gutter0=` is its rect width minus its plot width, printed so a reader can tell the two apart): a drag that equates the rect widths moves the divider ~17 px more than the pointer (the gutter's share is treated as weight; `d80=[350.4,350.4]->[447.6,253.2]`, +97.2 for +80) and reaches the left floor at 145.5 instead of 96 (`ext=[607.5,93.3\|93.3,607.5]` or `[659.7,93.3\|145.5,607.5]`), which reads `follows_80=0 relative=0 left_floor=0 right_floor=0 both_above_min=0`. `gutter0=` itself should read the y gutter (about 52 px; 0 means the first cell lost its gutter, a value near the cell width means the plot area collapsed). `follows_80=0`: the drag of +80 did not put the left cell within 1 px of `start + 80` (the weights were taken as proportional to widths and the plotter's 64 px floor ignored -- an old reading was `[442.9,310.1]`); `relative=0`: three updates (+30, +60, +80) from one begin did not land at +80, i.e. the drag read the live weights instead of the begin snapshot `layout_col_drag_start`; `sum_80=0` / `sum_ext=0`: the pair's total changed instead of being redistributed; `right_floor=0` / `left_floor=0`: after +10000 / -10000 the shrinking cell does not read `kMinCellPx` (96) within 1 px; `both_above_min=0`: a cell below 96 px; `tile=1` is `split_probe_tile()`, the same check the state lines print. `left_gt_right` is `d80.l > d80.r` (after +80 from equal halves the left is the wider). The probe goes through `layout_col_drag_begin()` + `layout_col_apply()`, the pair the gesture calls |
|
||||||
| `pre-insert0` (state line) | after the extreme drags `widths=[96,657]` | the last extreme drag was -10000: the left cell sits on the 96 px floor and the right takes the rest of the pair. A left cell reading 144 is the old floor-ignoring weights (`weights=[0.25,1.75]`); correct weights read `[0.1,1.9]` |
|
| `pre-insert0` (state line) | after the extreme drags `widths=[148,605]`, `weights=[0.111732,1.88827]` | the last extreme drag was -10000: the left *plot* sits on the 96 px floor (the rect reads 148 = 96 plus the 52 px y gutter) and the right takes the rest of the pair. A left rect reading 144 is the old floor-ignoring weights (`weights=[0.25,1.75]`); `[146,607]` / `[96,657]` are the readings of the drag before it measured plot widths and before the sharing (floor 93 px / 96 px rect) |
|
||||||
| `insert0-move` | `move_ok=1 before=[sine,square] after=[,sine,square]` | an insert at the left edge that renumbered the cells instead of moving their traces right with them |
|
| `insert0-move` | `move_ok=1 before=[sine,square] after=[,sine,square]` | an insert at the left edge that renumbered the cells instead of moving their traces right with them |
|
||||||
| `remove-mid` | `rm_ok=1 after=[,square]` | removing a middle cell that dropped or shifted the wrong neighbour |
|
| `remove-mid` | `rm_ok=1 after=[,square]` | removing a middle cell that dropped or shifted the wrong neighbour |
|
||||||
| `row-weights` | `wt_ok=1 before=[3,1,1] after=[1,1]` | the row weights not following an insert or removal of a *figure row* (the vector had the plot-count length once and drifted off the rows) |
|
| `row-weights` | `wt_ok=1 before=[3,1,1] after=[1,1]` | the row weights not following an insert or removal of a *figure row* (the vector had the plot-count length once and drifted off the rows) |
|
||||||
@@ -2905,7 +2905,7 @@ The other lines, in order:
|
|||||||
| `assign-1b` / `drop-2b` | `assigned=1 notice='plotting 'sine' on plot 1b'`; `dropped=1 notice='plotting 'square' on plot 2b'` | a notice that names the plot by number alone |
|
| `assign-1b` / `drop-2b` | `assigned=1 notice='plotting 'sine' on plot 1b'`; `dropped=1 notice='plotting 'square' on plot 2b'` | a notice that names the plot by number alone |
|
||||||
| `after-assign` (state line) | `traces=[1a:-\|1b:sine\|2a:-\|2b:square]` | the signal went to a neighbouring cell |
|
| `after-assign` (state line) | `traces=[1a:-\|1b:sine\|2a:-\|2b:square]` | the signal went to a neighbouring cell |
|
||||||
| `ruler-labels` | `split=[row 1,row 2]` and, with cells `{1,2}`, `mixed=[row 0,row 2]` | the cursors pane's ruler lines (a label read through the pane's own widgets): **one line per figure row**, because the cells of a split row share their y axis and so their rulers. A split row is `row <figure row, from 1>` and sits on its first cell; an unsplit row keeps `row <plot index>` (**0-based**, as it always was -- `row 0` is the first plot, not a defect). One line per cell (`plot 1a,plot 1b,...`) is the pre-sharing reading |
|
| `ruler-labels` | `split=[row 1,row 2]` and, with cells `{1,2}`, `mixed=[row 0,row 2]` | the cursors pane's ruler lines (a label read through the pane's own widgets): **one line per figure row**, because the cells of a split row share their y axis and so their rulers. A split row is `row <figure row, from 1>` and sits on its first cell; an unsplit row keeps `row <plot index>` (**0-based**, as it always was -- `row 0` is the first plot, not a defect). One line per cell (`plot 1a,plot 1b,...`) is the pre-sharing reading |
|
||||||
| `shared-y` | `y_shared=1 y_gutter=1 y_fit=1 ruler_mirror=1`, `ranges=[A=-3..7 B=-3..7 | fit A=B]`, `axes_w` equal to within a pixel, `auto=1` | a row of two cells with a trace each. `y_shared`: a `set_y_range(-3, 7)` on cell A is B's after one frame. `y_gutter`: only cell A has a y gutter (B's `y_axis_width` and `y_label_area.w` are 0) and the two plots are equally wide. `y_fit`: `fit_all()` leaves both cells on one range that covers both traces (not the manual one). `ruler_mirror`: a ruler written on A and read back by `sync_cursors(0)` is B's too. A 0 in the first two is `set_share_row_y(true)` not reaching the figure; in `y_fit`, no frame between `fit_all()` and the read (the probe restructures to pump one) or a group that did not follow the flag |
|
| `shared-y` | `y_shared=1 y_gutter=1 y_fit=1 ruler_mirror=1`, `ranges=[A=-3..7 B=-3..7 \| fit A=B]`, `axes_w` equal to within a pixel, `auto=1` | a row of two cells with a trace each. `y_shared`: a `set_y_range(-3, 7)` on cell A is B's after one frame. `y_gutter`: only cell A has a y gutter (B's `y_axis_width` and `y_label_area.w` are 0) and the two plots are equally wide. `y_fit`: `fit_all()` leaves both cells on one range that covers both traces (not the manual one). `ruler_mirror`: a ruler written on A and read back by `sync_cursors(0)` is B's too. A 0 in the first two is `set_share_row_y(true)` not reaching the figure; in `y_fit`, no frame between `fit_all()` and the read (the probe restructures to pump one) or a group that did not follow the flag |
|
||||||
|
|
||||||
The run ends with the usual `gtk warnings: 0` and no `Gtk-CRITICAL`. `x_equal=1` and `cursors_aligned=1` on every state line are
|
The run ends with the usual `gtk warnings: 0` and no `Gtk-CRITICAL`. `x_equal=1` and `cursors_aligned=1` on every state line are
|
||||||
the only readings of the shared time window across cells.
|
the only readings of the shared time window across cells.
|
||||||
@@ -44,7 +44,8 @@
|
|||||||
any row moves it on all of them. That is what makes <code>dt</code> meaningful across
|
any row moves it on all of them. That is what makes <code>dt</code> meaningful across
|
||||||
rows.</p></item>
|
rows.</p></item>
|
||||||
<item><p>A ruler belongs to its row and nothing shares it, so two rows' rulers are
|
<item><p>A ruler belongs to its row and nothing shares it, so two rows' rulers are
|
||||||
independent.</p></item>
|
independent. The plots of a split row are one row here: they share their y axis, so the
|
||||||
|
rulers are at the same values in each.</p></item>
|
||||||
<item><p><gui>mix</gui> is the reading mode: one line per <em>bound</em> trace, with the
|
<item><p><gui>mix</gui> is the reading mode: one line per <em>bound</em> trace, with the
|
||||||
trace's own values at each marker and their difference. A slot with a name but no
|
trace's own values at each marker and their difference. A slot with a name but no
|
||||||
channel behind it is not a trace and gets no line.</p></item>
|
channel behind it is not a trace and gets no line.</p></item>
|
||||||
|
|||||||
@@ -57,7 +57,9 @@
|
|||||||
</steps>
|
</steps>
|
||||||
<p>All the plots of a row, and of the whole figure, share one time window: zooming or
|
<p>All the plots of a row, and of the whole figure, share one time window: zooming or
|
||||||
panning along time in any of them moves all of them, and the vertical cursors are drawn in
|
panning along time in any of them moves all of them, and the vertical cursors are drawn in
|
||||||
every plot. The y axes, the horizontal cursors and the fits belong to each plot. Only the
|
every plot. The plots of a split row also share one y range: the y labels are drawn at the
|
||||||
|
left of the first plot only, the horizontal cursors sit at the same values in every plot, and a
|
||||||
|
zoom, pan, fit or fixed range in any of them applies to the whole row. Only the
|
||||||
bottom row carries the time labels. A row holds at most 4 plots and the figure at most 8
|
bottom row carries the time labels. A row holds at most 4 plots and the figure at most 8
|
||||||
in total; the <gui>+</gui> buttons go grey at the limit.</p>
|
in total; the <gui>+</gui> buttons go grey at the limit.</p>
|
||||||
</section>
|
</section>
|
||||||
@@ -66,7 +68,7 @@
|
|||||||
<title>Worth knowing</title>
|
<title>Worth knowing</title>
|
||||||
<list>
|
<list>
|
||||||
<item><p>A plot's own <gui>right-click menu</gui> (the same compact rows as the app's
|
<item><p>A plot's own <gui>right-click menu</gui> (the same compact rows as the app's
|
||||||
menu) holds the y-axis scale, the two fits, and <gui>Remove signal</gui>, which swaps
|
menu) holds the y-axis scale (for the whole row), the two fits, and <gui>Remove signal</gui>, which swaps
|
||||||
the menu to that plot's traces plus <gui>All traces on this plot</gui> — with a
|
the menu to that plot's traces plus <gui>All traces on this plot</gui> — with a
|
||||||
<gui>Back</gui> row to return. That is how a trace comes off a row without going through
|
<gui>Back</gui> row to return. That is how a trace comes off a row without going through
|
||||||
the layout editor.</p></item>
|
the layout editor.</p></item>
|
||||||
|
|||||||
Reference in new issue
Block a user