RUNE-1016: Story Path Stroke Trim Proposed
Summary
PathSprite exposes persistent animatable stroke-trim properties:
- create@route:
path: "M 120,640 C 360,120 920,120 1160,640"
stroke: "#38bdf8"
strokeWidth: 8
trimStart: 0
trimEnd: 0
trimOffset: 0
- set:
route.trimEnd: 1
with:
tween: 2s
easing: 0.16,1,0.3,1Defaults are 0, 1, and 0. An ordinary path remains trim-inactive and records the existing draw-only path/stroke commands. Explicitly authoring or setting any trim property permanently activates measured trimmed-stroke recording for that sprite instance, including after values return to defaults.
Semantics
- Start/end are normalized arc-length positions; offset is normalized turns.
- Non-finite values sanitize to zero. Start/end clamp to
[0,1]; offset wraps modulo one. - Equal start/end is empty; exact
0→1is full;1→0is empty. end < startis a forward seam-wrapped interval, matching Windows Composition.- Lottie differs by sorting start/end. A Lottie importer MUST translate that ordering before assigning Rune properties.
- Closed-path full trim restores the original closed contour. Partial trim is open and uses caps.
- V1 trims stroke only. Fill and clip remain complete.
Validation and Recording
Trim-active PathSprite MUST use a static, segment-bearing, single-contour path. V1 MUST reject:
- multiple contours;
- expression-derived/ViewBox path coordinates;
- dash combined with trim;
- malformed or over-capacity measured payloads.
ViewBox and dash rejection MUST surface as actionable Story validation diagnostics before native recording. The renderer records fill normally, then records stroke through CmdStrokeTrimmedPath. The aggregate reservation includes DATA_PATH v2 fixed bytes, path bytecode, measured segments, metric knots, the aligned fixed trim command, complete pending DATA_EXPRESSION, and renderer-specific Story reservation. Failure leaves recorder state unchanged.
Animation Lifecycle
trimStart, trimEnd, and trimOffset use ordinary Story numeric-property behavior. Model values become targets immediately, presentation values animate through the attached RuneNode, and later set actions retarget from current presentation. There is no temporary motion binding, generation callback, or precise-rest staticization. Detach/reattach and destroy need no special cleanup. Trim properties are not morph-interpolated in V1; a replacement PathSprite activates trim from its own authored properties.
V1 has no trace convenience action. Authors coordinate motion and trim with the same animation definition. Core sampling and trimming share source-aware metrics, so the follower and mathematical leading edge agree; round and square caps may extend visually beyond that point.
Bounds
Trim MUST NOT shrink recorded bounds. Local bounds use the complete source path expanded by the maximum animated stroke width and cap/join factor. Relative brushes use the complete geometry widened by the current resolved style, not the partial trim geometry. This prevents damage under-invalidation and gradient stretching during reveal.
Verification Contract
Verification MUST cover:
- YAML create and
setbinding for all three properties. - Default-inactive and permanently activated command selection.
- Immediate, animated, retargeted, wrapped, empty, and full trim.
- Fill remaining complete and cap behavior at partial endpoints.
- ViewBox, dash, multiple-contour, dynamic, malformed, and capacity rejection.
- Detach, morph, destroy, activation persistence, and sprite reuse.
- Rendered agreement between
motionand trim on straight and curved paths.
Rationale
Trim belongs to the stroke operation rather than mutable Path state, allowing one outline to be filled, clipped, fully stroked, or trimmed independently. Persistent activation avoids command-shape changes at animation rest. Dash emulation is rejected because it cannot guarantee seam, cap, offset, or follower synchronization semantics.