Lab Plugin
The experiment kernel for Clayground Labs: turns any sandbox into an
interactive, deterministic, agent-verifiable experimentation space.
Components
- Lab (singleton) — registry connecting everything; agents read/write
parameters via
Lab.p(name) / Lab.set(name, value) and fetch
Lab.labInfo() through the inspector’s eval action.
- Parameter — named, ranged tunable; bind your system to
value.
- Probe — named observable sampled on a fixed sim-time grid.
- SimClock — seeded deterministic clock; with a
world attached, sim
time advances with the physics steps (exact under the inspector’s
time/step action); seeded random()/randomRange() are the only
randomness a lab may use.
- ParamPanel — auto-generated slider panel for all parameters. Rows are
Tab-reachable and arrow-operable, with a visible focus ring.
- Plot2D — live autoscaled strip chart of probes. Pass
probes for a
fixed set, or series: [{probe, label, color, style, sigmaProbe}] when the
plotted set is built at runtime: the lab then owns naming and colouring, an
empty array draws placeholder instead of every probe, and legend entries
become clickable (seriesClicked) so the user can drop a curve where it is
named. style: "scatter" leaves discrete measurements unjoined,
sigmaProbe fills a translucent ±σ band behind a curve, and hovering the
chart reads every visible series back at the nearest sample.
- DataRecorder — probes to a run record (
record.js): lab, scenario,
seed, every parameter, the per-probe series with their summaries and the
command that regenerates it, no wall clock anywhere - so two runs of one
seed are byte-identical and a paper can cite the file by id. A .csv
destination still writes the flat table, but a CSV carries no provenance
and so cannot be cited. Always set an explicit destination (a relative
default once littered the repo root).
- LabTheme / ThemeSwitch / ScaleSwitch — every colour, shape, type and
spacing token, in a light and a dark palette that swap at runtime, all
multiplied by one
uiScale factor. The two palettes are counterparts
rather than inversions, and the rules that make them so live (and are
tested) in palette.js — most importantly LabTheme.inkOn(fill), which
every chip and badge must use instead of naming an ink, and
LabTheme.step(c, amount), which moves a colour away from the ground in
whichever direction the palette has room. The measurement half is
tokens.js: seven type roles (fontMicro … fontTitle), six spacing
steps (spaceXs … spaceXxl) and LabTheme.px(n) for one-off geometry.
node palette.test.js and node tokens.test.js check the relationships,
not the values. Two roles are easy to miss: the board has its own (board
for the sky, table, sheet, inkSolid for a rim or wall - ink as a lit
surface cannot simply invert or it becomes a light source), because a
recessed 2D paperDeep well still sinks in the dark while the board’s
ordering inverts; and data tokens keep their identity across themes - the
paper says “the rose track is GPS” and the legend has to agree in both, so a
colour is measured on the dark ground and lifted along its own hue only if
it fails to read there. The scale exists because a lab shown on a large
external screen had HUD controls nobody could read and nothing to turn:
every size in the chrome was a bare pixel literal.
- LabPrefs — the three settings that belong to the person rather than to
the run:
ui.theme, ui.scale, ui.lang. Backed by Clayground.Storage
when it is present and by memory when it is not, so a lab that never links
the storage plugin still runs — it just forgets on exit.
- LabLang / LangSwitch — runtime language switch for a published lab.
Whoever owns a vocabulary registers it (
LabLang.register(dict) with
{lang: {key: text}}), strings are ordinary bindings on LabLang.t(key)
/ tf(key, ...), and LabLang.num(v, digits) prints numbers in the
language’s notation (German gets a decimal comma; Plot2D, BudgetBar
and ParamPanel already use it). LabLang.qty(v, unit, digits) adds the
quantity layer on top — SI prefixes with the mA↔A, ms↔s and k/M crossovers
every lab used to hand-roll (format.js, node format.test.js). Not
qsTr: retranslating a live engine is a C++ call on the QQmlEngine,
which a lab hosted by the dojo or exported to WASM does not own. Drop
LangSwitch in a corner and it offers exactly the registered languages.
- Scenario / ScenarioSet — named, scripted situations wiring the
scenarios()/applyScenario() inspector convention; applying resets
the clock so runs are reproducible.
- Flow / FlowStep / Narrator / FlowChip — the guided, narrated
walkthrough: verbs-as-data driven through the lab’s own
flowActions(),
demo/task/watch steps, hint→solve escalation, checkpoint scrubbing.
FlowChip is the on-screen offer to be taught, so the lesson is not
hidden behind a key nobody knows.
- CameraDirector — which shot when, for a lab with a presenter in it:
journey (start, destination and subject in one frame, the presenter
followed until it lands), twoShot (presenter and the thing pointed at),
portrait (level with the face, for explanation), cutaway (one thing
alone for a beat, then back), wide. Science-TV grammar written once,
composed through OrbitCamera3D.fit() with a safe area for the chrome,
so no lab hand-rolls arrival/address hooks and no finger points at
something outside the picture. The professor kit’s FlowGuide takes one
as director:.
Focus mode
Tab clears the HUD: LabView.focus goes true and the instruments, panels,
plot, compass, clock and switches step out of the way, leaving the scene and -
while a flow runs - the Narrator. It is for studying a scene when nothing is
being changed or measured. Most of it is automatic: anything built on
LabPanel fades on its own, as do the kernel’s own pieces. A lab only wires
what it built itself (a scrim, a button declared beside a panel rather than
inside it) with visible: !LabView.focus, and a panel that must survive
focus mode sets hideOnFocus: false. LabPanel uses opacity and enabled
rather than visible, because labs bind visible on their own panels
constantly and a component assigning it would be silently overwritten by
exactly the labs that use it most. The alarm banner deliberately does not
hide - a short circuit outranks whatever you were looking at.
The board — what a build lab is made of
A lab that places typed parts on a grid, wires their pads and solves the
result owns none of that mechanism any more. The domain kit hands over a
part spec (spec[type] = { terminals, half, actuator, fields, rows,
watch }, see board.js), a solver and a part visual; the kernel does the
rest. board.js is pure JS (node board.test.js), tests/tst_board.qml
drives the store and the gesture with no GPU.
- Board — the store: parts, wires,
rev, hit test, keep-out, the
mutations, batching, state()/load(), an optional router; changed
is where the domain re-solves.
- BoardInput — the mouse: wire pads, select and drag, tap a wire, the
two-step actuator (
operate), eraser, the right-click cancel chain; the
camera and the instrument belt are asked first, always. A part’s state
belongs on its selection card (a resistor’s ohms, a gate’s function, a
switch’s on/off), which is also the only channel that survives having no
pointer on a touch screen; operating a part in the scene is a shortcut
gated on selection - the part you picked is the part that responds - so a
click during building flips nothing and no mode has to be remembered. An
actuator region comes first in the hit test (actuatorHalf(type)), has its
own gesture (no selection, no drag, fires on release) and three signals
that agree on one predicate: a pointing-hand cursor, the lever lightening
(never recolouring what already carries state), and a hint-bar line naming
what the click will do. electronics-101’s switch was the cautionary tale: its
pads reached inward and left a few pixels in the middle as the only place a
click flipped it, which read as a bug in wiring rather than in the switch.
- BoardWires3D — every wire as one flat batch plus the dangling preview;
the lab’s
lineOf styles them.
- PartPlacer — the palette’s parts as one handheld: take, ghost, place.
- BoardPalette — presets, parts and tools in foldable sections.
- PartCard — the selection card with the domain’s rows between the
kernel’s title/reading and plot/tag rows; a keyboard target via
keys.
- BoardOverlay — value labels, wire readings, watch marks, pinned tags.
Chrome — what makes two labs look like one product
Everything below was hand-rolled in two labs before it moved here.
- LabPanel — the titled paper panel every HUD is made of; children
stack, or set a size and anchor to its
body.
- LabKeys / LabHelp — the canonical key map (
1..9 presets, T flow,
f jump labels / ⇧F frame (plain F in a jump-less lab), 0 reset,
H takes the next instrument, P keeps its reading,
Shift+R record, ? help, arrows and WASD travel, Shift+arrows
turn) plus the lab’s own keys as data. The arrows used to turn, which a
drag already did well; crossing the scene had no key at all, so turning
moved onto Shift and watching moved off W to Q. f/⇧F are the
map’s first Shift-differentiated pair - f acquires a target, ⇧F frames
it. While a flow runs →/← and Space are the flow’s, so the arrows are
the camera’s only when nothing narrates.
- HintJump — keyboard selection:
f labels every target the lab
names (1–2 home-row letters), typing one selects it in place; off-screen
targets become a grouped badge strip and do fly on pick. The lab
supplies targets() (id, world pos, name, group) and wires
jump: on LabKeys; the letters are physical, the capture follows the
pin-prompt guard, and a focus loss puts the labels away. Declaring a key is what documents
it: LabHelp renders the map from the same list, so the two can never
drift. The six travel letters are reserved and dispatched after the
lab’s own keys, so claiming one silently costs a pan direction.
- HintBar — bottom-centre line that steps aside while a flow narrates
and stays width-capped against its neighbours.
- ScenarioBar — clickable preset chips, each with the one-line reason
it exists (
scenario.note.<name>).
- WatchMonitor — watch a thing, get a probe, a colour and a curve;
owns probe lifecycle, stable names and the one-quantity-per-axis rule —
which, since traces became
(id, quantity) pairs (traceIn), means one
strip per traced quantity, stacked on a shared time axis and cursor
(maxStrips, default 3). A part keeps one colour across strips, so its
WatchMark matches every curve it owns.
WatchChip is the per-object toggle that feeds it (watch / watched /
plot full, the limit read off the monitor), WatchMark the dot the
object then wears in the world, in its curve’s colour.
- WorldLabel — 2D paper chip pinned to a 3D point.
- MarkLayer — rings on the world points something is naming right
now, optionally captioned. Fed by a
FlowStep.mark list or a
performance script’s *mark ...* cue; keepOut keeps a ring off a
presenter standing in front of the part it marks.
- SelectionFrame3D — the shared hover/select language on the work
surface (thin outline hovering, full frame plus facing mark selected).
Used by the circuit kit; note that its lift is measured from the object,
so a part sunk into its board needs a matching
height.
- Compass — which way the work surface faces while you circle it.
- GridMode — snap/free placement with grafli’s contract (
# cycles,
Alt inverts for one gesture). It holds the mode and draws nothing;
LabStage3D is what shows it.
- LabStage3D — the ground every 3D lab stands on, plus the light rig
and the
SceneEnvironment that go with it. One quad, no texture: the
raster is computed in the fragment shader from world coordinates, so it
is millimeter paper on the light palette and blueprint on the dark one,
one line weight at any zoom, and it dissolves into the sky rather than
ending at a board edge. It shows GridMode’s mode as crosses or dots at
the intersections, answers worldAt(view, mx, my) for mouse editing, and
publishes the height/depthBias budget flat overlays have to stay
inside. Three labs built a table, a sheet, a rim, a light rig and 585 peg
Models between them before this existed. Three things to know: GridMode
draws nothing - hand it over with gridMode: and set cueSize: 0 in a lab
that places nothing, because a snap cue on a surface nobody snaps to is a
lie; worldAt(view, mx, my) is the pick, the plane being the only pickable
thing the stage adds (a raster not centred on the origin says so with
rasterOrigin); and flat markings sit between overlayMinY and
overlayMaxY (overlayY(layer) stacks them) with a depthBias in
overlayMinBias..overlayMaxBias - depthBias only settles sort order, so
the lift is what actually does the work.
Instruments — the shelf
Promoted from the labs, which had proved each of them (some three times over):
- InstrumentScale — what a reading means, with nothing that draws it:
value or probe, unit, fixed limits or a self-ranging set of
ranges,
linear or log positioning, severity bands, nice-number gradations, and the
lag and peak-hold of a real movement. One of these feeds as many faces as
the page shows, so they cannot disagree; adaptability lives in the model x
face matrix, so a music VU meter is a BarFace on a log scale with
peak-hold, not a new component. Bands (okUntil/warnUntil, or zones)
are the scale’s, not the face’s: declared once they colour the needle, the
fill, the digits and the tint behind them from LabTheme’s severity
tokens, and a reading past the end of the scale takes the band at that end
- a pinned needle on a red-topped dial must not read back “ok”.
settleTime is a swing for a value that changes on an action; damping
is the lag of a real movement, for a continuously noisy one. Never both.
Which face: Gauge for what is this relative to what the instrument can
take, BarFace for how far along, ColumnFace for how much read off
the scale, DigitFace for what is the number - paired with a needle or a
column, because alone it says nothing about what the number is worth.
demo/Instruments.qml is the reference page: one scale under four faces.
- Gauge — the needle face. Given
ranges it selects its own, and prints
the one it settled on. Laid out in fractions of its own size, so the same
component serves a HUD dial and a Texture baked onto a 3D part.
- BarFace — the level face, horizontal or vertical, optionally as the LED
ladder of a level meter, with the held peak marked. A music VU meter is
this on a log scale, not a component of its own.
- ColumnFace — the thermometer face: every major gradation labelled, for
a quantity read off the scale rather than as a proportion.
- DigitFace — the numeric face, in mono digits through
LabLang.qty().
- InstrumentDock / DockedInstrument — the HUD column, where each
instrument can be put away by the reader and taken back out of a tray at
its foot. The visible set rides in the lab’s
viewState().
- ReadoutPanel / ReadoutRow — swatch · name · live value rows, built from
data, with an optional share bar per row.
- MiniMap — the abstract view: fit-to-content projection plus the repaint
plumbing, driven by a
draw(ctx, map) callback the lab supplies.
- LabBanner — the centred status pill, severity in the fill and the ink
from
inkOn(), blinking only for a live fault.
- TransportChip — sim time, pause and speed, driving
SimClock.timeScale
from outside.
- RecIndicator — the recording dot, so a growing CSV is never a secret.
Instruments you hold
The shelf above is mounted: the lab author bound what each one measures
when the lab was written, and it reads for the whole run. These are the
other half — the viewer binds the subject at runtime by pointing, and the
reading dies with the gesture.
- HandheldInstrument — the contract. An instrument declares what a click
contributes (
pickKind: a "point" on the ground, an "object" in the
scene, or a "moment" in sim time), how many it takes (maxPicks, 0 for
an endless chain), and what the reading means (value / valueText). It
handles no input and knows nothing about the camera. That is the
acceptance test: a new instrument is one file saying what it picks and
what that means, with no gesture code in it.
- InstrumentBelt — what the viewer can pick up, and the owner of the
hand’s click. One line inside the
View3D (pointer: nav, unit: the
lab’s unit) and the lab has a TapeMeasure and a Stopwatch; a kit’s
own instrument is declared inside the belt and joins the same row. A ruler
you have to install first is a ruler nobody reaches for.
- TapeMeasure — the screen-space tape. Its arithmetic is
measure.js,
checked by node, so lengths and angles are not geometry that only exists
inside a paint call.
- Stopwatch — the same contract against the sim clock rather than the
ground.
- CameraAnchorMark — the dotted ring showing what the view is orbiting
and zooming about. A screen-space overlay, because as world content it
clipped into geometry at close range.
pin() is the one transition out: it names the reading, registers it as a
Probe, and from then on it is sampled on the clock grid like any other —
so it lands in the run record and a paper can cite it. It asks for the name
because that name is what gets cited. A measurement itself is never in
viewState(): it is a question being asked now, not scene state.
The mouse
One rule, and the rest follows from it: the left button is never the
camera’s. A mode used to exist only because the camera wanted LMB — panning
sat there, so a lab that needed LMB had to be able to take it back, and the
thing that took it back was the mode. OrbitInput3D declines the left button
instead, so nothing has to.
- RMB drag turns the view about the point under the cursor; a right
click cancels — the “put it down” gesture.
- Middle drag pans, wheel zooms towards the cursor, double-click
focuses. Always live, never taken away.
- LMB is the lab’s: its own tool, or a click handed to whatever
instrument is in the hand. Holding Space lends it to the camera for as
long as the key is down.
- A lab with nothing to build may spend LMB on the view deliberately
(
panButtons: Qt.LeftButton | Qt.MiddleButton) — one decision, made once,
not a mode.
The determinism contract
Every lab must (a) derive all randomness from SimClock (seeded),
(b) run correctly under the inspector’s time pause/step actions,
(c) expose labInfo() — typically just return Lab.labInfo().
Same seed + same stepped frames ⇒ identical probe series.
Minimal lab skeleton
Do not write one — generate it:
tools/lab-new/lab-new <slug> --kind build|continuous|draw --purpose learning|teaching|research
The generated Sandbox.qml answers the whole conventions contract
(scenarios(), applyScenario(), labInfo(), viewState(),
flowActions(), …), comes with a bilingual strings.js, the records and
figures drivers and the paper/board skeletons, and its template is itself
a lab that ctest -R lab_new boots. tools/lab-new/README.md explains the
tokens and how a kind is added.
See demo/Sandbox.qml — a damped oscillator with a noisy measurement, used
as an excuse to put every instrument on one page, and the fastest way to see
whether a change to the palette or the scale has broken anything. Render it at
two scales in two themes:
clayrender plugins/clay_lab/demo/Sandbox.qml --out shot.png --size 1400x900 \
--eval 'LabTheme.mode = "dark"' --eval 'LabTheme.uiScale = 1.6' --frames 300
labs/electronics-101/ and labs/hydraulics-101/ are full build labs on the board layer.
API Reference
BarFace
A level bar on an InstrumentScale - horizontal or vertical, with peak-hold
View full documentation
Properties
| Name | Type | Description |
frameRadius | real | Corner radius of the frame - 0 when baked into a Texture |
horizontal readonly | bool | True while the bar runs left to right |
label | string | Caption above the bar; empty hides the row |
orientation | int | Qt.Horizontal (default) or Qt.Vertical |
scale | InstrumentScale | Measurement model this face draws |
segments | int | Split the fill into this many cells - 0 (default) is a continuous bar |
showFrame | bool | Draw the panel background and border behind the face |
showPeak | bool | Mark the held peak. Follows scale.peakHold by default |
showTicks | bool | Draw the scale's gradations along the bar |
showValue | bool | Print the reading beside the label |
showZones | bool | Tint the severity bands into the track |
thickness | real | Across-the-bar size of the track |
Board
Store behind a build-type lab: typed parts on a grid, wires between their pads
View full documentation
Properties
| Name | Type | Description |
cell | real | |
cell \brief World units per cell. | real | |
cols | int | |
cols \brief Cells across. | int | |
cursorHeight | real | |
cursorHeight \brief The y \l cursorW is reported at. | real | |
cursorW | var | |
cursorW \brief The cursor on the board, in world units. | var | |
eraser | bool | |
eraser \brief The eraser tool is on. | bool | |
hoverHit | var | |
hoverHit \brief What is under the cursor, as \l hitAt reports it. | var | |
lane | real | |
lane \brief The raster a router turns on; half a cell by default. | real | |
nextId | int | |
nextId \brief The next id handed out, shared by parts and wires. | int | |
padY | real | A pad in world space, turned with its part; y is padY |
parts | var | [{ id, type, col, row, rot, ...fields }] - col/row are fractional cells |
rev | int | Bumped by every mutation and every move; the dependency in-place edits need |
router | var | Optional: { all(links, obstacles, lane), one(a, b, lane) } choosing wire paths |
routes readonly | var | Every wire's drawn path, { wireId: [{x, z}, ...] }, from the router |
rows | int | |
rows \brief Cells deep. | int | |
selectedId | int | |
selectedId \brief The selected part, -1 for none. | int | |
spec | var | Domain's part types: { type: { terminals, half, actuator, keepOut, fields, rows, watch } } |
wires | var | |
wires \brief \c {[{ id, a: [partId, ti], b: [partId, ti] }]}. | var | |
wiringFrom | var | |
wiringFrom \brief \c while a wire is dangling. | var | |
Methods
| Method | Returns | Description |
actuatorHalf(string type) | var | |
addJunction(real col, real row) | int | |
addPart(string type, real col, real row) | int | |
addRotated(string type, real col, real row, int quarters) | int | |
addWire(var a, var b) | void | |
beginBatch() | void | |
bodyHalf(string type) | var | |
bounds(var which, real pad) | var | |
cellFree(real col, real row, int ignoreId, string type) | bool | |
cellX(real col) | real | |
cellZ(real row) | real | |
clear() | void | |
closestOnPath(var path, real x, real z) | var | |
colOf(real x) | real | |
endBatch() | void | |
hitAt(real wx, real wz) | var | |
jumpTargets(var labelOf, var groupOf, real y) | var | |
keepOut(string type) | int | |
keys(var grid, var overlay) | var | |
load(var s) | void | |
movePart(int id, real col, real row, bool snap) | void | |
nearestFreeCell(real col, real row, string type) | var | |
partAt(int id) | var | |
previewPath(var from, var to) | var | |
removePart(int id) | void | |
removeWire(int id) | void | |
rotatePart(int id) | void | |
rowOf(real z) | real | |
selectionKeys(var monitor, var grid) | var | |
setField(int id, string key, var value) | bool | |
specOf(string type) | var | |
splitWireAt(int wireId, real wx, real wz) | int | |
state() | var | |
terminalCount(string type) | int | |
terminalDir(int id, int ti) | var | |
terminalLocal(string type, int ti) | var | |
wireMid(var wire) | var | |
wirePath(var wire) | var | |
Signals
| Signal | Description |
changed(string kind) | |
cleared() | |
removed(int id) | |
BoardInput
Mouse on a Board: wire pads, select and drag parts, operate, erase, tap a wire
View full documentation
Properties
| Name | Type | Description |
board | Board | |
dragSlop | real | World units a press must travel before it is a drag |
flow | var | Lab's Flow. While one runs, it decides what the board answers to |
gated readonly | bool | A flow is running, so it - not the learner - says what is live |
grid | var | |
grid \brief A \l GridMode; Alt inverts it for one drag. Optional. | var | |
hands | var | |
hands \brief The \l InstrumentBelt; asked second. Optional. | var | |
hint readonly | string | Hint-bar line for the current state, translated |
hintKeys | var | LabLang keys the hint line is built from; a lab overrides the wording by key |
hoverActuator readonly | bool | Cursor is over the SELECTED part's actuator - the next click operates it |
hoverActuatorIdle readonly | bool | Over an operable part that is not the one being worked on - the next click selects it |
nav | var | |
nav \brief The \c OrbitInput3D; asked first about every press. | var | |
stage | var | |
stage \brief The \l LabStage3D, for \c worldAt. | var | |
view | var | |
view \brief The View3D the stage projects through. | var | |
Methods
| Method | Returns | Description |
cancelAll() | void | |
clickAt(real x, real y, int mods) | void | |
dragFrom(real x1, real y1, real x2, real y2, int mods) | void | |
granted(var hit) | bool | |
moveAt(real mx, real my, int mods, bool isDown) | void | |
pressAt(real mx, real my, int button, int mods) | void | |
releaseAt() | void | |
worldAt(real mx, real my) | var | |
Signals
| Signal | Description |
interacted() | |
operate(int id) | |
BoardOverlay
2D readings over a Board: value labels, wire readings, watch marks and pinned tags
View full documentation
Properties
| Name | Type | Description |
attributes readonly | var | |
attributes \readonly \brief The monitor's quantity keys. readonly | var | |
board | Board | |
camera | var | |
hidden | bool | |
hidden \brief Wire readings, marks and tags step aside (a teacher on the board). | bool | |
keepOut | var | A screen rectangle ({x, y, width, height}) nothing is drawn into, or null |
labelOf | var | |
labelOf \brief \c {(id) -> string}, the short name a watch mark carries. | var | |
labelY | real | |
labelY \brief World height the value labels and marks anchor at. | real | |
monitor | var | |
monitor \brief The \l WatchMonitor: quantities, colours, the watched set. | var | |
readingOf | var | |
readingOf \brief \c {(id, attr) -> string}, one reading, any attribute. | var | |
severityOf | var | |
severityOf \brief \c {(id, attr) -> "ok" | "warn"}. | var | |
showValues | bool | Labels on. Turning it on lights the first attribute; setting any attribute sets it |
solved | var | Bind the domain's solve result here so every reading re-reads when it changes |
tagY | real | |
tags | var | |
tags \brief \c - the pinned tags. | var | |
valueAttr | string | Attribute the value labels show; "" is off. Twin of showValues |
view | var | |
view \brief The View3D to project through. | var | |
wireAttr | string | |
wireAttr \brief The one attribute a wire reading rides on; the first quantity. | string | |
wireReadingOf | var | |
wireReadingOf \brief \c {(wire) -> string | null}; null draws nothing. | var | |
wireY | real | |
Methods
| Method | Returns | Description |
cycleValueAttr() | void | |
load(var s) | void | |
setTag(int id, string attr) | void | |
state() | var | |
BoardPalette
Build lab's top-left panel: presets, the parts to take, the tools - in foldable sections
View full documentation
Properties
| Name | Type | Description |
board | Board | |
catalog | var | [{ type, color }] - the parts on offer, in this order, each with its board colour |
columnWidth readonly | real | |
columnWidth \brief The content width. readonly | real | |
compact | bool | Two-across layout without hints; measured from the window height by default |
flow | var | |
flow \brief The flow the \l FlowChip offers. | var | |
grid | var | |
grid \brief The \l GridMode the grid button toggles. | var | |
hands | var | |
hands \brief The \l InstrumentBelt the placer lives on. | var | |
icon | Component | Drawn beside each part; gets type and ink set on it. Optional |
lab | var | |
lab \brief The sandbox root, for the \l ScenarioBar. | var | |
overlay | var | |
overlay \brief The \l BoardOverlay the values button cycles. | var | |
placer | var | |
placer \brief The \l PartPlacer. | var | |
sectionsOpen | var | Which sections are unfolded, { presets, parts, tools }; put it in viewState() |
tools | var | Which of the standard tools to show, in order: "eraser", "values", "grid", "clear" |
Methods
| Method | Returns | Description |
sectionOpen(string key) | bool | |
toggleSection(string key) | void | |
BoardWires3D
Every wire on a Board as one flat instanced line batch, plus the dangling preview
View full documentation
Properties
| Name | Type | Description |
board | Board | |
clock | var | |
clock \brief The \l SimClock the flow animation runs on - deterministic, never a wall clock. | var | |
flowSteps readonly | int | |
flowSteps \brief How many chevron speeds there are. readonly | int | |
lineOf | var | (wire, points, hovered) -> { color, width, styleId, flow } |
lines readonly | var | What the batch draws; rebuilt on every board change, hover, eraser flip and solved |
previewColor | color | |
previewColor \brief The dangling wire's ink. | color | |
previewWidth | real | |
solved | var | Bind the domain's solve result here; the lines are rebuilt whenever it changes |
y | real | |
y \brief Height above the board the wires are drawn at (the stage's overlay budget). | real | |
Methods
| Method | Returns | Description |
flowStyle(real rel) | int | |
midOf(var wire) | vector3d | |
pathOf(var wire) | var | |
BudgetBar
Shows how one total splits into its parts
View full documentation
Properties
| Name | Type | Description |
decimals | int | Digits shown per value. Defaults to 2 |
segments | var | Shares, as [{label, value, color}] |
total | real | Whole the shares are measured against |
unit | string | Unit appended to every value in the legend |
CameraAnchorMark
Shows where the camera turns and zooms: a dotted ring on the anchor
View full documentation
Properties
| Name | Type | Description |
pointer | var | OrbitInput3D to watch |
radiusPx | real | Ring radius in pixels |
tone | color | Ring and dot colour |
CameraDirector
Which shot when, for a lab with a presenter in it
View full documentation
Properties
| Name | Type | Description |
cutMs | int | How long a change of shot glides. -1 takes the rig's travelMs |
followSlack | real | Handed to the rig's follow while a journey runs |
girth | real | Half-width of the presenter's frame box, as a factor of standHeight. Arms out, a hoverboard, a bit of ground |
headroom | real | How much taller than the presenter its frame box is, as a factor of standHeight. Room for a speech bubble |
pad | real | Air around a wide shot and a two-shot, as a factor on the fitted distance. 1 is edge to edge |
portraitLimits | var | Rig limits a portrait is allowed to relax: {minPitch, minHeight} |
portraitPitch | real | Angle a portrait is taken from: level with the face |
presenter | var | Who is in the picture. Duck-typed: stand (a vector3d on the ground), standHeight, travelling and present |
rig | var | OrbitCamera3D to direct. Null does nothing at all |
safe | var | What is NOT picture: {top, bottom, left, right}, fractions of the frame. The flow bar, the hint strip, the cards |
shot readonly | string | In force: "wide", "journey", "two", "portrait", "cutaway" or "" before the first one |
shotPoints readonly | var | World points the current shot was composed around. What a check reads back through rig.covers() |
title readonly | string | Scene title an establish is showing; "" otherwise |
widePitch | real | Angle a wide shot and a two-shot are taken from |
Methods
| Method | Returns | Description |
cutaway(var points, int holdMs) | bool | |
establish(var points, string title, int holdMs) | bool | |
journey(var to, var subject) | bool | |
portrait() | bool | |
presenterPoints(var at) | var | |
release() | void | |
twoShot(var subject) | bool | |
wide(var points, real pad) | bool | |
Signals
| Signal | Description |
cut(string shot) | |
CardFocusRing
Ring a card's control wears while the keyboard is on it
View full documentation
Properties
| Name | Type | Description |
host | Item | Where the ring actually lives - the first non-positioner ancestor unless a lab names one |
on | bool | This row is the one j/\c k landed on |
track | Item | Row to frame; defaults to where the ring was declared |
ColumnFace
A thermometer column on an InstrumentScale - every gradation labelled
View full documentation
Properties
| Name | Type | Description |
frameRadius | real | Corner radius of the frame - 0 when baked into a Texture |
label | string | Caption above the column; empty hides the row |
scale | InstrumentScale | Measurement model this face draws |
showBulb | bool | Draw the reservoir at the foot |
showFrame | bool | Draw the panel background and border behind the face |
showPeak | bool | Mark the held peak. Follows scale.peakHold by default |
showValue | bool | Print the reading above the column |
showZones | bool | Tint the severity bands into the column |
thickness | real | Width of the column |
Compass
Which way the work surface faces while you circle it
View full documentation
Properties
| Name | Type | Description |
aspect | real | Width/height of the surface shown |
frontColor | color | Marker on the surface's front edge |
yaw | real | Camera yaw in degrees (the rig's) |
DataRecorder
Records a run as a citable run record (or as plain CSV)
View full documentation
Properties
| Name | Type | Description |
command | string | That regenerates this record, written into it |
destination | string | Output path. A .csv suffix selects the flat CSV table; anything else (by convention .labrec) writes a run record |
error readonly | string | Why the last write failed ("" if it did not) |
format | string | "auto" (from the suffix), "record" or "csv" |
lab | string | Lab id written into the record; defaults to the destination's parent-of-records directory when it can be read off the path |
lastFile readonly | string | Path of the most recently written file ("" if none) |
maxBytes | int | Size above which the sample table is thinned instead of written in full (the record stays committable; summaries stay over all samples) |
probes | var | Probe names to record (empty = all registered probes) |
recordId | string | Id a paper cites this record by; defaults to the file's base name |
recording | bool | Toggle to start/stop; stopping writes the file |
rows readonly | int | Sample ticks recorded in the current/last run |
stepSize | real | Sim seconds per step, for the record's provenance |
steps | int | Fixed steps the run advanced, for the record's provenance (0 = unknown, e.g. a live dojo recording) |
Methods
| Method | Returns | Description |
record() | var | |
Signals
| Signal | Description |
written(string path, int rows) | |
DigitFace
Reading as mono digits - the numeric face of an InstrumentScale
View full documentation
Properties
| Name | Type | Description |
alignment | int | Qt.AlignLeft (default), Qt.AlignHCenter or Qt.AlignRight |
digitSize | real | Pixel size of the number |
frameRadius | real | Corner radius of the frame - 0 when baked into a Texture |
label | string | Caption above the digits; empty hides the row |
scale | InstrumentScale | Measurement model this face draws |
showFrame | bool | Draw the panel background and border behind the face |
showRange | bool | Print the scale in force under the digits |
showUnit | bool | Print the unit beside the number |
DockedInstrument
One instrument in an InstrumentDock - a titled panel the reader can put away
View full documentation
Properties
| Name | Type | Description |
accent | color | Title colour |
body readonly | Item | Panel's content area - bind a face's width to body.width |
content | Item | Stacked children (the default property) |
dismissable | bool | Offer the dismiss affordance. Turn it off for an instrument the lab considers essential |
dock readonly | InstrumentDock | This belongs to |
key | string | Stable name the dock and the lab's viewState() use |
label | string | Panel title, already translated |
Flow
A narrated walkthrough that drives the lab through its own mutation API
View full documentation
Properties
| Name | Type | Description |
camera | var | Lab's camera rig, for steps that carry a FlowStep::view |
control readonly | string | Who has the board right now: "learner", "flow" or "task" |
dwellTarget readonly | real | Sim seconds this step is estimated to need (0 while waiting) |
flowId | string | Stable id; prefixes narration keys and identifies the flow |
hintShown readonly | bool | |
index | int | Active step, -1 while the flow is not running |
lab | var | Sandbox root: source of flowActions() and of the checkpoints |
marks readonly | var | Active step's FlowStep::mark names; empty when idle |
narration readonly | string | Active step's text in the current language |
pacing | string | How a step ends: "ready" (default), "auto" or "manual" |
pending readonly | bool | Waiting on the learner or on the sim |
readyProgress readonly | real | 0..1 progress through the estimate; 1 means "read it, go on" |
readySince readonly | real | 0..1 progress through the estimate; 1 means "read it, go on" |
refusal | string | LabLang key of why the last touch did nothing; "" again after a moment |
ripe readonly | bool | Estimate has elapsed: Next is the obvious thing to do now |
running readonly | bool | |
step readonly | FlowStep | |
steps | list<FlowStep> | (default property) |
title readonly | string | |
titleKey | string | Dictionary key of the flow's title |
unresolvedVerbs readonly | var | Verb names a demo or a solve asked for and the lab does not have |
waiting readonly | bool | In a task: the learner must act |
Methods
| Method | Returns | Description |
applyView(var v) | void | |
check() | bool | |
goTo(int i) | void | |
grants(var id) | bool | |
next() | void | |
prev() | void | |
refuse() | void | |
replayStep() | void | |
run(var actions) | void | |
sayOf(QtObject step) | string | |
solve() | void | |
start() | void | |
stop() | void | |
Signals
| Signal | Description |
finished() | |
narrated(string text, string lang, string key) | |
FlowChip
Offer to be taught: starts a Flow, and says that it exists
View full documentation
Properties
| Name | Type | Description |
flow | var | Flow to offer |
label | string | Text; defaults to the flow's title |
FlowStep
One stop of a lab Flow: what is said, what the lab does, what the learner does
View full documentation
Properties
| Name | Type | Description |
demo | var | Actions the lab performs on entering, as [[verb, args...], ...] |
dwell | var | Sim seconds to linger, or "auto" to estimate from reading time |
expect | var | Optional predicate asserted after the step, for headless checks |
key | string | Stable step name; also the narration key suffix |
mark | var | Parts (or sub-parts) this step's line names, as a list of names |
say | string | Narration text, used when no dictionary entry exists for key |
task | var | What the learner must do: {until, allow, hint, hintAfter, solve} |
view | var | Where the camera should be for this step. Null leaves it alone |
watch | var | Wait for the simulation, as {until} |
Gauge
A needle dial that picks its own range - the instrument, not a number
View full documentation
Properties
| Name | Type | Description |
accent | color | Ring/symbol colour - use it to say what this is |
face | color | Dial face |
frameRadius | real | Corner radius of the face |
fullScale readonly | real | Selected range - the smallest one the reading fits in |
majorEvery | int | Every n-th tick is a long one |
needleColor | color | Needle, while the scale has no severity bands |
rangeText readonly | string | Selected range as a quantity, e.g. "10 mA" |
ranges | var | Full-scale values on offer, ascending |
scale | InstrumentScale | Measurement this face draws |
settleTime | int | Milliseconds the needle takes to swing to a new reading |
showFrame | bool | Draw the panel background and border |
showValue | bool | Print the reading under the dial as well |
sweep | real | Angular travel of the needle, in degrees |
symbol | string | Large glyph in the corner - what this instrument measures |
ticks | int | Tick marks across the sweep |
unit | string | SI unit of value, e.g. "A" |
value | real | Reading. Its magnitude drives the needle |
valueText readonly | string | Reading as a quantity |
GridMode
Snap-or-free placement, with grafli's grid contract
View full documentation
Properties
| Name | Type | Description |
snap | bool | Snapping to the raster |
step | real | Raster spacing in world units |
Methods
| Method | Returns | Description |
quantize(real v, int modifiers) | real | |
snapping(int modifiers) | bool | |
toggle() | void | |
HandheldInstrument
An instrument the viewer picks up, applies to the scene, and puts down
View full documentation
Properties
| Name | Type | Description |
clearOnPutAway | bool | Putting the instrument away ends the measurement. Off |
count readonly | int | How many picks the subject has |
empty readonly | bool | Nothing picked yet |
full readonly | bool | MaxPicks reached |
glyph | string | One character for the belt chip |
held | bool | Belt has this one in hand. Set by the belt, read by the visuals |
hint | string | One line describing what a click does with this in hand |
hovering | var | Where the cursor is right now, in the same shape as a pick |
label | string | Belt caption, already translated |
maxPicks | int | How many picks make a subject; 0 is unbounded |
name | string | Id-like name, language-neutral - what a pinned probe is named after |
pickKind | string | "point", "object" or "moment" |
picks readonly | var | Subject so far - points, objects or moments, per pickKind |
pinnable readonly | bool | There is a reading worth keeping |
pinnedReadings readonly | var | What has been pinned from this instrument: [{name, at, text}] |
tone | color | Instrument's ink |
unit | string | Unit of value, e.g. "m", "V", "s" |
value | real | Reading. Bind it |
valueText | string | Reading as text; defaults to value in unit |
view | var | View3D the picks came from - what the visuals project through |
Methods
| Method | Returns | Description |
add(var pick) | void | |
clear() | void | |
info() | var | |
pin(string probeName) | bool | |
sampler(var snapshot) | var | |
suggestedName() | string | |
undo() | void | |
Signals
| Signal | Description |
pinned(string probeName) | |
HintBar
Bottom-centre line that says what you can do right now
View full documentation
Properties
| Name | Type | Description |
flow | var | Hidden while this Flow runs |
leftGuard | Item | Panel on the left the bar may not grow into |
margin | int | Gap kept to the guards |
rightGuard | Item | Panel on the right the bar may not grow into (usually the monitor) |
text | string | Line to show; empty hides the bar |
Methods
| Method | Returns | Description |
flash(string message) | void | |
HintJump
Keyboard selection: label every target, type the label
View full documentation
Properties
| Name | Type | Description |
active readonly | bool | Labels are up and the keyboard is this component's |
camera | var | The View3D renders with (projection dependency) |
maxBadges | int | Off-screen entries shown before the strip says "+N" |
rig | var | An OrbitCamera3D; only focusOn is used, and only for off-screen targets (select-and-fly) |
targets | var | () => [{id, pos, name, group}] - what can be jumped to |
view | var | View3D to project through |
Methods
| Method | Returns | Description |
begin() | bool | |
cancel() | void | |
handleKey(var event) | bool | |
Signals
| Signal | Description |
selected(var target) | |
InstrumentBelt
What the viewer can pick up: the kernel's instruments in every lab, plus whatever the kit brought
View full documentation
Properties
| Name | Type | Description |
clickSlop | real | Pixels a press may travel and still count as a click |
defaults | bool | Carry the kernel's own instruments. On, and rarely off |
empty readonly | bool | Hand is empty |
held readonly | var | Instrument in hand, or null |
heldIndex | int | Which instrument is in hand; -1 for none |
instruments readonly | var | Everything on the belt: the kernel's, then the kit's, in order |
key | string | That cycles the belt, for hints |
pinKey | string | Key that keeps a reading |
pinning readonly | bool | Name prompt is open |
pointer | var | OrbitInput3D whose picks feed the hand |
rowMargin | real | How far above the bottom of the view the belt sits |
unit | string | This lab's world is in: "m", "mm", "u" .. |
view | var | View3D; defaults to the pointer's |
Methods
| Method | Returns | Description |
beginPin() | bool | |
cancelPin() | void | |
commitPin() | bool | |
cycle() | void | |
info() | var | |
move(real x, real y) | bool | |
press(real x, real y) | void | |
putAway() | void | |
release() | bool | |
take(int i) | void | |
takeNamed(string n) | void | |
InstrumentDock
A column of HUD instruments the reader can put away one at a time
View full documentation
Properties
| Name | Type | Description |
content | Item | Docked instruments (the default property) |
hidden | var | Keys currently put away, in the order they were dismissed |
isInstrumentDock readonly | bool | Marker a DockedInstrument finds its dock by |
itemWidth | real | Width every docked instrument takes |
revision readonly | int | Bumps whenever an instrument registers |
showTray | bool | Offer the put-away instruments back |
spacing | int | Gap between stacked instruments |
Methods
| Method | Returns | Description |
applyViewState(var s) | void | |
hide(string key) | void | |
isShown(string key) | bool | |
keys() | var | |
labelOf(string key) | string | |
show(string key) | void | |
showAll() | void | |
toggle(string key) | void | |
viewState() | var | |
Signals
| Signal | Description |
changed() | |
InstrumentScale
What a reading means - the measurement model every instrument face draws
View full documentation
Properties
| Name | Type | Description |
accent | color | Colour while the instrument has no opinion |
autoRange readonly | bool | True while ranges drives the limits |
damping | real | Movement time constant in seconds - the lag of a real meter |
digits | int | Decimals in the printed reading, or -1 for three significant figures |
displayValue readonly | real | Where the face actually points - the reading after lag and settling |
fraction readonly | real | Where the face points, 0 at lo and 1 at hi |
fullScale readonly | real | Alias of hi, in the language a bench meter uses |
graded readonly | bool | True while the instrument has bands to judge by |
hi readonly | real | Top of the scale in force - the selected range, when self-ranging |
lo readonly | real | Bottom of the scale in force |
logScale | bool | Position the reading by its logarithm - equal ratios, equal space |
max | real | Top of a fixed scale |
min | real | Bottom of a fixed scale |
okUntil | real | Top of the good band. Leave unset for an instrument with no opinion |
peakFall | real | How fast it then falls, in scale fractions per second |
peakFraction readonly | real | Where the peak marker sits, 0..1 |
peakHold | bool | Remember the highest reading and mark it |
peakHoldTime | real | Seconds the marker sits before falling |
probe | string | Name of a Probe to read instead of value |
rangeText readonly | string | Scale in force, e.g. "0 - 10 mA" |
ranges | var | Full-scale values on offer, for a self-ranging meter |
reading readonly | real | Value as it is read against the scale |
settleTime | int | Milliseconds a face takes to travel to a new reading |
severity readonly | string | "ok", "warn" or "alarm" |
severityColor readonly | color | Colour the reading has earned - accent while the instrument is ungraded |
source readonly | real | Raw reading, from probe or value |
symbol | string | What this instrument measures, as a glyph. Defaults to the unit |
tickCount | int | Labelled divisions a face should aim for |
ticks readonly | var | Gradations, [{value, fraction, major, text}] |
unit | string | SI unit of the reading, e.g. "V" |
value | real | Reading. Ignored while probe names one |
valueText readonly | string | Reading as a quantity, e.g. "50.0 mA" |
warnUntil | real | Top of the warned band |
zoneList readonly | var | Bands in force, [{from, to, severity}] |
zones | var | Explicit bands, [{from, to, severity}], overriding okUntil / warnUntil |
Methods
| Method | Returns | Description |
colorAt(real v) | color | |
colorFor(string severity) | color | |
fractionOf(real v) | real | |
reset() | void | |
valueAt(real f) | real | |
Lab
Global registry connecting parameters, probes and the sim clock
View full documentation
Properties
| Name | Type | Description |
clock | QtObject | Active SimClock (set automatically by SimClock) |
flowIds readonly | var | Ids of all registered flows (registration order) |
headless | bool | No one is watching: skip audio and character travel |
paramNames readonly | var | Names of all registered parameters (registration order) |
probeNames readonly | var | Names of all registered probes (registration order) |
scenario | string | Name of the currently applied scenario ("" if none) |
Methods
| Method | Returns | Description |
applyViewState(var s) | void | |
clearProbes() | void | |
labInfo() | var | |
p(string name) | real | |
parameter(string name) | Parameter | |
probe(string name) | Probe | |
probeSummary() | var | |
runFlow(string flowId, var opts) | var | |
set(string name, real value) | bool | |
viewState() | var | |
Signals
| Signal | Description |
sampled(real t) | |
LabBanner
Centred status pill: something is wrong, or something just happened
View full documentation
Properties
| Name | Type | Description |
active | bool | Show it |
alarm | bool | Fault colouring rather than a warning's gold |
blink | bool | Pulse - reserve it for a live fault |
fill | color | Override the severity colour outright |
guard | Item | A panel on either side the banner may not grow into |
maxWidth | real | Hard cap, whatever the guard allows |
text | string | Message (already translated) |
topMargin | real | Distance from the top edge |
LabHelp
Key map, on screen, generated from a LabKeys
View full documentation
Properties
| Name | Type | Description |
keymap | var | LabKeys to describe |
LabKeys
Canonical lab key map, and the help text that describes it
View full documentation
Properties
| Name | Type | Description |
camera | var | An OrbitCamera3D (or anything with orbitBy/zoomBy) |
entries readonly | var | Complete map as [{key, label}] - reserved keys included |
flow | var | Lab's Flow, if it has one |
frameAll | var | Called on 0; defaults to the lab's frameAll() |
frameSelection | var | Called on F; defaults to the lab's frameSelection() |
handKey | string | Letter that walks the belt |
handKeys readonly | bool | There is a belt to take instruments from |
hands | var | An InstrumentBelt - the keys that take, edit and keep a reading |
helpVisible | bool | Toggled by ?, rendered by LabHelp |
hints | var | A HintBar for refusals - a key that cannot act says why |
jump | var | A HintJump - keyboard selection on f |
keys | var | Lab's own keys: [{key, label, action, hidden}] |
lab | var | Sandbox root - supplies scenarios(), applyScenario() and the framing functions |
measureKeys readonly | bool | A measurement is being taken right now, so its editing keys are live |
navKeys readonly | bool | There is a pointer, so Space can lend it to the camera |
orbitStep | real | Degrees of yaw per Shift+Left / Shift+Right |
panStep | real | One arrow-key pan, as a fraction of the camera's distance |
pinKey | string | Letter that keeps a reading |
pointer | var | An OrbitInput3D, for the one key that touches the mouse |
recorder | var | A DataRecorder toggled by Shift+R |
scaleKeys | bool | Handle Ctrl+Plus / Ctrl+Minus / Ctrl+0 for LabTheme::uiScale |
scenarioNames | var | Names bound to 1..9; defaults to the lab's own scenario list |
selection | var | Selected part's card as a keyboard target |
selectionKeys readonly | bool | A card is selected, so its keys are live |
viewKeys | bool | Handle the arrow/zoom/frame keys |
Methods
| Method | Returns | Description |
handle(var event) | bool | |
handleRelease(var event) | bool | |
helpText() | string | |
releaseSprings() | void | |
Signals
| Signal | Description |
refused(string reason) | |
LabLang
Runtime language switch for labs: dictionaries, lookup and number format
View full documentation
Properties
| Name | Type | Description |
decimalPoint readonly | string | Decimal separator of the active language |
lang | string | Active language code, e.g. "en" or "de" |
languages | var | Language codes offered by the registered dictionaries |
Methods
| Method | Returns | Description |
langName(string code) | string | |
num(real v, int digits) | string | |
qty(real v, string unit, int digits) | string | |
qtyParts(real v, string unit, int digits) | var | |
register(var dict) | void | |
t(string key) | string | |
tf(string key, ...) | string | |
LabPanel
A titled paper panel - the surface every lab HUD is made of
View full documentation
Properties
| Name | Type | Description |
accent | color | Title colour |
body readonly | Item | Content area below the header - anchor to it for fixed-size panels |
content | Item | Stacked children (the default property) |
hideOnFocus | bool | Whether this panel steps out of the way in focus mode. True for everything but a flow's own overlay |
padding | int | Inset around the content |
spacing | int | Gap between stacked children |
tag | string | Key hint shown in the top-right corner, e.g. "M" |
title | string | Heading text (already translated) |
LabPrefs
Handful of settings that must outlive a reload, and where they go
View full documentation
Properties
| Name | Type | Description |
persistent readonly | bool | True when a backing store was found; false means memory only |
storeName | string | Database name. One store for all labs, so the settings are the person's rather than the lab's |
Methods
| Method | Returns | Description |
forget(string key) | void | |
get(string key, var fallback) | string | |
set(string key, var value) | void | |
LabStage3D
Ground every 3D lab stands on: an endless sheet of squared paper, plus the light rig and the environment that go with it
View full documentation
Properties
| Name | Type | Description |
cellSize | real | Spacing of the fine rules and of the snap cue, in world units |
cueColor | color | Snap cue: the peg role, one step further from the paper |
cueSize | real | Arm length of a snap cross / diameter of a free dot, in pixels |
cueWidth | real | Stroke weight of the snap cue, in pixels |
edgeColor | color | Work-area boundary |
edgeWidth | real | Work-area boundary weight, in pixels; 0 hides it |
environment | SceneEnvironment | The lab assigns to its View3D |
gridMode | var | GridMode whose mode the surface shows, or a plain bool |
ground readonly | Model | Pick plane itself, for a lab that has to compare hits |
horizonFar | real | Distance at which it has become the sky exactly |
horizonNear | real | Distance from the origin at which the surface starts dissolving |
keyBrightness | real | Strength of the shadow-casting key light |
lightsEnabled | bool | Set false for a lab that brings its own lighting |
majorColor | color | Heavy rules |
majorEvery | real | How many cells make one heavy rule. Defaults to 5, as paper does |
majorWidth | real | Heavy rule weight, in pixels |
minorColor | color | Fine rules |
minorWidth | real | Fine rule weight, in pixels |
overlayMaxBias readonly | int | Highest one worth using |
overlayMaxY readonly | real | Highest it may sit and still read as a marking ON the ground |
overlayMinBias readonly | int | Lowest sensible depthBias for a marking |
overlayMinY readonly | real | Lowest a flat overlay may sit and still clear the plane |
overlayStep readonly | real | Vertical gap between two overlays that must not fight each other |
rasterOrigin | vector2d | A world point that an intersection of the raster sits on |
shadowMapFar | real | How far the shadow volume reaches, measured from the camera |
sheetColor | color | Working surface |
skyColor | color | What the surface dissolves into - the environment's clear colour |
snapping readonly | bool | Mode the surface is showing |
tableColor | color | Outside the work area |
toneScale | real | What the light rig gives back on a flat, upward-facing surface |
workExtent | vector2d | Full width and depth of the area the lab actually works in |
workRadius readonly | real | Half the work area's diagonal |
Methods
| Method | Returns | Description |
overlayY(int layer) | real | |
worldAt(var view, real mx, real my) | var | |
LabTheme
Shared paper-and-ink design tokens for lab UIs, in light and dark
View full documentation
Properties
| Name | Type | Description |
ambient3d readonly | color | Ambient fill for a scene lit by a single key light |
dark readonly | bool | True while the dark palette is active |
fontAction readonly | int | Flow controls - things clicked by name |
fontBody readonly | int | Chips, readouts, buttons |
fontLabel readonly | int | Hand-font labels, notes, help rows |
fontLead readonly | int | Hint bar and a task's hint |
fontMicro readonly | int | Axis ticks, budget legend |
fontSmall readonly | int | Mono structure: panel titles, row labels, key caps |
fontTitle readonly | int | Narration, sized for the back row |
mode | string | Active palette: "light" (default) or "dark" |
modes readonly | var | Palette names on offer |
scaleLabel readonly | string | Scale as a percentage, e.g. "130%" |
shadowFactor readonly | int | How hard a cast shadow lands, as DirectionalLight expects it |
spaceL readonly | int | Panel padding, canvas insets |
spaceM readonly | int | Between chips in a row |
spaceS readonly | int | Between stacked rows |
spaceXl readonly | int | Panel-to-edge margin |
spaceXs readonly | int | Hairline gap |
spaceXxl readonly | int | Panel-to-panel margin |
uiScale | real | Multiplies every type size, spacing step and panel measurement |
Methods
| Method | Returns | Description |
contrast(color a, color b) | real | |
inkOn(color fill) | color | |
px(real v) | real | |
resetScale() | void | |
step(color c, real amount) | color | |
stepScale(int dir) | void | |
toggle() | void | |
LabView
Session-wide view state - currently, whether the HUD is out of the way
View full documentation
Properties
| Name | Type | Description |
focus | bool | Whether the lab is showing the scene and nothing else |
Methods
| Method | Returns | Description |
toggleFocus() | void | |
LangSwitch
A row of language chips driving LabLang::lang
View full documentation
Properties
| Name | Type | Description |
languages | var | Language codes to offer (defaults to every registered one) |
MarkLayer
Rings on the world points something is naming right now
View full documentation
Properties
| Name | Type | Description |
camera | var | |
camera \brief The camera the \l view renders with. | var | |
count readonly | int | |
count \readonly \brief How many marks are up. readonly | int | |
keepOut | var | A screen rectangle ({x, y, width, height}) no mark is drawn into, or null |
marks | var | What to ring: world points, each a vector3d or {at, label} |
pulseMs | int | Period of the halo that expands out of each ring; 0 draws none |
ringSize | real | |
ringSize \brief Ring diameter at rest, in px. | real | |
tone | color | |
tone \brief The ring colour. | color | |
view | var | View3D to project through |
Methods
| Method | Returns | Description |
clear(real sx, real sy) | bool | |
screenOf(int i) | var | |
MiniMap
Abstract view: a fitted 2D projection of the scene, drawn by the lab
View full documentation
Properties
| Name | Type | Description |
bounds | var | What to fit, as {x0, y0, x1, y1} - or {empty: true} |
contentPadding | real | Inset kept around the fitted content |
draw | var | (ctx, map) -> void: the lab's paint code |
emptyText | string | Drawn centred when bounds is empty |
map readonly | var | Live projection: {s, sx, sy, ox, oy, px(), py(), width, height, empty} |
revision | int | Bump to repaint. The counter every in-place model needs |
uniformScale | bool | Keep the aspect ratio, so the shape stays the shape you built |
Methods
| Method | Returns | Description |
repaint() | void | |
Narrator
Learner-facing surface of a Flow: what is being said and where we are
View full documentation
Properties
| Name | Type | Description |
flow | var | Flow to present |
showText | bool | Whether the panel carries the narration itself |
ParamPanel
Auto-generated slider panel for all registered parameters
View full documentation
Properties
| Name | Type | Description |
expanded | bool | Whether the slider rows are shown |
Parameter
A named, ranged lab parameter, auto-registered with Lab
View full documentation
Properties
| Name | Type | Description |
description | string | One-line explanation shown as tooltip/annotation |
from | real | Lower bound of the value range |
name | string | Unique name used by Lab, ParamPanel and agents |
stepSize | real | Slider step (0 = continuous) |
to | real | Upper bound of the value range |
unit | string | Display unit, e.g. "m/s²" |
value | real | Current value; bind your system to this |
PartCard
Selection card: what is selected, what it reads, what you can do to it
View full documentation
Properties
| Name | Type | Description |
adjust | var | (part, row, d) -> bool - h/\c l on a domain row; false refuses |
anchorDz | real | |
anchorDz \brief How far below the part's centre the card hangs, in world units. | real | |
anchorY | real | |
attributes readonly | var | |
attributes \readonly \brief What a tag may show: the monitor's quantity keys. readonly | var | |
board | Board | |
camera | var | |
camera \brief The camera the View3D renders with (projection dependency). | var | |
flow | var | Lab's Flow. While one runs, it says whether this card is live |
focusRow | int | |
focusRow \brief Which row \c j/\c k landed on. | int | |
focusedRow readonly | string | |
focusedRow \readonly readonly | string | |
hintOf | var | |
hintOf \brief \c {(part) -> string}, the last line. | var | |
keys readonly | QtObject | Adapter for LabKeys::selection |
live readonly | bool | Card's controls act right now |
minWidthOf | var | |
minWidthOf \brief \c {(part) -> real}, a floor for the card's width. | var | |
monitor | var | |
monitor \brief The \l WatchMonitor the watch row toggles. | var | |
operate | var | (part) -> bool - Enter on the card; false refuses |
overlay | var | |
overlay \brief The \l BoardOverlay the tag row pins into. | var | |
part readonly | var | Selected part, live; null when nothing is selected |
readingOf | var | |
readingOf \brief \c {(part) -> string}, the second line; "" hides it. | var | |
rows | alias | |
rows \brief The domain's rows, stacked between the reading and the watch row. | list<Item> | |
titleOf | var | |
titleOf \brief \c {(part) -> string}, the first line. | var | |
view | var | |
view \brief The View3D to project through. | var | |
watchable readonly | bool | |
watchable \readonly \brief The part takes a plot and a tag (spec \c watch). readonly | bool | |
Methods
| Method | Returns | Description |
rowsOf(var part) | var | |
PartPlacer
Palette's parts as ONE handheld tool that carries which part it is about to place
View full documentation
Properties
| Name | Type | Description |
board | Board | |
free readonly | bool | Spot is free - the one refusal a placement can meet, and the ghost says so first |
partType | string | |
partType \brief What the next click places. | string | |
spot readonly | var | Where the part would land, as board cells {col, row} - null off-board |
Signals
| Signal | Description |
placed(int id) | |
Plot2D
Live time-series plot of probe samples: lines, scatter, uncertainty bands
View full documentation
Properties
| Name | Type | Description |
cursorReadout | bool | Read every series back at the hovered sample |
placeholder | string | Text drawn centred while there is nothing to plot |
probes | var | Probe names to draw (empty = all registered probes) |
series | var | Explicit series as [{probe, label, color, style, sigmaProbe}], overriding probes |
seriesColors | var | Colors cycled through per series (LabTheme paper set) |
stripBandHeight readonly | real | Height of one strip's legend band (its title and latest values) |
stripMinChart | real | Smallest chart a stacked strip is allowed to shrink to |
strips | var | Stacked charts as [{label, series}], overriding series |
windowSeconds | real | Width of the sliding sim-time window |
Methods
| Method | Returns | Description |
heightForStrips(int n) | real | |
Signals
| Signal | Description |
seriesClicked(string probe) | |
Probe
A named observable sampled by the SimClock, auto-registered with Lab
View full documentation
Properties
| Name | Type | Description |
capacity | int | Maximum retained samples (ring buffer) |
expr | var | Function returning the current numeric value |
name | string | Unique name used by Lab, Plot2D and DataRecorder |
samples readonly | var | Retained samples as [{t, v}, ...] |
unit | string | Display unit, e.g. "J" |
value readonly | real | Most recent sampled value |
Methods
| Method | Returns | Description |
clear() | void | |
summary() | var | |
ReadoutPanel
A titled panel of live readout rows, built from data
View full documentation
Properties
| Name | Type | Description |
revision | int | Bump to re-read rows after mutating state in place |
rows | var | Readings, as [{swatch, label, value, valueColor, dim, bar, showSwatch}] |
ReadoutRow
One line of a readout: swatch, name, live value
View full documentation
Properties
| Name | Type | Description |
bar | real | 0..1 - draws a share bar under the row, or a negative to omit it |
dim | real | 0..1 - fades the swatch for a stale or inactive source |
label | string | What it is (already translated) |
showSwatch | bool | Draw the swatch (off for a plain stat line) |
swatch | color | Colour this thing wears elsewhere |
value | string | What it reads right now |
valueColor | color | Value colour; use alarm to raise it |
RecIndicator
Recording dot: this run is being written to a file
View full documentation
Properties
| Name | Type | Description |
recorder | var | DataRecorder to watch |
showRows | bool | Append the row count |
tone | color | Dot and text colour |
ScaleSwitch
Makes the whole lab larger or smaller: A-, the current percentage, A+
View full documentation
Properties
| Name | Type | Description |
showValue | bool | Show the percentage chip between the buttons |
Scenario
A named, scripted lab situation, used inside a ScenarioSet
View full documentation
Properties
| Name | Type | Description |
description | string | One-line explanation of the situation |
name | string | Name used with applyScenario()/the inspector reload action |
script | var | Function that sets up the situation imperatively |
ScenarioBar
Clickable preset chips, each carrying what it is worth noticing
View full documentation
Properties
| Name | Type | Description |
lab | var | Sandbox root (scenarios/applyScenario) |
namePrefix | string | Dictionary prefix for chip labels |
names | var | Presets to offer; defaults to the lab's |
notePrefix | string | Dictionary prefix for the active note |
showNote | bool | Show the active preset's one-liner |
ScenarioSet
Declarative collection of scenarios wiring the scenarios()/applyScenario() convention
View full documentation
Properties
| Name | Type | Description |
scenarios | list<Scenario> | (default property) |
Methods
| Method | Returns | Description |
apply(string name) | bool | |
names() | var | |
SceneTitle
A title card over the whole picture: fades in, holds, fades out
View full documentation
Properties
| Name | Type | Description |
at | real | Where the title sits, as a fraction of the height from the top |
fadeMs | int | |
fadeMs \brief Fade in and out, in ms. | int | |
text | string | |
text \brief The title; "" hides the card. | string | |
wash | real | How much the picture is calmed under the type, 0..1 |
SelectionFrame3D
Shared hover/select language: a flat frame on the work surface
View full documentation
Properties
| Name | Type | Description |
halfDepth | real | Half extent along z |
halfWidth | real | Half extent along x |
height | real | Height above the surface |
hovered | bool | Thin, quiet outline |
noseColor | color | Colour of the facing mark |
selected | bool | Full frame plus nose mark |
showNose | bool | Draw the facing mark when selected |
thickness | real | Bar thickness when selected |
tone | color | Frame colour (the interactive blue) |
SimClock
Seeded simulation clock driving deterministic probe sampling
View full documentation
Properties
| Name | Type | Description |
fixedStep | real | Length of a fixed simulation step; 0 disables stepped |
maxStepsPerAdvance | int | Fixed steps allowed per advance, so a hitch cannot become a freeze |
sampleInterval | real | Sim-time seconds between probe samples |
seed | int | Seed for the deterministic random generator; changing it resets the clock |
time readonly | real | Simulated seconds since the last reset |
timeScale | real | Slow-motion/fast-forward factor for live (frame-driven) mode |
world | var | Optional ClayWorld2d; when set, time advances with physics steps |
Methods
| Method | Returns | Description |
random() | real | |
randomGaussian() | real | |
randomRange(real from, real to) | real | |
reset() | void | |
Signals
| Signal | Description |
stepped(real dt) | |
wasReset() | |
Stopwatch
How long something took, in simulated seconds: click to start, click to stop
View full documentation
Properties
| Name | Type | Description |
running readonly | bool | Started and not yet stopped |
stopped readonly | bool | A completed timing is on the face |
TapeMeasure
Clicked points chained into a run, with the length of every leg, the angle at every corner and the total on screen
View full documentation
Properties
| Name | Type | Description |
angles readonly | var | Angle at each interior corner, in degrees |
arcPx | real | Radius of the angle arc, in pixels |
dotPx | real | Radius of a vertex dot, in pixels |
lengths readonly | var | Length of each leg |
plan readonly | var | Run projected into view pixels - what the overlay draws |
readout readonly | var | Whole measurement as data: segments, vertices and total |
total readonly | real | Total length of the run |
ThemeSwitch
A button that swaps the lab between the light and dark palette
TransportChip
Sim time, pause and speed - the clock, on screen
View full documentation
Properties
| Name | Type | Description |
clock | var | SimClock; the active one by default |
digits | int | Decimals on the time readout |
paused readonly | bool | Clock is standing still |
showSpeed | bool | Offer the speed cycle |
speeds | var | Speed rungs the button cycles |
Methods
| Method | Returns | Description |
cycleSpeed() | void | |
toggle() | void | |
WatchChip
Put this thing on the plot - the watch toggle, in three states
View full documentation
Properties
| Name | Type | Description |
full readonly | bool | No series left to give |
labels | var | Dictionary keys for the three states |
monitor | var | WatchMonitor that owns the watch set |
target | var | Id to watch |
watched readonly | bool | Target is on the plot |
WatchMark
Dot a watched object wears in the world, in its curve's colour
View full documentation
Properties
| Name | Type | Description |
label | string | Text beside the dot; empty draws the dot alone |
monitor | var | WatchMonitor supplying the colour |
onlyWhenWatched | bool | Hide unless the target is actually on the plot (the default) |
target | var | Watched id |
tone readonly | color | Target's series colour |
WatchMonitor
Watch a thing, get a probe, a colour and a curve
View full documentation
Properties
| Name | Type | Description |
canWatch | var | (id) -> bool: veto (a solder dot has no reading) |
idPrefix | string | Probe-name prefix; probes are <idPrefix><id> |
labelOf | var | (id) -> string: the legend/board label |
maxSeries | int | Beyond this the colours would repeat |
maxStrips | int | How many quantities may be stacked at once |
placeholder | string | Shown while nothing is watched |
plotHeight | real | Chart height - the budget the strips divide |
plotWidth | real | Chart width |
quantities | var | [{key, label, unit}] |
quantity | string | Active quantity key: what setWatched traces in, and what the chip row shows as chosen |
revision | int | Bump when labels change (ordinals, renames) |
stripModel readonly | var | What the plot is drawing: one entry per traced quantity, in quantities order, as [{key, label, series}] |
tracedQuantities readonly | var | Quantity keys that currently have a strip, in quantities order |
unitText readonly | string | Unit of the active quantity |
valueOf | var | (id, quantity) -> real: the current reading |
watched readonly | var | Ids, in plot order - which is also colour order |
windowSeconds | real | Plot window |
Methods
| Method | Returns | Description |
clear() | void | |
colorOf(var id) | color | |
isFull() | bool | |
isTracedIn(string quantity, var id) | bool | |
isWatched(var id) | bool | |
probeName(var id, string quantity) | string | |
prune(var stillExists) | void | |
setWatched(var id, bool on) | void | |
toggle(var id) | void | |
traceIn(string quantity, var id, bool on) | bool | |
traceOnly(var list) | void | |
traces() | var | |
unitOf(string quantity) | string | |
watchOnly(var ids) | void | |
Signals
| Signal | Description |
changed() | |
WorldLabel
A 2D chip pinned to a point in a 3D scene
View full documentation
Properties
| Name | Type | Description |
accent | color | Border colour - use it to carry meaning |
active | bool | Set false to hide without unloading |
camera | var | That View3D renders with |
contentItem readonly | var | Item children are parented to |
gap | real | Pixels between the point and the chip |
keepInView | bool | Keeps the chip inside its parent instead of letting it leave |
margin | real | Closest the chip may come to the window edge |
offset | point | Extra pixel nudge, applied after placement |
onScreen readonly | bool | Whether the anchor point is in front of the camera |
placement | int | See Placement |
text | string | Convenience one-line content |
view | var | View3D to project through |
worldPosition | vector3d | Scene point to pin to |