Represent tracing media as symbols and add pool thumbnails

This commit is contained in:
Olive Vaughn 2026-10-04 00:02:12 -04:00
parent 551d572347
commit 17e1b4f403
49 changed files with 1777 additions and 864 deletions

View file

@ -605,28 +605,26 @@ dense geometry. A topology setting cannot be treated as a per-frame gain curve.
The op list is the boundary with stage 7 in `docs/architecture.md`: the
rasteriser takes ops and knows nothing about nodes, channels or time.
**A photographic underlay is not an op.** The registered source frame that an
animator traces over is a reference, not output, and it may not enter the indexed
buffer — the same rule `docs/architecture.md` already sets for handles and
vertex boxes. It is a `drawImage` at an affine on a separate canvas, which clips
at the canvas edge for free, and the only thing it needs from the model is the
world transform of the node it rides:
**A tracing layer is an op that never reaches the raster.** Footage or a still
to draw over is a symbol with `:type :trace` and a `:media`, placed by an ordinary
instance — so it is moved, scaled, trimmed, held and put in a lane like anything
else — and it resolves to one `:trace` op: `{:kind :trace :node :layer :media
:frame :size :m}`. The raster refuses that kind, the player hands it to a
`drawImage` on a separate canvas over the picture, and `clip/resolver` makes one
only when asked with `:tracing?`, which only the stage does. An export, a
symbol's centre and a thumbnail never ask, so a reference cannot reach the
picture by any path that forgets to filter it. See `docs/tracing-symbol-plan.md`.
```clojure
(world-of resolver :head) ;; -> Float64Array[6]
```
A face's footage is one of these, placed as `:plate` under `:head` with the
anchor fit itself as its measured transform — the inverse of the head's, over
image height. Its world is `head · fit · 1/H`, so on a frame where the head and
the plate read the same measured frame the two cancel and the photo sits where
the face was filmed; on any other frame it rides the head. Registration is the
ordinary walk, not a matrix built beside it.
Composed with image-pixels-to-local — **both axes divided by `imgH`**, never by
their own dimension — the photo is registered with the shapes by construction,
and an unregistered underlay is merely decorative. The tracing editor chooses
which source frame to show under a cel. That reference choice is independent of
the finished picture fps and does not change the dense analysis track. A cel can
therefore use any useful source frame as its drawing reference, even when that
frame is not one of the displayed picture poses.
A photo that has to sit *between* two drawn layers is the case that would make it
a `:bitmap` node with an op of its own. Nothing wants that yet: a reference is
either under everything or over everything at low alpha.
Which frame it shows is the placement's: `:time {:holds [...]}` holds it on
chosen frames, and a head with `:reads {:holds-of :plate}` jumps to the same
ones. Whether it is showing at all is the editor's, `[:ui :tracing]`.
### Making it fast in CLJS
@ -680,8 +678,8 @@ Proof that it covers what exists, not just what is wanted:
| brow ring + quantised raise | node `:brow-r`, `[:geom :pts]` dense (the traced ring with height removed), `[:xform :pos]` dense (the quantised raise). **The decomposition design.md insists on is two channels.** |
| head plate, kept frames | node `:head`, `:symbol` per instance, keys on `[:symbol]` at kept frames |
| `makeXform` face-oval crop | **gone.** Placement is `[:xform :*]` on the face's own `:place`; the stage clips |
| `stabilize` transforms | dense `[:xform :*]` on `:head`, read through its optional `:anchors` map |
| registered underlay | not data — a UI layer riding `(world-of resolver :head)` |
| `stabilize` transforms | dense `[:xform :*]` on `:head` (the inverse fit) and on its `:plate` (the fit), read where `:reads` and `:time :holds` say |
| registered underlay | the face's `:plate`, an instance of the footage's tracing symbol under `:head`; a `:trace` op the raster never sees |
| painted background cel | node per layer, `[:geom :pts]` **framed**, `[:style :color]` framed |
| `mouth lead` | `:time {:offset k}` on performance nodes only |
| `exposure` | `:time {:expose n}` on the clip root, inherited |