raea/check

A check written as a system over a composition's world: what raea check runs for a rule a controller holds.

View as Markdown

A check holds the work to one line. It is a system over each frame's world, the same world raea world prints: a caption, a footage cut, a sound cue and a 3D part are entities with components, so one check reads the same for any of them. It finds entities, measures, and calls found for each thing that breaks its line; it never prints. A rule in a project's controller names it by its path, with its values, and raea check runs it on every composition, on the frames it says it ranges over. A check that reads what only a render has (needs: "render") waits for one, and says so. raea's own checks are the same thing written in Rust, under the names text-hold, text-above, text-below, text-margin, loudness, still-max, text-read and floor.

TS
// controllers/rules.ts holds the rule:
//   { label: "Captions clear the button band", type: "rule", check: "../checks/caption.ts", values: { band: 0.8 } }

// checks/caption.ts
import { check, found, type Layer, type Text } from "raea/check";

export default check({ id: "caption", text: "a caption clears the platform's button band" }, (world, _frame, values) => {
  const band = Number(values.band ?? 0.8);
  for (const t of world.find({ with: ["Text", "Layer"] })) {
    if (world.component<Layer>(t, "Layer")!.medium !== "2d-type") continue;
    const box = world.component<Text>(t, "Text")!.box; // in frame fractions: left, top, right, bottom
    if (box && box[3] > band) found({ about: [t], says: `the caption reaches ${Math.round(box[3] * 100)}% down the frame` });
  }
});

A check keeps what it needs from frame to frame in its memo (empty on the first frame, and each rule's own, so two asks that run one check don't share it), and its after runs once past the last frame with that memo: for what only the whole run can say, such as how long a shot held, or that no frame had the part the check was about (a check that finds nothing to measure must say so, or it passes for the wrong reason). A finding from after names what it is about by name, since there is no world then. named(world, id, kinds) is how a check finds what its values name: a part's, shot's or layer's name or a model file's, as written or as its start, in any case (desk is desk-3 and Desk.glb), component:Orb for what the Orb component drew, file:props/orb.glb for that model. turned(q, v) turns a vector by a part's rotation, and inside(a, b) says how far two parts are in each other, in metres, from their own boxes turned as they are. A note's ask names its check the same way a rule does (raea notes); the skill carries worked checks in craft/checks.

TS
// checks/shot-length.ts (the skill's craft/checks has it in full): a shot held at least N seconds
import { check, found, type Shot } from "raea/check";

export default check(
  { id: "shot-length", text: "the shot holds long enough" },
  (world, frame, values, memo) => {
    memo.fps ??= (world.resource("Frame") as { fps: number }).fps;
    for (const s of world.find({ with: ["Shot"] })) {
      const shot = world.component<Shot>(s, "Shot")!;
      if (shot.name !== String(values.shot)) continue;
      memo.shot ??= { from: shot.from, to: shot.to ?? null, last: frame };
      memo.shot.last = frame;
    }
  },
  (values, memo) => {
    if (!memo.shot) return found({ says: `no shot is named ${values.shot}`, fix: "name the shot's Sequence as the ask does" });
    const end = memo.shot.to ?? memo.shot.last + 1;
    const secs = (end - memo.shot.from) / memo.fps;
    if (secs < Number(values.seconds)) found({ about: [String(values.shot)], frames: [memo.shot.from, end], says: `the shot ${values.shot} is ${secs.toFixed(1)} s, under ${values.seconds} s` });
  },
);
TS
import { check, declared, found, inside } from "raea/check"

raea/check exports 8 functions, 1 class, 38 types. Source: sdk/raea/src/check.ts.

Functions

check

A check: what it holds the work to, the system that finds where the work doesn't hold, and what it says once the frames are run.

TS
function check(meta: CheckMeta, run: CheckFn, after?: AfterFn): Check

declared

Each check's declaration, as raea check reads it before it reads any frame.

TS
function declared(checks: Check[]): CheckMeta[]

found

Says what the running check found. Only inside a check's system, as raea check runs it.

TS
function found(f: Found): void

inside

How far two parts are in each other, in metres: the least overlap of their own boxes, turned as they are, along the axes that could part them (0: apart, or a part with no box).

TS
function inside(a: Part, b: Part): number

named

Whether entity id is one of kinds (a list, or one string of them split at commas): a bare kind is a part's, shot's or layer's name or a model file's, as written or as its start ("desk" is desk-3 and Desk.glb), any case; component:Orb is what the Orb component drew, and file:props/orb.glb what that model file is. Until a component's taxonomy says what a thing is, an ask's check names it this way.

TS
function named(world: World, id: number, kinds: string | string[] | undefined): boolean

runChecks

Every planned check run on its frames' worlds: what they found, a run of frames finding the same thing as one finding over those frames, and each check that threw, with what it threw.

TS
function runChecks(checks: Check[], plan: Planned[], worlds: Facts[]): { findings: Finding[]; threw: { check: string; says: string; }[]; }

turned

v turned by a rotation q (x, y, z, w), as a part's rotation turns its own space into the world's.

TS
function turned(q: number[], v: number[]): number[]

worldOf

A frame's facts as a raea/ecs world: the same components, the frame's size and rate as the Frame resource, and what the render added as resources. An entity's references to others (a Covered's layer, a relation's to) are its id in this world.

TS
function worldOf(facts: Facts): World

Classes

World

A world of entities: rows in tables by kind.

MemberWhat it does
new World(store?: Store | undefined)A world on store: the engine's tables when it has them, else TypeScript's (createWorld picks for you).
.native: booleanwhether the tables are the engine's (native) or TypeScript's
.spawn(kind: string, cols?: Cols): numberA new entity of kind with its columns; its id.
.set(id: number, cols: Cols): voidColumns set on an entity.
.get(id: number): RowAn entity's row.
.despawn(id: number): voidAn entity taken out of the world, with its children.
.query(kind: string, where?: Where): number[]The ids of kind's entities whose columns pass every condition, filtered by the engine.
.column(kind: string, col: string): (number | null)[]One numeric column of kind, in id order (null where unset).
.count(kind: string): numberHow many entities of kind there are.
.rows(): Row[]Every row, by kind then id.
.add(components: Components, opts?: { parent?: number | undefined; }): numberA new entity with components ({Position: {x, y}, Ball: {}}), optionally a child of parent.
.insert(id: number, components: Components): voidComponents added to (or replacing those on) an entity.
.remove(id: number, ...names: string[]): voidComponents taken off an entity.
.components(id: number): ComponentsAn entity's components by name.
.component<T = Record<string, Value>>(id: number, name: string): T | undefinedOne component of an entity (undefined when it hasn't it).
.find(f: Find): number[]Entities with every with component and no without one, passing every condition ("Comp.field", or a field of the first with), in id order: Bevy's query in the engine.
.setParent(id: number, parent: number | null): voidAn entity's parent (despawning a parent despawns its children).
.parent(id: number): number | nullAn entity's parent (null when it has none).
.children(id: number): number[]An entity's children's ids.
.resource<T = unknown>(name: string, value?: T | undefined): T | nullA resource: one value for the world (set it with a value; null when unset).
.send(name: string, value?: unknown): voidAn event for the systems after this one, this frame (each frame starts with none).
.events<T = unknown>(name: string): T[]This frame's events of a name, in the order sent.
.scene(): unknown[]What draws: Mesh + Transform entities (world space, parents composed) and lights (<Entities world={w.at(frame)} /> hands this to the renderer). From the engine's tables natively; from the world's own store in a browser (raea-web), the same list as runtime/calls ecs.scene makes.
.handle: number | nullThe engine's id of this world's tables (for the renderer), or null under plain bun.
.system(fn: System): WorldA system run on every frame, after the ones before it.
.step(frame: number): voidRun the systems once, for frame.
.at(frame: number): WorldThe world after frames 0..frame-1 have run (a copy; this world as built is the state before frame 0).

Types

About

TS
type About = { name: string, key?: string, }

AfterFn

Run once after the last frame, with what the frames kept: for what only the whole run can say (how long a shot held, that no frame had the part an ask is about). A finding here has no frames unless it gives them.

TS
type AfterFn = (values: Values, memo: Memo) => void

Ask

TS
type Ask = { /** * the one thing asked */ text: string, /** * the medium the person's words name for it, in their word: "2D", "3D", "flat", "extruded", "real", * "modelled", "voxel" */ medium?: string, /** * what it is about: the part the pin is on (found in the world), or what a check would query */ about?…

Camera

TS
type Camera = { eye: [number, number, number], look: [number, number, number], /** * vertical field of view in degrees (a perspective camera) */ fov?: number, /** * the height it sees in metres (an orthographic camera) */ ortho?: number, near: number, far: number, /** * how its eye moves, metres a second, to t…

Check

FieldTypeWhat it is
run (required)CheckFnits system, run on each frame it ranges over
afterAfterFnrun once past its last frame
id (required)stringwhat raea check --only names, and its findings' check
text (required)stringthe line it holds the work to
needs"render""render": it reads what only a render has (Contrast, Picture, Mix, Onset, Covers); with none, it waits for one
levelLevelhow bad what it finds is, where a finding doesn't say (error when unset)
framesFrameswhere it runs ("all" when unset)

CheckFn

The system: a frame's world, the frame, the rule's values, and what this run has kept.

TS
type CheckFn = (world: World, frame: number, values: Values, memo: Memo) => void

CheckMeta

FieldTypeWhat it is
id (required)stringwhat raea check --only names, and its findings' check
text (required)stringthe line it holds the work to
needs"render""render": it reads what only a render has (Contrast, Picture, Mix, Onset, Covers); with none, it waits for one
levelLevelhow bad what it finds is, where a finding doesn't say (error when unset)
framesFrameswhere it runs ("all" when unset)

Clip

TS
type Clip = { source: string, hash?: string, time?: number, in: number, out?: number, at: number, words: Array<Word>, }

Contrast

TS
type Contrast = { ratio: number, }

Covered

TS
type Covered = { layer: number, share: number, }

Covers

TS
type Covers = Array<Covered>

Cue

TS
type Cue = { source: string, at: number, gain?: number, bus?: string, }

Facts

A frame's facts as raea world --json prints them.

FieldTypeWhat it is
frame (required)numberthe frame, from the composition's first
width (required)numberthe composition's width in pixels
height (required)numberthe composition's height in pixels
fps (required)numberthe composition's frames a second
entities (required){ id: number; parent?: number | null; components: Record<string, unknown> }[]each entity: its id, its parent's, and its components by name
resourcesRecord<string, unknown>what is the frame's or the piece's, not an entity's (Rendered, Picture, Mix, Waits)

Finding

TS
type Finding = { check: string, level: Level, about: Array<About>, /** * from its first frame to the frame after its last; None when it is about the whole piece */ frames: [number, number] | null, says: string, measure: Measure | null, fix: string | null, by: string, }

Found

What a check found: the entities it is about, what it says, and, where they aren't the check's or the frame's, its level and frames (its first, and the frame after its last); the number it turns on and the fix, when there are.

FieldTypeWhat it is
about(number | string)[]the entities it is about, by their ids in this frame's world, or by name (a check's after has no world)
says (required)stringwhat is wrong, in a line a person reads
levelLevelits level, where it isn't its rule's or its check's
frames[number, number]its first frame and the frame after its last (this frame alone when unset)
measureOmit<Measure, "limit"> & { limit?: number }the number it turns on: its name, value and unit, and the limit it broke
fixstringwhat to change, when there is one thing to change

Frames

The frames a check runs on: every frame; the first frame of each shot after the first, and the frame before it; every frame a line is said or sung on; once, on the first frame (for what is the piece's, as its mix); or these.

TS
type Frames = "all" | "cuts" | "lines" | "once" | number[]

Layer

TS
type Layer = { medium: Medium, /** * its place in the stack, 0 at the back */ order: number, opacity: number, /** * how it blends with what's under it (CSS mix-blend-mode), when not normally */ blend?: string, }

Level

TS
type Level = "error" | "warn" | "note"

Line

TS
type Line = { text: string, from: number, to?: number, words: Array<Word>, needs?: string, }

Measure

TS
type Measure = { name: string, value: number, unit: string, limit?: number, }

Medium

TS
type Medium = "2d-type" | "2d-graphic" | "3d" | "footage" | "page"

Memo

What one rule's run of a check keeps from frame to frame: empty on its first frame, its own (two asks that run one check each have theirs).

TS
type Memo = Record<string, any>

Mix

TS
type Mix = { lufs?: number, peak?: number, }

Note

TS
type Note = { words: Array<string>, from: string, round: number, pin?: Pin, redesign?: boolean, corrected?: Array<number>, asks: Array<Ask>, }

Ocr

TS
type Ocr = { words: string, share: number, }

Onset

TS
type Onset = { at?: number, late_ms?: number, }

Part

TS
type Part = { /** * what draws it: "mesh", "model", "text3d" ... */ kind: string, /** * its name in the scene (`<Mesh3d name>`), or what it draws */ name: string, /** * its taxonomy tags (a component's `meta`, DR11), none until it has one */ tags: Array<string>, /** * its world position, rotation (x, y, z, w…

Picture

TS
type Picture = { luma: number, motion?: number, frames?: number, }

Pin

TS
type Pin = { t?: number, x?: number, y?: number, part?: string, }

Planned

Which check runs on which frames with which values, as raea check plans it from a rule: the rule's level (none: the check's own) and where the rule was written.

FieldTypeWhat it is
check (required)numberthe check, by its place in the list run
frames (required)number[]the frames it runs on
values (required)Valuesthe rule's values, which the check's system is given
levelLevel | nullthe rule's level, which its findings take where they don't say their own
bystringwhere the rule was written, which its findings carry

Proof

TS
type Proof = { shot?: string, frames?: [number, number], sign: boolean, }

Rendered

TS
type Rendered = { out: string, manifest: string, record?: string, stale: boolean, }

Shot

TS
type Shot = { name?: string, from: number, /** * None: it runs to the end */ to?: number, }

Source

TS
type Source = { file?: string, line?: number, component?: string, /** * the layer key as the engine has it */ key?: string, }

Text

TS
type Text = { words: string, font?: string, /** * its letters' height in px of a frame 1080 on its short side (None: not laid out without a render) */ size?: number, /** * its box in the frame, each 0 to 1: x0, y0, x1, y1 (None: not laid out without a render) */ box?: [number, number, number, number], extrud…

Values

A rule's values for its check (a controller's values, an ask's).

TS
type Values = Record<string, number | string | boolean>

Waits

TS
type Waits = { [key in string]: string }

Word

TS
type Word = { text: string, start: number, end: number, }