485 lines
28 KiB
Markdown
485 lines
28 KiB
Markdown
|
|
# The Lane Model
|
|||
|
|
|
|||
|
|
Revised 2026-09-30. Target design. Occurrence ownership, source playback, the
|
|||
|
|
first exposure commands and a one-row cel strip are implemented; correction
|
|||
|
|
layers, the other commands and the remaining views are not. See the status note
|
|||
|
|
under [Proof obligations](#proof-obligations-and-implementation-order).
|
|||
|
|
|
|||
|
|
This revises the Claude artifact [The Lane Model](https://claude.ai/code/artifact/cd42981d-ed08-493f-94df-b7dd6657f0e6).
|
|||
|
|
Its prose and diagram source were recovered from session
|
|||
|
|
`1c603f71-84eb-498e-aeaf-4c0346f1f513`; the live artifact was not accessible for
|
|||
|
|
reading or editing here. This repository document is the revised design. The
|
|||
|
|
original artifact has not been updated, and edits made there outside the recorded
|
|||
|
|
session may not be represented here.
|
|||
|
|
|
|||
|
|
For the subjects covered here, this document supersedes the original artifact
|
|||
|
|
and conflicting proposals in `animation-model.md`, `timing-model.md`, and
|
|||
|
|
`architecture.md`. Those documents retain useful detail about the existing system.
|
|||
|
|
|
|||
|
|
## Goal and compatibility policy
|
|||
|
|
|
|||
|
|
Arthur is one animation document with several ways to see and edit it: drawing
|
|||
|
|
on the stage, arranging clips, timing exposures, editing curves, and generating
|
|||
|
|
motion from footage. Each view exposes relevant facts and invokes shared editing
|
|||
|
|
operations. Switching views must preserve the meaning of the work.
|
|||
|
|
|
|||
|
|
The user explicitly requires no backward compatibility. Replace obsolete shapes,
|
|||
|
|
APIs, and tests when a better model requires it. Do not retain compatibility
|
|||
|
|
branches, adapters, or migrations solely to preserve the current document format.
|
|||
|
|
A format marker can reject unsupported files clearly; it does not promise to
|
|||
|
|
convert them. This policy does not authorize deleting existing user assets.
|
|||
|
|
|
|||
|
|
Simplicity means predictable composition, clear ownership, and few independent
|
|||
|
|
rules. Minimizing field count is secondary to representing independent choices.
|
|||
|
|
|
|||
|
|
## What stays
|
|||
|
|
|
|||
|
|
- A symbol is the one container for authored scene nodes. A drawing can be a
|
|||
|
|
one-frame symbol; an animation uses the same container over more frames.
|
|||
|
|
- Nodes have stable identities and flat parent references. Shared content is
|
|||
|
|
referenced rather than copied implicitly.
|
|||
|
|
- Animatable properties are addressed by channel paths. Generated and authored
|
|||
|
|
values participate in the same evaluation machinery.
|
|||
|
|
- Authored data, generated blocks, and source media remain separate. Documents
|
|||
|
|
reference immutable blocks; caches and resolver indexes remain derived.
|
|||
|
|
- A pure reference evaluator specifies the result. Playback, seeking, preview,
|
|||
|
|
export, and optimized cursors must agree with it.
|
|||
|
|
- Existence, visibility, and missing measured data remain distinct facts.
|
|||
|
|
|
|||
|
|
## Content, occurrences, lanes, and rows
|
|||
|
|
|
|||
|
|
These have different identities and responsibilities:
|
|||
|
|
|
|||
|
|
| Concept | Owns | Example |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| Content | Reusable nodes and their animation | Drawing `a2`, an animated head, or a sound asset |
|
|||
|
|
| Occurrence | One use of content, its interval, source playback, and local treatment | `a2` exposed on frames 12–16 |
|
|||
|
|
| Lane | A sequence of occurrences and shared properties | The girl's drawings and the girl's overall transform |
|
|||
|
|
| View row or column | Presentation and editor state | Timeline row, exposure-sheet column, or property curve |
|
|||
|
|
|
|||
|
|
Use the existing instance/node identity mechanism for occurrences. An exposure
|
|||
|
|
should not acquire a second identity system just because it is shown as a cel.
|
|||
|
|
Lanes group occurrences; they do not introduce another node-holding content type.
|
|||
|
|
The concrete candidate below uses existing group and instance nodes; its ownership
|
|||
|
|
boundaries are part of the design. It is now the implemented shape, and the field
|
|||
|
|
spellings below are the ones the runtime reads.
|
|||
|
|
|
|||
|
|
Each occurrence has a stable ID. Moving it, changing its hold, swapping its source,
|
|||
|
|
or trimming it preserves that ID. Repeating it creates a new occurrence that may
|
|||
|
|
reference the same content. A split retains the original ID on the left and gives
|
|||
|
|
the right piece a new ID; commands return the resulting selection explicitly.
|
|||
|
|
|
|||
|
|
Occurrences are the canonical authored arrangement. A source-at-time channel or
|
|||
|
|
interval index may be compiled from them for evaluation, but is not a second
|
|||
|
|
editable copy of the schedule. This replaces the earlier proposal that every cel
|
|||
|
|
must be represented solely as a source key. Ordinary property animation still
|
|||
|
|
uses lightweight keys; it does not need occurrence objects.
|
|||
|
|
|
|||
|
|
A sequence lane has non-overlapping half-open occurrence intervals `[start,end)`
|
|||
|
|
in its own time space. Uncovered intervals are gaps. Empty lanes are valid.
|
|||
|
|
Compositing and simultaneous sounds are represented by multiple lanes or ordinary
|
|||
|
|
scene composition; an accidental overlap never silently selects a winner.
|
|||
|
|
Transitions, if added, need explicit overlap and mixing semantics.
|
|||
|
|
|
|||
|
|
Properties can belong to content, one occurrence, or the lane. For example:
|
|||
|
|
|
|||
|
|
- Rotate the reusable drawing: all its uses change.
|
|||
|
|
- Rotate one occurrence: only that exposure changes.
|
|||
|
|
- Animate the lane's rotation: whichever drawing is showing follows it.
|
|||
|
|
|
|||
|
|
An occurrence can have its own transform, gain, corrections, and source timing
|
|||
|
|
while remaining a block in the same timeline row. Independent treatment never
|
|||
|
|
requires a new row or an otherwise unnecessary wrapper symbol.
|
|||
|
|
|
|||
|
|
### Concrete candidate: a sequence group and ordinary instances
|
|||
|
|
|
|||
|
|
A lane is a group node with `:layout :sequence`. Its occurrences are ordinary
|
|||
|
|
instance nodes whose `:parent` points to the group. All remain in their symbol's
|
|||
|
|
flat node map. The sequence constraint is document semantics; which rows the UI
|
|||
|
|
expands remains editor state. Ordinary groups retain unconstrained composition.
|
|||
|
|
|
|||
|
|
This example is a document the runtime accepts, built and evaluated by
|
|||
|
|
`frontend/test/arthur/domain/lane_test.cljs`. Times here are zero-based. Channels
|
|||
|
|
use the existing representation; occurrence source references and playback have
|
|||
|
|
replaced the `[:source]` channel, which no longer exists.
|
|||
|
|
|
|||
|
|
```clojure
|
|||
|
|
;; Within :main's :nodes; referenced drawings/animations live in :symbols.
|
|||
|
|
{:girl
|
|||
|
|
{:id :girl :kind :group :layout :sequence :z "b"
|
|||
|
|
:channels {[:xform :rot]
|
|||
|
|
{:animated? true :interp :linear :keys {0 0, 6 30, 12 0}}}}
|
|||
|
|
|
|||
|
|
:exposure-a
|
|||
|
|
{:id :exposure-a :kind :instance :parent :girl :z "a"
|
|||
|
|
:time {:at 0 :rate 1} :span [0 4]
|
|||
|
|
:source {:symbol :drawing-a}
|
|||
|
|
:playback {:in 0 :speed 0 :end :stop}}
|
|||
|
|
|
|||
|
|
:exposure-b
|
|||
|
|
{:id :exposure-b :kind :instance :parent :girl :z "b"
|
|||
|
|
:time {:at 4 :rate 1} :span [0 4]
|
|||
|
|
:source {:symbol :drawing-b}
|
|||
|
|
:playback {:in 0 :speed 0 :end :stop}
|
|||
|
|
:channels {[:xform :pos]
|
|||
|
|
{:animated? true :interp :hold :keys {0 [0 0], 1 [2 0]}}}}
|
|||
|
|
|
|||
|
|
:animated-insert
|
|||
|
|
{:id :animated-insert :kind :instance :parent :girl :z "c"
|
|||
|
|
:time {:at 8 :rate 1} :span [0 4]
|
|||
|
|
:source {:symbol :wave}
|
|||
|
|
:playback {:in 3 :speed 1 :end :stop}}}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The lane's rotation reads lane time. Each occurrence's channels read occurrence
|
|||
|
|
time. Its content reads source time. The stills sample frame 0, while the insert
|
|||
|
|
samples source frames 3, 4, 5, and 6. The group transform composes with the
|
|||
|
|
occurrence transform and then the content's own transform.
|
|||
|
|
|
|||
|
|
`:span` remains in the node's own coordinates, consistent with ordinary nodes.
|
|||
|
|
The interval in lane time is derived through `:time`; do not also store parent
|
|||
|
|
start/end values. Sequence children require finite intervals and positive
|
|||
|
|
placement rates. Ordering and overlap checks use the mapped intervals, not `:z`.
|
|||
|
|
The sequence group may contain visual occurrences or audio occurrences; its
|
|||
|
|
capability must reject an incompatible mixture rather than infer it per frame.
|
|||
|
|
|
|||
|
|
A source reference is fixed within an occurrence. The lane changes content when
|
|||
|
|
another occurrence becomes active. This is a deliberate revision of the original
|
|||
|
|
diagnosis that making `:of` a channel was necessary to avoid vertical growth:
|
|||
|
|
multiple instances can occupy one row when the view presents their containing
|
|||
|
|
sequence. A lane-level source schedule is therefore derived, not authored twice.
|
|||
|
|
|
|||
|
|
## Source selection and source playback are independent
|
|||
|
|
|
|||
|
|
The current implementation makes framed sources play and keyed sources hold.
|
|||
|
|
Retire that rule. Channel storage shape must not determine playback behavior.
|
|||
|
|
Adding or removing a key must not turn a still into an animation or vice versa.
|
|||
|
|
|
|||
|
|
An occurrence names content and describes how its source time is sampled. In the
|
|||
|
|
basic case, after mapping lane time into occurrence time:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
source_time = in_point + speed × occurrence_time
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
A newly created occurrence starts at local time zero. Moving it preserves this
|
|||
|
|
origin relative to its content. Trimming can narrow its local support without
|
|||
|
|
resetting that origin; split pieces likewise preserve the source and property
|
|||
|
|
values at the cut. Trimming, slipping, and retiming are distinct operations with
|
|||
|
|
explicitly different effects on the interval and the source map.
|
|||
|
|
|
|||
|
|
| Intent | Source playback |
|
|||
|
|
| --- | --- |
|
|||
|
|
| Hold a drawing | Constant source frame, equivalently speed 0 |
|
|||
|
|
| Play an animated symbol | Advancing source time, normally speed 1 |
|
|||
|
|
| Cut between animations | Several occurrences, each with its own in-point and speed |
|
|||
|
|
| Mix stills and animation in a lane | Constant and advancing maps in the same sequence |
|
|||
|
|
|
|||
|
|
The source reference itself is discrete and never numerically interpolated.
|
|||
|
|
Interpolation belongs to properties that support it; a property registry should
|
|||
|
|
declare value types, defaults, and permitted interpolation and correction modes.
|
|||
|
|
Generic key toggles must consult those capabilities rather than assume every
|
|||
|
|
non-boolean value can be tweened.
|
|||
|
|
|
|||
|
|
Define source bounds and end behavior explicitly: stop contributing outside the
|
|||
|
|
source, hold an endpoint, or loop an explicit range. A still uses a valid constant
|
|||
|
|
frame. A loop uses a nonempty half-open range and a defined modulo rule. Playback
|
|||
|
|
never guesses these policies from whether a channel happens to have keys.
|
|||
|
|
|
|||
|
|
Audio shares occurrence arrangement, trimming, gain ownership, and clock mapping.
|
|||
|
|
It does not inherit visual frame-hold semantics: holding one audio sample is not
|
|||
|
|
an audio freeze effect. Validate supported playback policies by media capability.
|
|||
|
|
Actual audio scheduling must follow active occurrences, including gaps and cuts,
|
|||
|
|
rather than playing every sound reachable through a structural reference.
|
|||
|
|
|
|||
|
|
## Time spaces and sampling
|
|||
|
|
|
|||
|
|
Name the relevant space whenever an API accepts a time or range: project,
|
|||
|
|
symbol/lane, occurrence, or source. Store authored frame coordinates exactly;
|
|||
|
|
avoid cumulative rounding when moving through nested mappings. Quantize at a
|
|||
|
|
declared sampling boundary, not at every traversal step. Audio also needs its
|
|||
|
|
continuous clock/sample space rather than visual frame quantization.
|
|||
|
|
|
|||
|
|
A hold is an evaluable time map with no unique inverse. A loop can map many
|
|||
|
|
displayed occurrences to one source time. APIs must distinguish forward sampling
|
|||
|
|
from inverse editing, and expose enough context to resolve an occurrence or
|
|||
|
|
explicitly refuse an ambiguous operation. Do not report a missing time map merely
|
|||
|
|
because inversion is unavailable.
|
|||
|
|
|
|||
|
|
Separate invertible placement timing from source sampling. A zero source speed
|
|||
|
|
can mean hold without making the occurrence's own edit clock non-invertible.
|
|||
|
|
Reparenting through changing transforms or non-invertible timing must either
|
|||
|
|
preserve the full result by an explicit bake or return a reason it cannot; a
|
|||
|
|
matrix captured at one frame does not prove preservation across the animation.
|
|||
|
|
|
|||
|
|
The source's frame step, generated-pose sampling, and the lane's transform clock
|
|||
|
|
are independent scopes. Drawing on twos must not accidentally step a smooth lane
|
|||
|
|
transform. An explicit whole-subtree stepping operation can exist separately.
|
|||
|
|
|
|||
|
|
Share the quantization primitive where possible, but retain its units, phase,
|
|||
|
|
rounding policy, and order relative to retiming and lead. The original suggestion
|
|||
|
|
that exposure and picture-rate sampling are simply one floor is insufficient:
|
|||
|
|
noninteger grids and source-frame quantization require specified behavior.
|
|||
|
|
Identity timing can be implicit; remove `:time :mode` if it only duplicates that.
|
|||
|
|
|
|||
|
|
An occurrence interval is authored. Lane content extent is derived from its
|
|||
|
|
occurrences, including the explicit end of the last one. A separately authored
|
|||
|
|
container trim/window is legitimate when it intentionally gates children. Do not
|
|||
|
|
conflate that window with occupied extent or infer a final hold from the next key
|
|||
|
|
when no next key exists. A range of frame numbers alone cannot encode visibility
|
|||
|
|
or a missing measurement.
|
|||
|
|
|
|||
|
|
## Shared editing operations
|
|||
|
|
|
|||
|
|
Every view issues the same domain commands. A command accepts an explicit target
|
|||
|
|
and edit policy, computes a valid change, and returns the change, resulting
|
|||
|
|
selection, and any refusal reason. A button and a drag must not implement two
|
|||
|
|
versions of exposure extension.
|
|||
|
|
|
|||
|
|
An edit target identifies the symbol, occurrence path, selected entities or
|
|||
|
|
properties, and the time range with its space. Navigation also distinguishes
|
|||
|
|
editing shared content directly from editing it through a particular occurrence.
|
|||
|
|
Crossing a source cut must not silently redirect an active drawing edit to a
|
|||
|
|
different symbol: retain the explicit content target until navigation changes it.
|
|||
|
|
|
|||
|
|
Core commands include new drawing, reuse drawing, duplicate drawing, make unique,
|
|||
|
|
blank range, split, trim, move, extend exposure, slip source, retime, and apply a
|
|||
|
|
bounded property edit. Ripple/overwrite policy and the set of affected lanes are
|
|||
|
|
explicit command arguments. Preview consequences before committing a gesture.
|
|||
|
|
|
|||
|
|
New drawing creates fresh empty content and an occurrence. Blank range removes
|
|||
|
|
content coverage without inventing a hidden drawing. These are different actions.
|
|||
|
|
Reuse creates another occurrence pointing at existing content. Duplicate creates
|
|||
|
|
a new content identity. Make unique rebinds the selected occurrence only.
|
|||
|
|
|
|||
|
|
Copy semantics must specify nested sharing. A normal content copy duplicates its
|
|||
|
|
owned nodes and channels while preserving references to other reusable symbols.
|
|||
|
|
For a fully independent drawing assembled from nested symbols, provide an
|
|||
|
|
explicit deep-copy operation with ID remapping. Never promise decoupling while
|
|||
|
|
leaving the relevant edited object shared. Immutable media blocks may remain shared.
|
|||
|
|
|
|||
|
|
Commands are atomic undo transactions, even when they touch several leaves.
|
|||
|
|
Pointer movement and keyboard invocation use explicit begin/preview/commit or
|
|||
|
|
cancel boundaries; a timing heuristic alone must not decide user intent.
|
|||
|
|
Collaboration applies a transaction consistently, validates affected references,
|
|||
|
|
and detects conflicts at the owned data being changed. One leaf per channel does
|
|||
|
|
not solve simultaneous edits to different keys of that same channel; define a
|
|||
|
|
conflict policy rather than claiming that granularity solves all collaboration.
|
|||
|
|
|
|||
|
|
### Default timing behavior: exposure edits preserve lane keys
|
|||
|
|
|
|||
|
|
Working default from the follow-up discussion: extending a drawing's hold changes
|
|||
|
|
exposure timing, leaving lane animation at its authored times. The user raised
|
|||
|
|
keeping keyframes in place as a possibility; this is the proposed predictable
|
|||
|
|
default, not a claim that they selected every timing policy below.
|
|||
|
|
|
|||
|
|
Ownership supplies the remaining rule: properties attached to an occurrence
|
|||
|
|
travel with it. Extending its end does not stretch those properties; moving it
|
|||
|
|
changes where their existing local times land. No per-key attachment flag is
|
|||
|
|
needed to recover ownership that the document already expresses.
|
|||
|
|
|
|||
|
|
For the concrete example, extend `:exposure-a` by two lane frames with ripple:
|
|||
|
|
|
|||
|
|
| Fact | Before | After |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| Exposure A's lane interval | `[0,4)` | `[0,6)` |
|
|||
|
|
| Exposure B's lane interval | `[4,8)` | `[6,10)` |
|
|||
|
|
| Animated insert's lane interval | `[8,12)` | `[10,14)` |
|
|||
|
|
| Girl's rotation peak | Lane frame 6 | Lane frame 6 |
|
|||
|
|
| B's position change | B frame 1, lane frame 5 | B frame 1, lane frame 7 |
|
|||
|
|
| Insert's first source frame | Source frame 3 | Source frame 3 |
|
|||
|
|
|
|||
|
|
The rotation peak now coincides with a different point in the drawing sequence.
|
|||
|
|
That is the intended consequence of changing exposures underneath timed motion.
|
|||
|
|
The position correction stays attached to drawing B's occurrence. Neither the
|
|||
|
|
background's keys nor audio on another lane moves.
|
|||
|
|
|
|||
|
|
The command contract for this edit names the symbol and occurrence, a delta in
|
|||
|
|
lane frames, `:ripple` behavior, and an explicit scope of exposure timing. It
|
|||
|
|
extends A's local support by the delta converted through A's placement rate,
|
|||
|
|
and shifts subsequent occurrence placements by that delta in lane time. It does
|
|||
|
|
not modify any channel's key map, source in-point, or playback speed. Reject a
|
|||
|
|
nonpositive resulting duration. Validate and commit the entire change together.
|
|||
|
|
|
|||
|
|
The symbol's authored end is another explicit boundary: preview an overflow and
|
|||
|
|
offer to extend the symbol or cancel. A command can request that extension as
|
|||
|
|
part of its transaction; it must not silently truncate later occurrences or grow
|
|||
|
|
other uses of a shared symbol. In the example, a 12-frame symbol needs an explicit
|
|||
|
|
extension to 14 frames or the edit must be refused without partial changes.
|
|||
|
|
|
|||
|
|
Retime performance is a separate operation over explicitly selected occurrences
|
|||
|
|
and channels. It applies the same time transformation to their relevant clocks,
|
|||
|
|
keys, and correction supports. Stretching an interval requires a defined warp
|
|||
|
|
and interpolation behavior; it is not merely moving keys whose frame numbers
|
|||
|
|
happen to lie inside the selection. Until supported, refuse this operation
|
|||
|
|
rather than approximating it with an exposure ripple.
|
|||
|
|
|
|||
|
|
The initial UI should default stage transforms to the lane when drawing in a cel
|
|||
|
|
workflow, so movement usually remains independent of exposure timing. The
|
|||
|
|
inspector names the target: lane motion, this occurrence, or shared drawing.
|
|||
|
|
Changing that scope is explicit. It changes what the edit means, not just which
|
|||
|
|
panel happens to be open.
|
|||
|
|
|
|||
|
|
## Three-frame rotation and correction layers
|
|||
|
|
|
|||
|
|
A range says where an edit applies; it does not specify the motion. Offer distinct
|
|||
|
|
commands for a constant adjustment, a ramp, and a return-to-start motion. For UI
|
|||
|
|
frames 10–12, the internal range contains exactly three frame samples after
|
|||
|
|
conversion from the displayed numbering convention.
|
|||
|
|
|
|||
|
|
- Constant adjustment: the same offset throughout those three samples.
|
|||
|
|
- Ramp: interpolate from the specified start value to the target over the range.
|
|||
|
|
- Return motion: interpolate from the starting value to a peak and back.
|
|||
|
|
|
|||
|
|
For a return motion sampled on three frames, the values can be `0, angle, 0`.
|
|||
|
|
Outside the selected range, the underlying animation must evaluate exactly as it
|
|||
|
|
did before. A range-scoped correction layer expresses this directly; blindly
|
|||
|
|
inserting boundary keys can alter neighboring segments or destroy existing motion.
|
|||
|
|
|
|||
|
|
Implement corrections as an ordered stack over the base channel. Each correction
|
|||
|
|
has stable identity, explicit support interval, blend operation, and values in a
|
|||
|
|
named time space. Outside its support it is inactive. `replace` can supply a value
|
|||
|
|
over an absent base; `offset` cannot offset a nonexistent value. Blend capability
|
|||
|
|
depends on property type, and geometry corrections require compatible topology.
|
|||
|
|
|
|||
|
|
Regeneration replaces the generated base and preserves corrections. If changed
|
|||
|
|
topology or removed targets make a correction incompatible, report a resolvable
|
|||
|
|
conflict instead of silently dropping or misapplying it. Provenance explains
|
|||
|
|
where the base came from; explicit sampling policy determines its playback.
|
|||
|
|
|
|||
|
|
This is core to the workflow: generate motion, correct it by hand, adjust the
|
|||
|
|
generator, and keep the corrections. It should be proven before adding many views.
|
|||
|
|
|
|||
|
|
## Other unifications worth keeping
|
|||
|
|
|
|||
|
|
Pose choices, tracing-frame choices, and ordinary held values should share the
|
|||
|
|
channel evaluator and cursor infrastructure. Preserve their different ownership,
|
|||
|
|
fallback behavior, and sampling scope. A pose choice must address the relevant
|
|||
|
|
content/feature explicitly; switching to another symbol must not accidentally
|
|||
|
|
reuse a track just because both symbols contain a node with the same local name.
|
|||
|
|
|
|||
|
|
Keep the two animation idioms distinct: keyed geometry modifies one mark over
|
|||
|
|
time; drawing substitution selects content that may have different structure.
|
|||
|
|
Linear geometry interpolation requires compatible vertex correspondence, not
|
|||
|
|
merely two drawings that happen to look related.
|
|||
|
|
|
|||
|
|
Derived library grouping may collect drawings used by a single lane. This is a
|
|||
|
|
convenience, not ownership or deletion authority. Reference discovery for cycle
|
|||
|
|
validation, copying, and deletion examines all structural references, including
|
|||
|
|
currently inactive occurrences. Authored folders, favorites, and labels remain
|
|||
|
|
legitimate user data even when the UI could have suggested defaults.
|
|||
|
|
|
|||
|
|
## UX: location, selection, and controls
|
|||
|
|
|
|||
|
|
The breadcrumb sits above the timeline and states the editing location, shared
|
|||
|
|
content identity, and occurrence context when applicable. Show local time and
|
|||
|
|
its project context where a useful mapping exists. Holds and loops need an honest
|
|||
|
|
description instead of a fictitious unique global frame.
|
|||
|
|
|
|||
|
|
Creation controls next to the breadcrumb act in that explicit location. Selection
|
|||
|
|
does not secretly change where a new symbol goes. A shared drawing indicates its
|
|||
|
|
reuse and offers Make this occurrence unique. Names help identify content;
|
|||
|
|
linked-use indicators must rely on IDs, because different drawings can share names.
|
|||
|
|
|
|||
|
|
| Surface | Primary scope and controls |
|
|||
|
|
| --- | --- |
|
|||
|
|
| Topbar | Project name, save/open/export, project rate and stage size |
|
|||
|
|
| Location bar | Breadcrumb, add lane/content, shared-content context |
|
|||
|
|
| Cel action strip | New drawing, duplicate drawing, hold longer/shorter, blank range |
|
|||
|
|
| Lane header | Lane selection, lock, mute/solo where applicable, onion settings, expansion |
|
|||
|
|
| Stage tools | Drawing and transform modes, active target and scope |
|
|||
|
|
| Inspector | Selected content/occurrence/lane properties and valid key controls |
|
|||
|
|
|
|||
|
|
Cel actions have visible contextual buttons, shortcuts, a context menu, and
|
|||
|
|
command-palette entries. These are different entrances to the same commands.
|
|||
|
|
Shortcut names from the original sketch (`N`, `D`, `H`, `B`, `K`) are provisional;
|
|||
|
|
their meanings must match the visible labels and avoid tool conflicts.
|
|||
|
|
|
|||
|
|
The inspector normally edits values and the timeline normally edits timing, but
|
|||
|
|
this is an organizational default. Numeric duration and in-point controls are
|
|||
|
|
useful inspector edits to the same domain facts. Do not ban a convenient control
|
|||
|
|
just to preserve a visual division.
|
|||
|
|
|
|||
|
|
Default nesting navigation enters content; expanding a lane reveals properties.
|
|||
|
|
Other views may show hierarchies differently without changing the document.
|
|||
|
|
Tabs can pin explicit locations. Zoom, expansion, onion preferences, and current
|
|||
|
|
selection are editor state rather than animation content. Persistent workspace
|
|||
|
|
preferences can be saved separately.
|
|||
|
|
|
|||
|
|
## A session, revised
|
|||
|
|
|
|||
|
|
1. In `main`, create a girl lane and a new drawing. Draw; use New drawing (`N`)
|
|||
|
|
to create the next one with the previous exposure ghosted behind it.
|
|||
|
|
2. Use Duplicate drawing (`D`) when the current shapes are the starting point.
|
|||
|
|
Use Reuse drawing for a deliberately linked exposure. The UI shows the
|
|||
|
|
difference before an edit can change other uses.
|
|||
|
|
3. Time the performance. Hold longer (`H`) extends the selected occurrence and
|
|||
|
|
ripples later occurrences in the explicitly targeted lane. A trim gesture
|
|||
|
|
can use overwrite instead. The preview shows which boundaries will move.
|
|||
|
|
4. Choose a two-frame default exposure for newly created drawings, or run a
|
|||
|
|
separate Retime exposures command on a selected range. This does not quantize
|
|||
|
|
lane transforms or silently retime already authored exposures.
|
|||
|
|
5. Place the background in a lane below. Its source holds one frame throughout
|
|||
|
|
its occurrence. Key the lane's X position at the beginning and end and choose
|
|||
|
|
linear interpolation. The background slides while the girl's drawings cut.
|
|||
|
|
6. Select three frames on the girl's lane, choose Return motion, and rotate to
|
|||
|
|
the desired peak. A bounded rotation correction affects the girl across any
|
|||
|
|
drawing boundaries in that range. Existing motion survives outside it.
|
|||
|
|
7. Insert a playing animated symbol among the girl's held drawings. Set that
|
|||
|
|
occurrence's source playback to advance. No lane conversion is required.
|
|||
|
|
|
|||
|
|
The timeline shows named exposure blocks with property marks and optional curve
|
|||
|
|
subrows. The exposure sheet shows the same occurrences by frame and lane. The
|
|||
|
|
graph editor edits the same properties; the stage resolves the same document.
|
|||
|
|
Onion skin is configurable and counts neighboring exposure events, skipping gaps
|
|||
|
|
by default; a long hold does not consume the budget. Repeated uses of the same
|
|||
|
|
drawing remain distinct events. Deduplicating identical ghosts is a display option.
|
|||
|
|
|
|||
|
|
## Proof obligations and implementation order
|
|||
|
|
|
|||
|
|
The source-channel prototype has been removed: an occurrence names one symbol
|
|||
|
|
and carries its own playback clock, and `node/problems` rejects the old
|
|||
|
|
`[:source]` channel. `arthur.domain.sequence` holds lane validation and the
|
|||
|
|
first three commands — add lane, append drawing, extend hold with explicit
|
|||
|
|
ripple and shot-length policy — each one history step. The timeline draws a
|
|||
|
|
lane's occurrences as cel blocks on the lane's own row.
|
|||
|
|
|
|||
|
|
Correction layers, the remaining commands (reuse, duplicate, make unique, blank,
|
|||
|
|
split, trim, slip, retime) and the exposure-sheet view are not implemented; a
|
|||
|
|
refusal is the current behavior where the model demands an explicit choice
|
|||
|
|
nobody has made yet. The suite stands at 392 tests and 5,525 assertions, with
|
|||
|
|
`frontend/test/browser/sequence.mjs` driving the editor through create, hold,
|
|||
|
|
overflow and undo. Rewrite tests that encode superseded behavior rather than
|
|||
|
|
preserving behavior to keep them green.
|
|||
|
|
|
|||
|
|
Build small adversarial documents and test their domain operations before
|
|||
|
|
expanding the interface:
|
|||
|
|
|
|||
|
|
| Scenario | Required invariant |
|
|||
|
|
| --- | --- |
|
|||
|
|
| Same drawing exposed twice, then one made unique | Linked edits affect both before copying and only the selected content after |
|
|||
|
|
| Holds, playing inserts, nonzero in-points, and gaps on one lane | Source behavior is independent of property key count and channel encoding |
|
|||
|
|
| Adjacent occurrences, final hold, split, trim, ripple, and overwrite | Exact boundaries, stable IDs, deterministic collision handling |
|
|||
|
|
| Extend a hold under lane keys and occurrence-local corrections | Lane key times remain fixed; later occurrence corrections travel with their owners; source playback origins survive |
|
|||
|
|
| Ripple beyond the symbol end | Explicit extent policy; refusal leaves the document unchanged; resizing and retiming undo together |
|
|||
|
|
| Girl on twos over a moving background | Drawing cadence does not quantize either lane's continuous properties |
|
|||
|
|
| Three-frame correction crossing a drawing boundary | Exact support, same result outside it, one undo step |
|
|||
|
|
| Nested retiming, holds, loops, and fractional sampling | Explicit time spaces; ambiguous inverse edits cannot silently choose a target |
|
|||
|
|
| Audio inside changing source occurrences | Only active intervals sound, with correct trim and source timing |
|
|||
|
|
| Regenerate with corrections and a topology change | Compatible edits survive; incompatible ones produce actionable conflicts |
|
|||
|
|
| Reference cycles and deletion of reused content | Inactive references are validated too; no dangling references |
|
|||
|
|
| Save/load and command undo/redo | Identity, source maps, corrections, and evaluation round-trip |
|
|||
|
|
| Timeline and exposure-sheet invocation of one command | Identical document changes and selection targets |
|
|||
|
|
| Random forward/backward seeks and export | Reference and optimized evaluation agree, including defaults and absence |
|
|||
|
|
| Concurrent commands on overlapping and disjoint targets | Transactions remain valid; conflicts are explicit and undo preserves others' work |
|
|||
|
|
|
|||
|
|
Implementation order: occurrence ownership and playback semantics; shared
|
|||
|
|
commands and validation; correction layers and time-addressing contracts; then
|
|||
|
|
breadcrumb, cel strip, and an exposure-sheet projection. Use those two temporal
|
|||
|
|
views plus direct stage editing to prove the model before broadening the UI.
|
|||
|
|
|
|||
|
|
A new presentation should not require duplicate animation state. A genuinely new
|
|||
|
|
authoring capability may require new domain data. The model is successful when
|
|||
|
|
such additions have a clear owner and compose with existing operations, not when
|
|||
|
|
it can claim that no future feature will ever need another field.
|