arthur/clips/migrations
Your Name ddef5c6bfd A node has a pivot
Rotation and scale are composed about `[:xform :pivot]`, a point in the
node's own coordinates:

    local = T(pos) · T(piv) · R · K · S · T(-piv)

Schema 7 deleted this field, on the argument that an anchor is a peg. The
algebra was right and the conclusion was not. The identity holds between a
pivot and a peg THAT ALREADY EXISTS; it says nothing about what a node
turns about when nobody has made one, and that default is what a person
meets. With no pivot in the composition, a turn about anything but the
node's own origin has to be paid for by solving `pos` per frame —
`gesture/about` — and that solution is an arc in the angle while `pos`
tweens along the chord. Right on the frame it is written, wrong on every
frame between two keys.

A drawing escaped it: `paint/centred` puts a shape's origin on the middle
of what it draws. A symbol instance cannot — its origin is its symbol's,
and a symbol is drawn on the stage, so its origin is the top-left corner
of the stage. Off the document this was reported on: a symbol's content
centred 161 px from its own origin, and one instance of it keyed rot 0→60
put the drawing where it was put on both keys and at (-88, 121) halfway
between, a stage and a half away. The advice on offer was "make a peg
first", for wanting to spin a drawing.

So a turn now writes `rot` and nothing else, always, and the pivot is held
exactly between two keys because the matrix is built about it on every
frame. The default, and the way back to it, are the parts the old anchor
was missing:

  - a node nobody has pivoted turns about the middle of what it draws,
    `pick/bounds-of` — the same bounds the selection box comes from
  - the first turn or scale writes that middle down, in the same edit,
    with the `pos` that holds the picture still (`gesture/with-pivot`)
  - `clip/place-symbol` stores the middle of what a symbol draws as the
    instance's pivot, so a drop spins in place from the start
  - ⌃/⌘-drag the cross on the stage to put the pivot anywhere, moving
    nothing — on any node now, not pegs alone
  - ⌖ beside the pivot row in the inspector puts it back on the middle of
    what the node draws NOW (`gesture/centred`)

A pivot is a CHOICE and does not follow the drawing: once it is the node's
own, adding a shape inside a symbol cannot re-aim a keyed spin of any
instance of it. `instance-test` has asserted both answers to that now, and
the stored one is right.

A peg stays a peg, for the three things a node's own pivot is not: a pivot
SHARED between nodes, a SECOND transform on one node, and a hand transform
over a measured one. `nest/repivot` is gone — a pivot inside the node's own
transform has nothing to correct in anybody else's `:pinv`, so the gesture
works on every node and is no longer refused on an animated one. A measured
node's pivot is authored like any other, so a traced mouth can be told
where to turn without a peg.

Schema 8, and the first version that converts rather than refusing: an
absent pivot reads as [0 0] and T(pos)·T(0)·M·T(-0) is T(pos)·M to the
bit, so every stored document composes to exactly the matrices it did and
the migration only restamps the version.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-06 14:33:35 -04:00
..
0001_initial.py Serve the document from a Django backend, split into three tiers 2026-09-28 01:11:41 -04:00
0002_remove_analysis_artifact_analysis_source_blocks.py Add video upload, extraction progress, and reusable analysis sources 2026-09-28 10:45:51 -04:00
0003_source_extraction.py Add video upload, extraction progress, and reusable analysis sources 2026-09-28 10:45:51 -04:00
0004_footage_video_alter_footageframe_index.py Measure the video, not a PNG per frame 2026-09-28 11:32:01 -04:00
0005_footage_stream_alter_footage_video.py Decode uploaded footage in order with WebCodecs 2026-09-28 14:18:09 -04:00
0006_project_schema_version.py Add project schema version 2026-09-28 21:03:18 -04:00
0007_symbols_not_timelines.py Symbols, not timelines; no symbol is special 2026-09-29 12:46:42 -04:00
0008_owners_editors_leaf_seq.py Projects live at URLs, have owners, and are edited together live 2026-09-29 22:04:03 -04:00
0009_revision_blocks.py Projects live at URLs, have owners, and are edited together live 2026-09-29 22:04:03 -04:00
0010_sounds.py sound stuff 2026-09-30 03:31:19 -04:00
0011_occurrence_schema.py An occurrence is a node, with a clock of its own 2026-09-30 15:20:23 -04:00
0012_sound_label.py Make the media pool a list you can actually read 2026-10-01 00:56:39 -04:00
0013_palette_track.py Model palette tracks as typed symbol lanes 2026-10-02 10:06:05 -04:00
0014_multiple_analyses.py Support multiple regeneratable footage analyses 2026-10-03 01:24:45 -04:00
0015_tracing_images.py Represent tracing media as symbols and add pool thumbnails 2026-10-04 00:02:12 -04:00
0016_an_anchor_is_a_peg.py An anchor is a peg 2026-10-05 19:12:16 -04:00
0017_a_node_has_a_pivot.py A node has a pivot 2026-10-06 14:33:35 -04:00
__init__.py Serve the document from a Django backend, split into three tiers 2026-09-28 01:11:41 -04:00