arthur/clips/migrations/0017_a_node_has_a_pivot.py

46 lines
2.1 KiB
Python
Raw Normal View History

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
"""Schema 8: a node has a pivot.
`[:xform :pivot]` is back in the transform, as the point rotation and scale are
composed about: `local = T(pos)·T(piv)·R·K·S·T(-piv)`. Schema 7 deleted it, on
the argument that an anchor is a peg — true as algebra, and not true as a feature.
A peg is a node, and a turn about a point that is not the turning node's own
origin still has to solve for a position to hold that point still; that solution
is an arc in the angle while a position channel tweens along the chord, so it is
right on the frame it is written and wrong on every frame between two keys. A
drawing escaped it, since its origin is the middle of what it draws. A symbol
instance could not: its origin is its symbol's, which is the top-left corner of
the stage, so one keyed turn of an instance swung its drawing round that corner
on an orbit the size of the stage.
CONVERTED, unlike 6 and 7, because adding this one is exact. A schema-7 node has
no pivot; an absent pivot reads as [0 0]; and T(pos)·T(0)·M·T(-0) is T(pos)·M to
the last bit of the mantissa. Every stored document therefore composes to exactly
the matrices it composed to before, dense tier-2 transforms included, so there is
nothing to guess at and no node a conversion could silently move. The version is
restamped and nothing else is touched.
What a converted document does NOT get is a pivot somebody chose: nodes placed
before this carry none, so they still turn about their own origin until the first
turn or scale writes one — `gesture/with-pivot`, from the middle of what the node
draws at that moment — or until the cross is dragged (ctrl/cmd-drag on the stage).
"""
from django.db import migrations, models
def forwards(apps, schema_editor):
apps.get_model("clips", "Project").objects.update(schema_version=8)
class Migration(migrations.Migration):
dependencies = [("clips", "0016_an_anchor_is_a_peg")]
operations = [
migrations.AlterField(
model_name="project",
name="schema_version",
field=models.PositiveIntegerField(default=8),
),
migrations.RunPython(forwards, migrations.RunPython.noop),
]