9. The visual layer

Shapes, motion and effects are phrases built on a few natives in std/look, exactly like behaviour. Looks live in ordinary packages and run in look context (spec/05): they observe the trace, draw, and can never change what happens.

9.1 Observers

Taught in 12. Looks

A when on a trace event is an observer. It runs once per occurrence of the event, on the presentation clock, and sees exported state as it was at that point of the trace: the state after that fact (spec/05). A timer (after) sees the state as of the last fact applied before its presentation time.

when (sim.Crashed n)
  look.Fade (card of n) to 0.3

9.2 Drawing

Taught in 12. Looks

drawing    = "draw" arg "as" word [ "for" arg ] [ block ] ;
attachment = word "of" arg ;     (* look context: a shape attached with draw … for *)
when (sim.Added [n: raft.Node])
  draw (look.Card for n) as card for n
    look.Fill card with (RoleColor n.raft.Role)

The typed binding [n: raft.Node] (spec/05 §5.6) gives n the fields of raft.

9.3 Motion

9.4 Layout

Node cards are placed by layout, never by coordinates in behaviour code. look.Layout "ring" (also "row", "grid"; any other kind is bad-argument) arranges every shape attached to a node under the name card, for nodes not removed, whose card is not deleted or pinned, in node id order. With N members and index i from 0:

After each activation in which membership or the layout kind changed the layout re-flows: new cards are placed at once, the others morph (§9.3). Other shapes are positioned relative to cards (look.Place … above, below, beside, §9.2). A card that is pinned (§9.2) stays out of the layout.

9.5 Colors and type

9.6 The presentation clock

The scene builder maps trace order onto screen time. Presentation times are whole milliseconds. Pace is an integer in thousandths, 250 to 4000 (1000 is 1×); scale(ms) = ms·1000/pace, rounded down. Every look duration (after, Tween, and the builder's constants below) is scaled by pace (decision 66); an after duration in microseconds becomes milliseconds by rounding down. Normative rules:

  1. Order is preserved. If fact A precedes fact B in the trace, A's animation starts no later than B's.
  2. Flights are readable. A message flight (from net.Sent to net.Arrived or net.Dropped) lasts at least scale(400ms) on screen; later facts shift to make room.
  3. Idle gaps are compressed. The simulated gap between two facts shows as gap × 8 milliseconds (the gap in simulated microseconds times 8 over 1000, rounded down), but at most scale(600ms).
  4. Tweens finish. A fact waits until the shapes it touches are free of tweens and morphs already running on them (effects and pending flights do not count). The shapes it touches are the live shapes attached under any name to any node of the fact (node, from, to, and both lists of net.Partitioned), plus the owner shapes of reactive blocks whose read-set meets the fact's changes. A group counts its members.
  5. Pace. A global pace factor (0.25× to 4×) scales everything.
  6. Proportional mode (a player toggle, --mode proportional) instead maps screen time linearly to simulated time, for lessons where timing is the point: tail latency, racing timeouts, clock skew. The slot of a fact is t · 30000 / End, End being the time of the last fact (0 if End is 0), then scaled by pace and never earlier than the previous slot; equal slots are allowed. Only rules 1 and 5 apply.

In ordered mode the first fact starts at 0; each later fact starts at least scale(30ms) after the previous one. The slot is the largest of: the previous slot plus the compressed gap, the end of the busy shapes of rule 4, the send slot plus scale(400ms) for an arrival or drop, and the previous slot plus scale(30ms).

An after inside an observer counts presentation time from the moment its observer starts. Timers interleave with facts by presentation time: a timer due no later than the next fact's slot runs first, seeing the state as of the last fact applied before it. After the last fact, timers due within scale(2s) of the last slot run; later ones are dropped. More than 100000 timer firings at one instant is run-limit. The scene's duration is the latest of the last slot, the last key and the last marker.

9.7 The default look

std/look/basic is used when a program uses no look. A program uses a look when std/look is in its package graph (spec/06 §6.6). Otherwise the observers of std/look/basic run before the program's own. Nodes are cards on a ring labelled with the node's name (sim.Name n), each card lists the node's exported state fields as name value rows that morph on sim.Changed, messages are dots that fly between cards and are labelled with their record type, dropped messages fade midway, crashed nodes grey out, sim.Broken flashes the whole scene border in theme.Critical, and lesson.Noted shows a caption. Look packages may use std/look/basic and add to it.