A drawing's origin is the middle of what it draws
A shape keyed from the bottom left to the top centre with a 360° turn on the way
left the stage completely in the middle of the spin and came back. The keys were
right and every frame between them was wrong, which is the signature of a wrong
pivot and a full turn: 0° and 360° are the only two frames where a wrong pivot
cannot be seen at all.
`paint/new-shape` stored a stroke EXACTLY AS DRAWN, in the containing symbol's
coordinates, and wrote no `pos`. So a drawing's origin was the SYMBOL's origin —
on the stage, its top-left corner. `node/local!` turns and scales about the
node's own origin and nothing else, deliberately, since `[:xform :anchor]` was
deleted in 925c12f. A node whose origin is nowhere near its content therefore
turns about nowhere near its content: the reported shape orbited at a radius of
126 px on a 320x200 stage.
It could not be seen while a drag was the only way to turn something, because
`gesture/about` solves for the `pos` that holds the chosen pivot still and
`turn` wrote it alongside the rotation — exactly right on the frame of the drag.
But that solution is `p' = c + R(θ)(p − c)`, an ARC, and `pos` interpolates along
the CHORD. Right on a drag, right on a key, wrong on every frame between two.
So `paint/centred` splits a stroke into a ring about its own middle and the `pos`
that puts it back, and `new-shape` is the one place every drawing is born — the
pen, the brush, and each piece the eraser leaves. The pivot rule is unchanged,
the middle of what the node draws; for a drawing that point is now its ORIGIN, so
`gesture/at-origin?` holds, `turn` writes `rot` alone, `scale` writes `scale`
alone, and a keyed turn is right on every frame. `pos` goes back to being the
motion path it reads as. Hand-authored scenes were always written this way:
`demo/scene.edn`'s card is `[-44 -30 44 -30 44 30 -44 30]` with its place in
`pos`.
NOT the universal rule, and `a-face-part-scales-about-its-own-middle` is why. A
measured part's points and position are dense tier-2 geometry in the footage's
space and cannot be re-originated, so its pivot is not its origin and `about` is
the only thing that will hold it; the same is true of an instance, whose origin
IS its symbol's coordinate system. Both still drag correctly about their middle,
both are inexact if that drag is keyed, and for both a pivot that has to persist
or be keyed is a peg — which is a node, so its pivot is its own origin, so it
collapses again one level up.
`cut/erase` took EVERY leftover piece back through `world⁻¹`, the cut shape's own
coordinates, and handed the offcuts to `new-shape`, which gives them a fresh
identity transform. Those two spaces coincide only while a shape has `pos [0 0]`,
which was every shape, so erasing anything that had been moved already scattered
its offcuts, silently. The kept piece comes back through `world⁻¹` and the new
ones through `parent⁻¹`, the space a node's `pos` lives in.
And `::adjust-last` re-traces the same stroke from stage pixels, so it goes
through `paint/place-points` rather than writing symbol-space points into a node
that now has a position of its own.
No schema change: the same fields, better values. An existing document keeps
evaluating exactly as it does now.
`a-keyed-turn-holds-its-pivot-between-its-keys` checks all 31 frames of the
tween. Checking the keys is what let this through. 604 CLJS tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
2e021a13eb
commit
e7f5f82845
11 changed files with 344 additions and 104 deletions
|
|
@ -370,11 +370,40 @@ q = M⁻¹(c − p) the material point under c
|
|||
p' = c − M'·q = c − M'·M⁻¹(c − p)
|
||||
```
|
||||
|
||||
so a turn writes `pos` **and** `rot`. Nothing is cached, so nothing can go stale:
|
||||
the stored anchor was the centre of what the node drew, captured once at creation,
|
||||
while the box beside it was recomputed every render — so on anything edited since
|
||||
it was made, the cross and the box visibly disagreed and the pivot was wrong.
|
||||
A symbol with more than one node diverged on the first edit.
|
||||
so a turn about a point that is **not** the node's origin writes `pos` as well as
|
||||
`rot`. Nothing is cached, so nothing can go stale: the stored anchor was the
|
||||
centre of what the node drew, captured once at creation, while the box beside it
|
||||
was recomputed every render — so on anything edited since it was made, the cross
|
||||
and the box visibly disagreed and the pivot was wrong. A symbol with more than one
|
||||
node diverged on the first edit.
|
||||
|
||||
**And a drawing's origin is the middle of what it draws**, from the moment it is
|
||||
drawn — `paint/centred`, which splits a stroke into a ring about its own middle
|
||||
and the `pos` that puts it back. This is what keeps the paragraph above from being
|
||||
the whole story, because the `pos` that `about` solves for is an **arc** in the
|
||||
angle and `pos` interpolates along the **chord**:
|
||||
|
||||
| | pivot = origin | pivot ≠ origin |
|
||||
| --- | --- | --- |
|
||||
| one drag | right | right |
|
||||
| between two keys | right | **wrong**, by the sagitta of the arc |
|
||||
|
||||
A 360° turn is where that is unmissable and was first seen: 0° and 360° are the
|
||||
only two frames where a wrong pivot cannot be seen at all, so the keys looked
|
||||
right and every frame between them was wrong — a shape keyed bottom-left to
|
||||
top-centre with one full turn on the way left the stage completely in the middle
|
||||
of the spin, orbiting its origin at a radius of 126 px on a 320×200 stage, because
|
||||
a stroke used to be stored exactly as drawn and its origin was therefore the
|
||||
**symbol's** origin, the top-left corner of the stage.
|
||||
|
||||
With the origin on the content there is nothing to solve: `gesture/at-origin?`
|
||||
holds, `turn` writes `rot` alone, `scale` writes `scale` alone, and a keyed turn is
|
||||
right on every frame. `about` is then needed only where the pivot genuinely is not
|
||||
any node's origin — a multi-selection about its shared box, a measured part, or a
|
||||
drawing whose points have been edited away from their own middle — and in each of
|
||||
those a pivot that has to be **keyed** is a peg, below. Hand-authored scenes were
|
||||
always written this way: `demo/scene.edn`'s card is
|
||||
`[-44 -30 44 -30 44 30 -44 30]` with its place in `pos`.
|
||||
|
||||
**A pivot somebody chose is a peg** — an ordinary `:group` parent, `nest/peg`,
|
||||
with `:pinv` captured so nothing moves when it appears. Toon Boom's peg, Fusion's
|
||||
|
|
@ -383,7 +412,7 @@ three things a derived pivot cannot do:
|
|||
|
||||
| want | why a derived pivot cannot | what the peg does |
|
||||
| --- | --- | --- |
|
||||
| a pivot that persists — an arm turning about its shoulder | a gesture's pivot is the middle of the drawing and lives for one drag | the peg's `pos`, static, nowhere near the middle |
|
||||
| a pivot that persists — an arm turning about its shoulder | a gesture's pivot is the middle of the drawing and lives for one drag, and the drawing's own origin cannot be moved there without moving its points out from under everything that reads them | the peg's `pos`, static, nowhere near the middle |
|
||||
| a pivot that travels — a foot roll | an anchor could only be keyed against `pos`, interpolated in the same breath, the two obliged to agree frame for frame | the peg's `pos` is an ordinary channel, so key it |
|
||||
| a hand transform over a **measured** one | impossible: `local`'s translation is `pos − M·a`, and under a measured `M` writing `a` moves the thing it was meant to leave alone | the peg's channels are its own, so the hand transform composes outside the measurement, which stays regenerable |
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue