animl-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NOTICE +34 -0
- package/README.md +25 -2
- package/docs/AUTHORING.md +1530 -0
- package/docs/CLI.md +627 -0
- package/docs/parity/inventory.json +34570 -0
- package/examples/bell.ani +359 -0
- package/examples/bell.animl +114 -0
- package/examples/composite.ani +110 -0
- package/examples/composite.animl +21 -0
- package/examples/feather.ani +56 -0
- package/examples/feather.animl +20 -0
- package/examples/image-paints.ani +32 -0
- package/examples/image-paints.animl +8 -0
- package/examples/lib/cards.animl +9 -0
- package/examples/lib/shared.animl +10 -0
- package/examples/lib/ui.animl +8 -0
- package/examples/moon-toggle.png +0 -0
- package/examples/moon.ani +250 -0
- package/examples/moon.animl +55 -0
- package/examples/motion.ani +94 -0
- package/examples/motion.animl +24 -0
- package/examples/paint.ani +91 -0
- package/examples/paint.animl +21 -0
- package/examples/photo.ani +29 -0
- package/examples/photo.animl +6 -0
- package/examples/progress.ani +43 -0
- package/examples/progress.animl +6 -0
- package/examples/shapes.ani +109 -0
- package/examples/shapes.animl +16 -0
- package/examples/slice.ani +32 -0
- package/examples/slice.animl +9 -0
- package/examples/switch.ani +91 -0
- package/examples/switch.animl +22 -0
- package/examples/toolbar.ani +480 -0
- package/examples/toolbar.animl +115 -0
- package/examples/trash-ring.png +0 -0
- package/examples/trash.ani +357 -0
- package/examples/trash.animl +88 -0
- package/examples/web/index.html +520 -0
- package/examples/web/template.html +162 -0
- package/fixtures/bell/rive-reference/frames/00-rest.png +0 -0
- package/fixtures/bell/rive-reference/frames/01-hover.png +0 -0
- package/fixtures/bell/rive-reference/frames/02-ring-f3.png +0 -0
- package/fixtures/bell/rive-reference/frames/03-ring-f7.png +0 -0
- package/fixtures/bell/rive-reference/frames/04-ring-f17.png +0 -0
- package/fixtures/bell/rive-reference/frames/05-ring-f27.png +0 -0
- package/fixtures/bell/rive-reference/frames/06-ring-f57.png +0 -0
- package/fixtures/bell/rive-reference/frames/07-rested.png +0 -0
- package/fixtures/bell/rive-reference/frames/08-out.png +0 -0
- package/fixtures/bell/rive-reference/frames/commands.txt +24 -0
- package/llms.txt +30 -0
- package/modules/a11y/AUTHORING.md +108 -0
- package/modules/a11y/SPEC.md +304 -0
- package/modules/a11y/capabilities/a11y-focus-condition/example.ani +69 -0
- package/modules/a11y/capabilities/a11y-focus-condition/example.animl +17 -0
- package/modules/a11y/capabilities/a11y-inferred-text/example.ani +47 -0
- package/modules/a11y/capabilities/a11y-inferred-text/example.animl +13 -0
- package/modules/a11y/capabilities/a11y-property-heading-level/example.ani +26 -0
- package/modules/a11y/capabilities/a11y-property-heading-level/example.animl +6 -0
- package/modules/a11y/capabilities/a11y-tree-bounds/example.ani +88 -0
- package/modules/a11y/capabilities/a11y-tree-bounds/example.animl +18 -0
- package/modules/a11y/capabilities/a11y-tree-visibility/example.ani +16 -0
- package/modules/a11y/capabilities/a11y-tree-visibility/example.animl +4 -0
- package/modules/a11y/core/commands.source +135 -0
- package/modules/a11y/examples/contract.ani +74 -0
- package/modules/a11y/examples/contract.animl +15 -0
- package/modules/a11y/examples/focus-ancestor-policy.ani +72 -0
- package/modules/a11y/examples/focus-ancestor-policy.animl +16 -0
- package/modules/a11y/examples/painted-extents.ani +331 -0
- package/modules/a11y/examples/painted-extents.animl +62 -0
- package/modules/a11y/examples/painted-groups.ani +88 -0
- package/modules/a11y/examples/painted-groups.animl +18 -0
- package/modules/a11y/examples/slider-value-text.ani +53 -0
- package/modules/a11y/examples/slider-value-text.animl +11 -0
- package/modules/a11y/manifest.source +49 -0
- package/modules/a11y-actions/manifest.source +22 -0
- package/modules/a11y-dom/AUTHORING.md +35 -0
- package/modules/a11y-dom/SPEC.md +165 -0
- package/modules/a11y-dom/examples/mute.ani +28 -0
- package/modules/a11y-dom/examples/mute.animl +6 -0
- package/modules/a11y-dom/examples/navigation.ani +152 -0
- package/modules/a11y-dom/examples/navigation.animl +35 -0
- package/modules/a11y-dom/manifest.source +22 -0
- package/modules/assetio/SPEC.md +16 -0
- package/modules/assetio/manifest.source +12 -0
- package/modules/assets/SPEC.md +23 -0
- package/modules/assets/capabilities/assets-audio/example.ani +60 -0
- package/modules/assets/capabilities/assets-audio/example.animl +16 -0
- package/modules/assets/capabilities/assets-audio-clip/example.ani +62 -0
- package/modules/assets/capabilities/assets-audio-clip/example.animl +17 -0
- package/modules/assets/capabilities/assets-audio-export-source/example.ani +60 -0
- package/modules/assets/capabilities/assets-audio-export-source/example.animl +16 -0
- package/modules/assets/capabilities/assets-audio-import-flac/example.ani +63 -0
- package/modules/assets/capabilities/assets-audio-import-flac/example.animl +17 -0
- package/modules/assets/capabilities/assets-audio-import-mp3/example.ani +63 -0
- package/modules/assets/capabilities/assets-audio-import-mp3/example.animl +17 -0
- package/modules/assets/capabilities/assets-audio-import-wav/example.ani +60 -0
- package/modules/assets/capabilities/assets-audio-import-wav/example.animl +16 -0
- package/modules/assets/capabilities/assets-blob/example.ani +52 -0
- package/modules/assets/capabilities/assets-blob/example.animl +13 -0
- package/modules/assets/capabilities/assets-cdn-disable/example.ani +48 -0
- package/modules/assets/capabilities/assets-cdn-disable/example.animl +12 -0
- package/modules/assets/capabilities/assets-exclude-export/example.ani +49 -0
- package/modules/assets/capabilities/assets-exclude-export/example.animl +12 -0
- package/modules/assets/capabilities/assets-host-decode-audio/example.ani +60 -0
- package/modules/assets/capabilities/assets-host-decode-audio/example.animl +16 -0
- package/modules/assets/capabilities/assets-image-encoding-jpeg/example.ani +50 -0
- package/modules/assets/capabilities/assets-image-encoding-jpeg/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-encoding-png/example.ani +50 -0
- package/modules/assets/capabilities/assets-image-encoding-png/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-encoding-webp/example.ani +50 -0
- package/modules/assets/capabilities/assets-image-encoding-webp/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-export-controls/example.ani +49 -0
- package/modules/assets/capabilities/assets-image-export-controls/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-filter-bilinear/example.ani +49 -0
- package/modules/assets/capabilities/assets-image-filter-bilinear/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-filter-nearest/example.ani +49 -0
- package/modules/assets/capabilities/assets-image-filter-nearest/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-import-jpeg/example.ani +49 -0
- package/modules/assets/capabilities/assets-image-import-jpeg/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-import-jpg/example.ani +49 -0
- package/modules/assets/capabilities/assets-image-import-jpg/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-import-png/example.ani +49 -0
- package/modules/assets/capabilities/assets-image-import-png/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-import-webp/example.ani +49 -0
- package/modules/assets/capabilities/assets-image-import-webp/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-wrap-x-clamp/example.ani +50 -0
- package/modules/assets/capabilities/assets-image-wrap-x-clamp/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-wrap-x-mirror/example.ani +50 -0
- package/modules/assets/capabilities/assets-image-wrap-x-mirror/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-wrap-x-repeat/example.ani +50 -0
- package/modules/assets/capabilities/assets-image-wrap-x-repeat/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-wrap-y-clamp/example.ani +50 -0
- package/modules/assets/capabilities/assets-image-wrap-y-clamp/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-wrap-y-mirror/example.ani +50 -0
- package/modules/assets/capabilities/assets-image-wrap-y-mirror/example.animl +12 -0
- package/modules/assets/capabilities/assets-image-wrap-y-repeat/example.ani +50 -0
- package/modules/assets/capabilities/assets-image-wrap-y-repeat/example.animl +12 -0
- package/modules/assets/capabilities/assets-lottie-import/example.ani +58 -0
- package/modules/assets/capabilities/assets-lottie-import/example.animl +20 -0
- package/modules/assets/capabilities/assets-metadata/example.ani +53 -0
- package/modules/assets/capabilities/assets-metadata/example.animl +12 -0
- package/modules/assets/capabilities/assets-replacement-scope-image/example.ani +48 -0
- package/modules/assets/capabilities/assets-replacement-scope-image/example.animl +12 -0
- package/modules/assets/capabilities/assets-svg-import/example.ani +52 -0
- package/modules/assets/capabilities/assets-svg-import/example.animl +11 -0
- package/modules/assets/capabilities/host-api-asset-cdn-policy/example.ani +48 -0
- package/modules/assets/capabilities/host-api-asset-cdn-policy/example.animl +12 -0
- package/modules/assets/capabilities/host-api-asset-loader/example.ani +48 -0
- package/modules/assets/capabilities/host-api-asset-loader/example.animl +12 -0
- package/modules/assets/capabilities/host-api-asset-metadata/example.ani +53 -0
- package/modules/assets/capabilities/host-api-asset-metadata/example.animl +12 -0
- package/modules/assets/manifest.source +79 -0
- package/modules/bind/AUTHORING.md +49 -0
- package/modules/bind/capabilities/data-bind-listener-actions/example.ani +30 -0
- package/modules/bind/capabilities/data-bind-listener-actions/example.animl +5 -0
- package/modules/bind/core/expression.source +224 -0
- package/modules/bind/examples/filtering-list.ani +122 -0
- package/modules/bind/examples/filtering-list.animl +29 -0
- package/modules/bind/manifest.source +58 -0
- package/modules/canvas/capabilities/host-api-device-pixel-ratio/example.ani +16 -0
- package/modules/canvas/capabilities/host-api-device-pixel-ratio/example.animl +3 -0
- package/modules/canvas/capabilities/host-api-resize-backing-surface/example.ani +16 -0
- package/modules/canvas/capabilities/host-api-resize-backing-surface/example.animl +3 -0
- package/modules/canvas/capabilities/platforms-web-canvas/example.ani +16 -0
- package/modules/canvas/capabilities/platforms-web-canvas/example.animl +3 -0
- package/modules/canvas/manifest.source +16 -0
- package/modules/colormix/examples/switch.ani +51 -0
- package/modules/colormix/examples/switch.animl +13 -0
- package/modules/colormix/manifest.source +39 -0
- package/modules/componentpaint/capabilities/drawing-paint-paint-artboard/example.ani +72 -0
- package/modules/componentpaint/capabilities/drawing-paint-paint-artboard/example.animl +13 -0
- package/modules/componentpaint/examples/live.ani +72 -0
- package/modules/componentpaint/examples/live.animl +13 -0
- package/modules/componentpaint/manifest.source +29 -0
- package/modules/components/manifest.source +61 -0
- package/modules/composite/manifest.source +49 -0
- package/modules/contour/capabilities/drawing-paint-asymmetric-vertex/example.ani +32 -0
- package/modules/contour/capabilities/drawing-paint-asymmetric-vertex/example.animl +5 -0
- package/modules/contour/capabilities/drawing-paint-detached-vertex/example.ani +32 -0
- package/modules/contour/capabilities/drawing-paint-detached-vertex/example.animl +5 -0
- package/modules/contour/capabilities/drawing-paint-effect-order/example.ani +28 -0
- package/modules/contour/capabilities/drawing-paint-effect-order/example.animl +3 -0
- package/modules/contour/capabilities/drawing-paint-fill-rule-clockwise/example.ani +17 -0
- package/modules/contour/capabilities/drawing-paint-fill-rule-clockwise/example.animl +3 -0
- package/modules/contour/capabilities/drawing-paint-geometry-origin/example.ani +35 -0
- package/modules/contour/capabilities/drawing-paint-geometry-origin/example.animl +5 -0
- package/modules/contour/capabilities/drawing-paint-mirrored-vertex/example.ani +31 -0
- package/modules/contour/capabilities/drawing-paint-mirrored-vertex/example.animl +5 -0
- package/modules/contour/capabilities/drawing-paint-rounded-vertex/example.ani +31 -0
- package/modules/contour/capabilities/drawing-paint-rounded-vertex/example.animl +5 -0
- package/modules/contour/capabilities/drawing-paint-shared-effects/example.ani +26 -0
- package/modules/contour/capabilities/drawing-paint-shared-effects/example.animl +3 -0
- package/modules/contour/capabilities/drawing-paint-straight-vertex/example.ani +30 -0
- package/modules/contour/capabilities/drawing-paint-straight-vertex/example.animl +5 -0
- package/modules/contour/capabilities/drawing-paint-trim-sequential/example.ani +22 -0
- package/modules/contour/capabilities/drawing-paint-trim-sequential/example.animl +3 -0
- package/modules/contour/capabilities/drawing-paint-trim-synchronized/example.ani +22 -0
- package/modules/contour/capabilities/drawing-paint-trim-synchronized/example.animl +3 -0
- package/modules/contour/manifest.source +47 -0
- package/modules/convert/SPEC.md +140 -0
- package/modules/convert/capabilities/data-converter-color-formatting/example.ani +44 -0
- package/modules/convert/capabilities/data-converter-color-formatting/example.animl +8 -0
- package/modules/convert/capabilities/data-converter-trigger/example.ani +78 -0
- package/modules/convert/capabilities/data-converter-trigger/example.animl +22 -0
- package/modules/convert/core/math.source +126 -0
- package/modules/convert/examples/score.ani +60 -0
- package/modules/convert/examples/score.animl +12 -0
- package/modules/convert/examples/travelling-indicator.ani +41 -0
- package/modules/convert/examples/travelling-indicator.animl +8 -0
- package/modules/convert/manifest.source +53 -0
- package/modules/corners/examples/switch.ani +20 -0
- package/modules/corners/examples/switch.animl +3 -0
- package/modules/corners/manifest.source +25 -0
- package/modules/data/SPEC.md +216 -0
- package/modules/data/capabilities/data-bind-conditions/example.ani +52 -0
- package/modules/data/capabilities/data-bind-conditions/example.animl +17 -0
- package/modules/data/capabilities/data-bind-converter-options/example.ani +16 -0
- package/modules/data/capabilities/data-bind-converter-options/example.animl +4 -0
- package/modules/data/capabilities/data-bind-instance-values/example.ani +66 -0
- package/modules/data/capabilities/data-bind-instance-values/example.animl +17 -0
- package/modules/data/capabilities/data-bind-keyframes/example.ani +16 -0
- package/modules/data/capabilities/data-bind-keyframes/example.animl +4 -0
- package/modules/data/capabilities/data-bind-script-inputs/example.ani +16 -0
- package/modules/data/capabilities/data-bind-script-inputs/example.animl +4 -0
- package/modules/data/capabilities/data-bind-text/example.ani +16 -0
- package/modules/data/capabilities/data-bind-text/example.animl +4 -0
- package/modules/data/capabilities/data-binding-absolute/example.ani +43 -0
- package/modules/data/capabilities/data-binding-absolute/example.animl +13 -0
- package/modules/data/capabilities/data-binding-once/example.ani +58 -0
- package/modules/data/capabilities/data-binding-once/example.animl +20 -0
- package/modules/data/capabilities/data-binding-relative/example.ani +16 -0
- package/modules/data/capabilities/data-binding-relative/example.animl +4 -0
- package/modules/data/capabilities/data-binding-to-source/example.ani +58 -0
- package/modules/data/capabilities/data-binding-to-source/example.animl +20 -0
- package/modules/data/capabilities/data-binding-two-way-prefer-source/example.ani +59 -0
- package/modules/data/capabilities/data-binding-two-way-prefer-source/example.animl +20 -0
- package/modules/data/capabilities/data-binding-two-way-prefer-target/example.ani +59 -0
- package/modules/data/capabilities/data-binding-two-way-prefer-target/example.animl +20 -0
- package/modules/data/capabilities/data-component-public-data/example.ani +66 -0
- package/modules/data/capabilities/data-component-public-data/example.animl +17 -0
- package/modules/data/capabilities/data-custom-property-boolean/example.ani +35 -0
- package/modules/data/capabilities/data-custom-property-boolean/example.animl +9 -0
- package/modules/data/capabilities/data-custom-property-color/example.ani +38 -0
- package/modules/data/capabilities/data-custom-property-color/example.animl +9 -0
- package/modules/data/capabilities/data-custom-property-enum/example.ani +40 -0
- package/modules/data/capabilities/data-custom-property-enum/example.animl +9 -0
- package/modules/data/capabilities/data-custom-property-number/example.ani +35 -0
- package/modules/data/capabilities/data-custom-property-number/example.animl +9 -0
- package/modules/data/capabilities/data-custom-property-string/example.ani +35 -0
- package/modules/data/capabilities/data-custom-property-string/example.animl +9 -0
- package/modules/data/capabilities/data-custom-property-trigger/example.ani +52 -0
- package/modules/data/capabilities/data-custom-property-trigger/example.animl +15 -0
- package/modules/data/capabilities/data-default-instance/example.ani +43 -0
- package/modules/data/capabilities/data-default-instance/example.animl +13 -0
- package/modules/data/capabilities/data-editor-preview-instance/example.ani +16 -0
- package/modules/data/capabilities/data-editor-preview-instance/example.animl +4 -0
- package/modules/data/capabilities/data-enum-members/example.ani +41 -0
- package/modules/data/capabilities/data-enum-members/example.animl +8 -0
- package/modules/data/capabilities/data-global-model/example.ani +64 -0
- package/modules/data/capabilities/data-global-model/example.animl +17 -0
- package/modules/data/capabilities/data-instance-export/example.ani +41 -0
- package/modules/data/capabilities/data-instance-export/example.animl +8 -0
- package/modules/data/capabilities/data-list-items/example.ani +55 -0
- package/modules/data/capabilities/data-list-items/example.animl +16 -0
- package/modules/data/capabilities/data-model-definition/example.ani +43 -0
- package/modules/data/capabilities/data-model-definition/example.animl +13 -0
- package/modules/data/capabilities/data-named-instances/example.ani +43 -0
- package/modules/data/capabilities/data-named-instances/example.animl +13 -0
- package/modules/data/capabilities/data-operation-operand-view-model/example.ani +32 -0
- package/modules/data/capabilities/data-operation-operand-view-model/example.animl +9 -0
- package/modules/data/capabilities/data-property-artboard/example.ani +16 -0
- package/modules/data/capabilities/data-property-artboard/example.animl +4 -0
- package/modules/data/capabilities/data-property-asset-blob/example.ani +40 -0
- package/modules/data/capabilities/data-property-asset-blob/example.animl +10 -0
- package/modules/data/capabilities/data-property-asset-font/example.ani +40 -0
- package/modules/data/capabilities/data-property-asset-font/example.animl +10 -0
- package/modules/data/capabilities/data-property-asset-image/example.ani +40 -0
- package/modules/data/capabilities/data-property-asset-image/example.animl +12 -0
- package/modules/data/capabilities/data-property-boolean/example.ani +28 -0
- package/modules/data/capabilities/data-property-boolean/example.animl +7 -0
- package/modules/data/capabilities/data-property-color/example.ani +29 -0
- package/modules/data/capabilities/data-property-color/example.animl +7 -0
- package/modules/data/capabilities/data-property-enum-custom/example.ani +29 -0
- package/modules/data/capabilities/data-property-enum-custom/example.animl +7 -0
- package/modules/data/capabilities/data-property-enum-system/example.ani +29 -0
- package/modules/data/capabilities/data-property-enum-system/example.animl +7 -0
- package/modules/data/capabilities/data-property-list/example.ani +55 -0
- package/modules/data/capabilities/data-property-list/example.animl +16 -0
- package/modules/data/capabilities/data-property-number/example.ani +29 -0
- package/modules/data/capabilities/data-property-number/example.animl +7 -0
- package/modules/data/capabilities/data-property-string/example.ani +29 -0
- package/modules/data/capabilities/data-property-string/example.animl +7 -0
- package/modules/data/capabilities/data-property-symbol-list-index/example.ani +16 -0
- package/modules/data/capabilities/data-property-symbol-list-index/example.animl +4 -0
- package/modules/data/capabilities/data-property-text-style/example.ani +16 -0
- package/modules/data/capabilities/data-property-text-style/example.animl +4 -0
- package/modules/data/capabilities/data-property-trigger/example.ani +51 -0
- package/modules/data/capabilities/data-property-trigger/example.animl +17 -0
- package/modules/data/capabilities/data-property-view-model/example.ani +55 -0
- package/modules/data/capabilities/data-property-view-model/example.animl +16 -0
- package/modules/data/capabilities/data-script-model-api/example.ani +16 -0
- package/modules/data/capabilities/data-script-model-api/example.animl +4 -0
- package/modules/data/capabilities/data-scripted-converter/example.ani +16 -0
- package/modules/data/capabilities/data-scripted-converter/example.animl +4 -0
- package/modules/data/capabilities/data-state-action-trigger/example.ani +51 -0
- package/modules/data/capabilities/data-state-action-trigger/example.animl +17 -0
- package/modules/data/capabilities/data-system-enum-horizontal-text-align/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-horizontal-text-align/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-layout-alignment/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-layout-alignment/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-layout-direction/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-layout-direction/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-layout-display/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-layout-display/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-layout-flex-direction/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-layout-flex-direction/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-layout-position/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-layout-position/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-layout-position-units/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-layout-position-units/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-layout-scale-type/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-layout-scale-type/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-layout-wrap/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-layout-wrap/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-scroll-direction/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-scroll-direction/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-semantic-check-state/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-semantic-check-state/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-text-box/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-text-box/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-text-overflow/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-text-overflow/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-text-trim-bottom/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-text-trim-bottom/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-text-trim-top/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-text-trim-top/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-vertical-text-align/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-vertical-text-align/example.animl +7 -0
- package/modules/data/capabilities/data-system-enum-word-wrap/example.ani +29 -0
- package/modules/data/capabilities/data-system-enum-word-wrap/example.animl +7 -0
- package/modules/data/capabilities/drawing-paint-gradient-channel-binding/example.ani +46 -0
- package/modules/data/capabilities/drawing-paint-gradient-channel-binding/example.animl +14 -0
- package/modules/data/capabilities/input-data-listener/example.ani +41 -0
- package/modules/data/capabilities/input-data-listener/example.animl +10 -0
- package/modules/data/capabilities/input-write-data-constant/example.ani +41 -0
- package/modules/data/capabilities/input-write-data-constant/example.animl +10 -0
- package/modules/data/capabilities/input-write-data-copy/example.ani +39 -0
- package/modules/data/capabilities/input-write-data-copy/example.animl +10 -0
- package/modules/data/capabilities/input-write-focused/example.ani +39 -0
- package/modules/data/capabilities/input-write-focused/example.animl +11 -0
- package/modules/data/capabilities/input-write-gamepad-axis/example.ani +38 -0
- package/modules/data/capabilities/input-write-gamepad-axis/example.animl +9 -0
- package/modules/data/capabilities/input-write-gamepad-button-pressed/example.ani +38 -0
- package/modules/data/capabilities/input-write-gamepad-button-pressed/example.animl +9 -0
- package/modules/data/capabilities/input-write-gamepad-button-value/example.ani +38 -0
- package/modules/data/capabilities/input-write-gamepad-button-value/example.animl +9 -0
- package/modules/data/capabilities/input-write-gamepad-changed-value/example.ani +38 -0
- package/modules/data/capabilities/input-write-gamepad-changed-value/example.animl +9 -0
- package/modules/data/examples/data-extras.ani +109 -0
- package/modules/data/examples/data-extras.animl +28 -0
- package/modules/data/examples/scoreboard.ani +74 -0
- package/modules/data/examples/scoreboard.animl +18 -0
- package/modules/data/manifest.source +111 -0
- package/modules/data-artboard/SPEC.md +94 -0
- package/modules/data-artboard/capabilities/components-host-artboard-reference/example.ani +16 -0
- package/modules/data-artboard/capabilities/components-host-artboard-reference/example.animl +26 -0
- package/modules/data-artboard/capabilities/data-host-artboard/example.ani +16 -0
- package/modules/data-artboard/capabilities/data-host-artboard/example.animl +18 -0
- package/modules/data-artboard/capabilities/host-api-bindable-artboards/example.ani +16 -0
- package/modules/data-artboard/capabilities/host-api-bindable-artboards/example.animl +34 -0
- package/modules/data-artboard/capabilities/host-api-data-artboard/example.ani +16 -0
- package/modules/data-artboard/capabilities/host-api-data-artboard/example.animl +18 -0
- package/modules/data-artboard/manifest.source +41 -0
- package/modules/data-host/SPEC.md +50 -0
- package/modules/data-host/examples/nested.ani +82 -0
- package/modules/data-host/examples/nested.animl +21 -0
- package/modules/data-host/manifest.source +26 -0
- package/modules/derive/SPEC.md +66 -0
- package/modules/derive/examples/scoreboard.ani +121 -0
- package/modules/derive/examples/scoreboard.animl +27 -0
- package/modules/derive/manifest.source +24 -0
- package/modules/devtools/SPEC.md +25 -0
- package/modules/devtools/capabilities/host-api-deprecation-policy/example.ani +16 -0
- package/modules/devtools/capabilities/host-api-deprecation-policy/example.animl +3 -0
- package/modules/devtools/capabilities/host-api-fps-counter/example.ani +16 -0
- package/modules/devtools/capabilities/host-api-fps-counter/example.animl +9 -0
- package/modules/devtools/capabilities/host-api-performance-marks/example.ani +16 -0
- package/modules/devtools/capabilities/host-api-performance-marks/example.animl +3 -0
- package/modules/devtools/capabilities/host-api-stop/example.ani +16 -0
- package/modules/devtools/capabilities/host-api-stop/example.animl +3 -0
- package/modules/devtools/manifest.source +13 -0
- package/modules/events/examples/events.ani +75 -0
- package/modules/events/examples/events.animl +23 -0
- package/modules/events/examples/share-evidence/README.md +48 -0
- package/modules/events/examples/share-evidence/clicks.jsonl +51 -0
- package/modules/events/examples/share-evidence/clicks.script +102 -0
- package/modules/events/examples/share-evidence/interrupt.jsonl +3 -0
- package/modules/events/examples/share-evidence/interrupt.script +7 -0
- package/modules/events/examples/share-evidence/perf.txt +4 -0
- package/modules/events/examples/share-evidence/pulse.jsonl +10 -0
- package/modules/events/examples/share-evidence/pulse.script +20 -0
- package/modules/events/examples/share-evidence/rest-diff.txt +1 -0
- package/modules/events/examples/share-evidence/size.txt +18 -0
- package/modules/events/examples/share-evidence/verify.py +48 -0
- package/modules/events/examples/share-tone.wav +0 -0
- package/modules/events/examples/share.ani +186 -0
- package/modules/events/examples/share.animl +40 -0
- package/modules/events/manifest.source +74 -0
- package/modules/focus/capabilities/input-focus-clear/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-clear/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-down/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-down/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-edge-closed-loop/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-edge-closed-loop/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-edge-parent-scope/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-edge-parent-scope/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-edge-stop/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-edge-stop/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-enabled/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-enabled/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-left/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-left/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-next/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-next/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-pointer/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-pointer/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-previous/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-previous/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-right/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-right/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-target/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-target/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-traverse/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-traverse/example.animl +28 -0
- package/modules/focus/capabilities/input-focus-up/example.ani +116 -0
- package/modules/focus/capabilities/input-focus-up/example.animl +28 -0
- package/modules/focus/capabilities/input-listen-blur/example.ani +116 -0
- package/modules/focus/capabilities/input-listen-blur/example.animl +28 -0
- package/modules/focus/capabilities/input-listen-focus/example.ani +116 -0
- package/modules/focus/capabilities/input-listen-focus/example.animl +28 -0
- package/modules/focus/examples/README.md +10 -0
- package/modules/focus/examples/traversal.ani +70 -0
- package/modules/focus/examples/traversal.animl +16 -0
- package/modules/focus/manifest.source +48 -0
- package/modules/fonts/SPEC.md +92 -0
- package/modules/fonts/capabilities/assets-font/example.ani +23 -0
- package/modules/fonts/capabilities/assets-font/example.animl +5 -0
- package/modules/fonts/capabilities/assets-host-decode-font/example.ani +23 -0
- package/modules/fonts/capabilities/assets-host-decode-font/example.animl +5 -0
- package/modules/fonts/capabilities/assets-replacement-scope-font/example.ani +23 -0
- package/modules/fonts/capabilities/assets-replacement-scope-font/example.animl +5 -0
- package/modules/fonts/capabilities/data-host-font/example.ani +23 -0
- package/modules/fonts/capabilities/data-host-font/example.animl +5 -0
- package/modules/fonts/capabilities/host-api-data-font/example.ani +23 -0
- package/modules/fonts/capabilities/host-api-data-font/example.animl +5 -0
- package/modules/fonts/capabilities/host-api-font-fallback/example.ani +23 -0
- package/modules/fonts/capabilities/host-api-font-fallback/example.animl +5 -0
- package/modules/fonts/capabilities/text-font-assets/example.ani +23 -0
- package/modules/fonts/capabilities/text-font-assets/example.animl +5 -0
- package/modules/fonts/capabilities/text-font-decoding/example.ani +43 -0
- package/modules/fonts/capabilities/text-font-decoding/example.animl +9 -0
- package/modules/fonts/capabilities/text-font-fallback/example.ani +23 -0
- package/modules/fonts/capabilities/text-font-fallback/example.animl +5 -0
- package/modules/fonts/capabilities/text-font-metrics/example.ani +40 -0
- package/modules/fonts/capabilities/text-font-metrics/example.animl +9 -0
- package/modules/fonts/capabilities/text-opentype-features/example.ani +23 -0
- package/modules/fonts/capabilities/text-opentype-features/example.animl +7 -0
- package/modules/fonts/capabilities/text-variable-axes/example.ani +23 -0
- package/modules/fonts/capabilities/text-variable-axes/example.animl +9 -0
- package/modules/fonts/manifest.source +57 -0
- package/modules/host/AUTHORING.md +33 -0
- package/modules/host/SPEC.md +76 -0
- package/modules/host/examples/page.html +23 -0
- package/modules/host/examples/score.ani +48 -0
- package/modules/host/examples/score.animl +10 -0
- package/modules/host/manifest.source +19 -0
- package/modules/host-edit/AUTHORING.md +66 -0
- package/modules/host-edit/SPEC.md +96 -0
- package/modules/host-edit/examples/board.ani +72 -0
- package/modules/host-edit/examples/board.animl +17 -0
- package/modules/host-edit/examples/page.html +34 -0
- package/modules/host-edit/manifest.source +21 -0
- package/modules/host-fit/capabilities/host-api-fit-layout/example.ani +55 -0
- package/modules/host-fit/capabilities/host-api-fit-layout/example.animl +11 -0
- package/modules/host-fit/examples/tile.ani +19 -0
- package/modules/host-fit/examples/tile.animl +3 -0
- package/modules/host-fit/manifest.source +22 -0
- package/modules/host-values/SPEC.md +9 -0
- package/modules/host-values/manifest.source +22 -0
- package/modules/ik/capabilities/rigging-ik/example.ani +16 -0
- package/modules/ik/capabilities/rigging-ik/example.animl +11 -0
- package/modules/ik/capabilities/rigging-ik-bend/example.ani +16 -0
- package/modules/ik/capabilities/rigging-ik-bend/example.animl +11 -0
- package/modules/ik/examples/reach.ani +100 -0
- package/modules/ik/examples/reach.animl +18 -0
- package/modules/ik/manifest.source +30 -0
- package/modules/input/SPEC.md +397 -0
- package/modules/input/capabilities/host-api-coordinate-mapping/example.ani +32 -0
- package/modules/input/capabilities/host-api-coordinate-mapping/example.animl +7 -0
- package/modules/input/capabilities/host-api-dom-listener-control/example.ani +32 -0
- package/modules/input/capabilities/host-api-dom-listener-control/example.animl +7 -0
- package/modules/input/capabilities/host-api-gamepad/example.ani +34 -0
- package/modules/input/capabilities/host-api-gamepad/example.animl +7 -0
- package/modules/input/capabilities/host-api-low-level-pointer/example.ani +32 -0
- package/modules/input/capabilities/host-api-low-level-pointer/example.animl +7 -0
- package/modules/input/capabilities/host-api-low-level-scroll/example.ani +32 -0
- package/modules/input/capabilities/host-api-low-level-scroll/example.animl +7 -0
- package/modules/input/capabilities/host-api-manual-gamepad-batch/example.ani +34 -0
- package/modules/input/capabilities/host-api-manual-gamepad-batch/example.animl +7 -0
- package/modules/input/capabilities/host-api-multitouch/example.ani +32 -0
- package/modules/input/capabilities/host-api-multitouch/example.animl +7 -0
- package/modules/input/capabilities/host-api-pointer-exit/example.ani +31 -0
- package/modules/input/capabilities/host-api-pointer-exit/example.animl +7 -0
- package/modules/input/capabilities/host-api-scroll-hit-policy/example.ani +32 -0
- package/modules/input/capabilities/host-api-scroll-hit-policy/example.animl +7 -0
- package/modules/input/capabilities/host-api-scroll-latching/example.ani +32 -0
- package/modules/input/capabilities/host-api-scroll-latching/example.animl +7 -0
- package/modules/input/capabilities/input-align-preserve/example.ani +43 -0
- package/modules/input/capabilities/input-align-preserve/example.animl +9 -0
- package/modules/input/capabilities/input-align-snap/example.ani +43 -0
- package/modules/input/capabilities/input-align-snap/example.animl +9 -0
- package/modules/input/capabilities/input-bounded-hit-tests/example.ani +32 -0
- package/modules/input/capabilities/input-bounded-hit-tests/example.animl +7 -0
- package/modules/input/capabilities/input-button-middle/example.ani +33 -0
- package/modules/input/capabilities/input-button-middle/example.animl +7 -0
- package/modules/input/capabilities/input-button-secondary/example.ani +33 -0
- package/modules/input/capabilities/input-button-secondary/example.animl +7 -0
- package/modules/input/capabilities/input-capture-phase/example.ani +60 -0
- package/modules/input/capabilities/input-capture-phase/example.animl +11 -0
- package/modules/input/capabilities/input-cursor-arrow/example.ani +33 -0
- package/modules/input/capabilities/input-cursor-arrow/example.animl +7 -0
- package/modules/input/capabilities/input-cursor-hidden/example.ani +33 -0
- package/modules/input/capabilities/input-cursor-hidden/example.animl +7 -0
- package/modules/input/capabilities/input-cursor-intent/example.ani +33 -0
- package/modules/input/capabilities/input-cursor-intent/example.animl +7 -0
- package/modules/input/capabilities/input-cursor-pointer/example.ani +33 -0
- package/modules/input/capabilities/input-cursor-pointer/example.animl +7 -0
- package/modules/input/capabilities/input-cursor-resize-horizontal/example.ani +33 -0
- package/modules/input/capabilities/input-cursor-resize-horizontal/example.animl +7 -0
- package/modules/input/capabilities/input-cursor-resize-vertical/example.ani +33 -0
- package/modules/input/capabilities/input-cursor-resize-vertical/example.animl +7 -0
- package/modules/input/capabilities/input-cursor-text/example.ani +33 -0
- package/modules/input/capabilities/input-cursor-text/example.animl +7 -0
- package/modules/input/capabilities/input-disable-listeners/example.ani +32 -0
- package/modules/input/capabilities/input-disable-listeners/example.animl +7 -0
- package/modules/input/capabilities/input-gamepad-axis/example.ani +34 -0
- package/modules/input/capabilities/input-gamepad-axis/example.animl +7 -0
- package/modules/input/capabilities/input-gamepad-button/example.ani +32 -0
- package/modules/input/capabilities/input-gamepad-button/example.animl +7 -0
- package/modules/input/capabilities/input-gamepad-button-phases/example.ani +33 -0
- package/modules/input/capabilities/input-gamepad-button-phases/example.animl +7 -0
- package/modules/input/capabilities/input-gamepad-connected/example.ani +31 -0
- package/modules/input/capabilities/input-gamepad-connected/example.animl +7 -0
- package/modules/input/capabilities/input-gamepad-disconnected/example.ani +31 -0
- package/modules/input/capabilities/input-gamepad-disconnected/example.animl +7 -0
- package/modules/input/capabilities/input-gamepad-host/example.ani +34 -0
- package/modules/input/capabilities/input-gamepad-host/example.animl +7 -0
- package/modules/input/capabilities/input-gamepad-index-mapping/example.ani +34 -0
- package/modules/input/capabilities/input-gamepad-index-mapping/example.animl +7 -0
- package/modules/input/capabilities/input-hit-occlusion-query/example.ani +42 -0
- package/modules/input/capabilities/input-hit-occlusion-query/example.animl +8 -0
- package/modules/input/capabilities/input-hit-parent-chain/example.ani +39 -0
- package/modules/input/capabilities/input-hit-parent-chain/example.animl +9 -0
- package/modules/input/capabilities/input-hit-shape-geometry/example.ani +32 -0
- package/modules/input/capabilities/input-hit-shape-geometry/example.animl +7 -0
- package/modules/input/capabilities/input-hit-test-host/example.ani +32 -0
- package/modules/input/capabilities/input-hit-test-host/example.animl +7 -0
- package/modules/input/capabilities/input-key-down/example.ani +32 -0
- package/modules/input/capabilities/input-key-down/example.animl +7 -0
- package/modules/input/capabilities/input-key-filter/example.ani +32 -0
- package/modules/input/capabilities/input-key-filter/example.animl +7 -0
- package/modules/input/capabilities/input-key-modifiers/example.ani +33 -0
- package/modules/input/capabilities/input-key-modifiers/example.animl +7 -0
- package/modules/input/capabilities/input-key-repeat/example.ani +32 -0
- package/modules/input/capabilities/input-key-repeat/example.animl +7 -0
- package/modules/input/capabilities/input-key-up/example.ani +32 -0
- package/modules/input/capabilities/input-key-up/example.animl +7 -0
- package/modules/input/capabilities/input-keyboard-bubbling/example.ani +45 -0
- package/modules/input/capabilities/input-keyboard-bubbling/example.animl +10 -0
- package/modules/input/capabilities/input-multi-touch/example.ani +32 -0
- package/modules/input/capabilities/input-multi-touch/example.animl +7 -0
- package/modules/input/capabilities/input-multi-trigger-listener/example.ani +32 -0
- package/modules/input/capabilities/input-multi-trigger-listener/example.animl +7 -0
- package/modules/input/capabilities/input-opaque-targets/example.ani +42 -0
- package/modules/input/capabilities/input-opaque-targets/example.animl +8 -0
- package/modules/input/capabilities/input-pointer-cancel/example.ani +31 -0
- package/modules/input/capabilities/input-pointer-cancel/example.animl +7 -0
- package/modules/input/capabilities/input-pointer-drag/example.ani +58 -0
- package/modules/input/capabilities/input-pointer-drag/example.animl +17 -0
- package/modules/input/capabilities/input-pointer-drag-end/example.ani +58 -0
- package/modules/input/capabilities/input-pointer-drag-end/example.animl +17 -0
- package/modules/input/capabilities/input-pointer-drag-start/example.ani +58 -0
- package/modules/input/capabilities/input-pointer-drag-start/example.animl +17 -0
- package/modules/input/capabilities/input-pointer-enable/example.ani +32 -0
- package/modules/input/capabilities/input-pointer-enable/example.animl +7 -0
- package/modules/input/capabilities/input-touch-page-scroll/example.ani +32 -0
- package/modules/input/capabilities/input-touch-page-scroll/example.animl +7 -0
- package/modules/input/capabilities/input-trackpad-scroll/example.ani +32 -0
- package/modules/input/capabilities/input-trackpad-scroll/example.animl +7 -0
- package/modules/input/capabilities/input-wheel-scroll/example.ani +32 -0
- package/modules/input/capabilities/input-wheel-scroll/example.animl +7 -0
- package/modules/input/capabilities/input-write-key-pressed/example.ani +39 -0
- package/modules/input/capabilities/input-write-key-pressed/example.animl +7 -0
- package/modules/input/capabilities/input-write-pointer-delta-x/example.ani +39 -0
- package/modules/input/capabilities/input-write-pointer-delta-x/example.animl +7 -0
- package/modules/input/capabilities/input-write-pointer-delta-y/example.ani +39 -0
- package/modules/input/capabilities/input-write-pointer-delta-y/example.animl +7 -0
- package/modules/input/capabilities/input-write-pointer-x/example.ani +39 -0
- package/modules/input/capabilities/input-write-pointer-x/example.animl +7 -0
- package/modules/input/capabilities/input-write-pointer-y/example.ani +39 -0
- package/modules/input/capabilities/input-write-pointer-y/example.animl +7 -0
- package/modules/input/capabilities/transforms-collapse/example.ani +39 -0
- package/modules/input/capabilities/transforms-collapse/example.animl +9 -0
- package/modules/input/capabilities/transforms-solo/example.ani +48 -0
- package/modules/input/capabilities/transforms-solo/example.animl +10 -0
- package/modules/input/core/commands.source +64 -0
- package/modules/input/core/types.source +97 -0
- package/modules/input/examples/README.md +9 -0
- package/modules/input/examples/drag-slider.ani +50 -0
- package/modules/input/examples/drag-slider.animl +11 -0
- package/modules/input/manifest.source +98 -0
- package/modules/layout/SPEC.md +210 -0
- package/modules/layout/capabilities/data-bind-layout/example.ani +60 -0
- package/modules/layout/capabilities/data-bind-layout/example.animl +10 -0
- package/modules/layout/capabilities/layout-alignment/example.ani +44 -0
- package/modules/layout/capabilities/layout-alignment/example.animl +6 -0
- package/modules/layout/capabilities/layout-animated-reflow/example.ani +62 -0
- package/modules/layout/capabilities/layout-animated-reflow/example.animl +12 -0
- package/modules/layout/capabilities/layout-containers/example.ani +62 -0
- package/modules/layout/capabilities/layout-containers/example.animl +9 -0
- package/modules/layout/capabilities/layout-fill-sizing/example.ani +45 -0
- package/modules/layout/capabilities/layout-fill-sizing/example.animl +6 -0
- package/modules/layout/capabilities/layout-fixed-sizing/example.ani +46 -0
- package/modules/layout/capabilities/layout-fixed-sizing/example.animl +6 -0
- package/modules/layout/capabilities/layout-flex-align-items/example.ani +43 -0
- package/modules/layout/capabilities/layout-flex-align-items/example.animl +6 -0
- package/modules/layout/capabilities/layout-flex-direction/example.ani +43 -0
- package/modules/layout/capabilities/layout-flex-direction/example.animl +6 -0
- package/modules/layout/capabilities/layout-flex-justify/example.ani +42 -0
- package/modules/layout/capabilities/layout-flex-justify/example.animl +6 -0
- package/modules/layout/capabilities/layout-hug-sizing/example.ani +42 -0
- package/modules/layout/capabilities/layout-hug-sizing/example.animl +6 -0
- package/modules/layout/capabilities/layout-intrinsic/example.ani +40 -0
- package/modules/layout/capabilities/layout-intrinsic/example.animl +6 -0
- package/modules/layout/capabilities/layout-paint/example.ani +60 -0
- package/modules/layout/capabilities/layout-paint/example.animl +8 -0
- package/modules/layout/capabilities/layout-participants/example.ani +40 -0
- package/modules/layout/capabilities/layout-participants/example.animl +6 -0
- package/modules/layout/capabilities/layout-self-alignment/example.ani +45 -0
- package/modules/layout/capabilities/layout-self-alignment/example.animl +6 -0
- package/modules/layout/capabilities/layout-size-bounds/example.ani +49 -0
- package/modules/layout/capabilities/layout-size-bounds/example.animl +6 -0
- package/modules/layout/capabilities/layout-spacing/example.ani +45 -0
- package/modules/layout/capabilities/layout-spacing/example.animl +6 -0
- package/modules/layout/capabilities/layout-visibility/example.ani +56 -0
- package/modules/layout/capabilities/layout-visibility/example.animl +7 -0
- package/modules/layout/examples/button.ani +61 -0
- package/modules/layout/examples/button.animl +12 -0
- package/modules/layout/examples/reflow.ani +62 -0
- package/modules/layout/examples/reflow.animl +12 -0
- package/modules/layout/examples/wrapped.ani +55 -0
- package/modules/layout/examples/wrapped.animl +11 -0
- package/modules/layout/manifest.source +73 -0
- package/modules/lists/SPEC.md +44 -0
- package/modules/lists/capabilities/components-explicit-context/example.ani +81 -0
- package/modules/lists/capabilities/components-explicit-context/example.animl +22 -0
- package/modules/lists/capabilities/components-inherited-context/example.ani +99 -0
- package/modules/lists/capabilities/components-inherited-context/example.animl +24 -0
- package/modules/lists/capabilities/components-list/example.ani +77 -0
- package/modules/lists/capabilities/components-list/example.animl +19 -0
- package/modules/lists/capabilities/components-list-map/example.ani +116 -0
- package/modules/lists/capabilities/components-list-map/example.animl +29 -0
- package/modules/lists/capabilities/components-list-size-override/example.ani +83 -0
- package/modules/lists/capabilities/components-list-size-override/example.animl +20 -0
- package/modules/lists/capabilities/data-binding-relative/example.ani +77 -0
- package/modules/lists/capabilities/data-binding-relative/example.animl +19 -0
- package/modules/lists/capabilities/data-converter-number-to-list/example.ani +109 -0
- package/modules/lists/capabilities/data-converter-number-to-list/example.animl +29 -0
- package/modules/lists/capabilities/data-property-symbol-list-index/example.ani +77 -0
- package/modules/lists/capabilities/data-property-symbol-list-index/example.animl +19 -0
- package/modules/lists/examples/players.ani +77 -0
- package/modules/lists/examples/players.animl +19 -0
- package/modules/lists/manifest.source +43 -0
- package/modules/machine/capabilities/state-machine-any-state/example.ani +62 -0
- package/modules/machine/capabilities/state-machine-any-state/example.animl +22 -0
- package/modules/machine/capabilities/state-machine-blend-1d-data/example.ani +74 -0
- package/modules/machine/capabilities/state-machine-blend-1d-data/example.animl +23 -0
- package/modules/machine/capabilities/state-machine-blend-1d-input/example.ani +70 -0
- package/modules/machine/capabilities/state-machine-blend-1d-input/example.animl +23 -0
- package/modules/machine/capabilities/state-machine-blend-additive/example.ani +69 -0
- package/modules/machine/capabilities/state-machine-blend-additive/example.animl +23 -0
- package/modules/machine/capabilities/state-machine-blend-direct/example.ani +64 -0
- package/modules/machine/capabilities/state-machine-blend-direct/example.animl +21 -0
- package/modules/machine/capabilities/state-machine-blend-exit-clock/example.ani +72 -0
- package/modules/machine/capabilities/state-machine-blend-exit-clock/example.animl +25 -0
- package/modules/machine/capabilities/state-machine-blend-weight-constant/example.ani +64 -0
- package/modules/machine/capabilities/state-machine-blend-weight-constant/example.animl +21 -0
- package/modules/machine/capabilities/state-machine-blend-weight-data/example.ani +71 -0
- package/modules/machine/capabilities/state-machine-blend-weight-data/example.animl +23 -0
- package/modules/machine/capabilities/state-machine-blend-weight-input/example.ani +69 -0
- package/modules/machine/capabilities/state-machine-blend-weight-input/example.animl +23 -0
- package/modules/machine/capabilities/state-machine-condition-artboard/example.ani +50 -0
- package/modules/machine/capabilities/state-machine-condition-artboard/example.animl +13 -0
- package/modules/machine/capabilities/state-machine-condition-component/example.ani +76 -0
- package/modules/machine/capabilities/state-machine-condition-component/example.animl +19 -0
- package/modules/machine/capabilities/state-machine-condition-data/example.ani +54 -0
- package/modules/machine/capabilities/state-machine-condition-data/example.animl +15 -0
- package/modules/machine/capabilities/state-machine-condition-focus/example.ani +49 -0
- package/modules/machine/capabilities/state-machine-condition-focus/example.animl +13 -0
- package/modules/machine/capabilities/state-machine-continue-on-exit/example.ani +58 -0
- package/modules/machine/capabilities/state-machine-continue-on-exit/example.animl +21 -0
- package/modules/machine/capabilities/state-machine-entry-state/example.ani +56 -0
- package/modules/machine/capabilities/state-machine-entry-state/example.animl +19 -0
- package/modules/machine/capabilities/state-machine-exit-state/example.ani +46 -0
- package/modules/machine/capabilities/state-machine-exit-state/example.animl +15 -0
- package/modules/machine/capabilities/state-machine-exit-time-relative/example.ani +52 -0
- package/modules/machine/capabilities/state-machine-exit-time-relative/example.animl +19 -0
- package/modules/machine/capabilities/state-machine-literal-asset/example.ani +62 -0
- package/modules/machine/capabilities/state-machine-literal-asset/example.animl +18 -0
- package/modules/machine/capabilities/state-machine-literal-boolean/example.ani +53 -0
- package/modules/machine/capabilities/state-machine-literal-boolean/example.animl +15 -0
- package/modules/machine/capabilities/state-machine-literal-color/example.ani +54 -0
- package/modules/machine/capabilities/state-machine-literal-color/example.animl +15 -0
- package/modules/machine/capabilities/state-machine-literal-enum/example.ani +54 -0
- package/modules/machine/capabilities/state-machine-literal-enum/example.animl +15 -0
- package/modules/machine/capabilities/state-machine-literal-string/example.ani +54 -0
- package/modules/machine/capabilities/state-machine-literal-string/example.animl +15 -0
- package/modules/machine/capabilities/state-machine-literal-trigger/example.ani +53 -0
- package/modules/machine/capabilities/state-machine-literal-trigger/example.animl +15 -0
- package/modules/machine/capabilities/state-machine-state-entry-actions/example.ani +56 -0
- package/modules/machine/capabilities/state-machine-state-entry-actions/example.animl +16 -0
- package/modules/machine/capabilities/state-machine-state-exit-actions/example.ani +56 -0
- package/modules/machine/capabilities/state-machine-state-exit-actions/example.animl +16 -0
- package/modules/machine/capabilities/state-machine-state-random/example.ani +53 -0
- package/modules/machine/capabilities/state-machine-state-random/example.animl +17 -0
- package/modules/machine/capabilities/state-machine-state-reset/example.ani +52 -0
- package/modules/machine/capabilities/state-machine-state-reset/example.animl +18 -0
- package/modules/machine/capabilities/state-machine-transition-actions/example.ani +56 -0
- package/modules/machine/capabilities/state-machine-transition-actions/example.animl +16 -0
- package/modules/machine/capabilities/state-machine-transition-disabled/example.ani +57 -0
- package/modules/machine/capabilities/state-machine-transition-disabled/example.animl +19 -0
- package/modules/machine/capabilities/state-machine-transition-duration-relative/example.ani +57 -0
- package/modules/machine/capabilities/state-machine-transition-duration-relative/example.animl +21 -0
- package/modules/machine/capabilities/state-machine-transition-end-actions/example.ani +56 -0
- package/modules/machine/capabilities/state-machine-transition-end-actions/example.animl +16 -0
- package/modules/machine/capabilities/state-machine-transition-random-weight/example.ani +53 -0
- package/modules/machine/capabilities/state-machine-transition-random-weight/example.animl +17 -0
- package/modules/machine/examples/README.md +12 -0
- package/modules/machine/examples/blend-pulse.ani +115 -0
- package/modules/machine/examples/blend-pulse.animl +32 -0
- package/modules/machine/manifest.source +73 -0
- package/modules/markup/manifest.source +26 -0
- package/modules/markup-animation/manifest.source +23 -0
- package/modules/markup-hooks/manifest.source +13 -0
- package/modules/markup-libraries/SPEC.md +56 -0
- package/modules/markup-libraries/capabilities/components-library-export/example.ani +45 -0
- package/modules/markup-libraries/capabilities/components-library-export/example.animl +4 -0
- package/modules/markup-libraries/capabilities/components-library-models/example.ani +45 -0
- package/modules/markup-libraries/capabilities/components-library-models/example.animl +4 -0
- package/modules/markup-libraries/capabilities/components-library-update/example.ani +45 -0
- package/modules/markup-libraries/capabilities/components-library-update/example.animl +4 -0
- package/modules/markup-libraries/manifest.source +38 -0
- package/modules/media/manifest.source +21 -0
- package/modules/mesh/capabilities/rigging-mesh/example.ani +50 -0
- package/modules/mesh/capabilities/rigging-mesh/example.animl +9 -0
- package/modules/mesh/capabilities/rigging-mesh-animation/example.ani +65 -0
- package/modules/mesh/capabilities/rigging-mesh-animation/example.animl +15 -0
- package/modules/mesh/examples/flag.ani +108 -0
- package/modules/mesh/examples/flag.animl +23 -0
- package/modules/mesh/manifest.source +37 -0
- package/modules/mix/capabilities/drawing-paint-additive-strength/example.ani +34 -0
- package/modules/mix/capabilities/drawing-paint-additive-strength/example.animl +4 -0
- package/modules/mix/capabilities/drawing-paint-blend-additive/example.ani +33 -0
- package/modules/mix/capabilities/drawing-paint-blend-additive/example.animl +4 -0
- package/modules/mix/capabilities/drawing-paint-paint-blend-override/example.ani +37 -0
- package/modules/mix/capabilities/drawing-paint-paint-blend-override/example.animl +4 -0
- package/modules/mix/capabilities/host-api-factory/example.ani +16 -0
- package/modules/mix/capabilities/host-api-factory/example.animl +3 -0
- package/modules/mix/capabilities/host-api-renderer/example.ani +16 -0
- package/modules/mix/capabilities/host-api-renderer/example.animl +3 -0
- package/modules/mix/manifest.source +36 -0
- package/modules/motion/capabilities/animation-cubic-value/example.ani +40 -0
- package/modules/motion/capabilities/animation-cubic-value/example.animl +9 -0
- package/modules/motion/capabilities/animation-elastic-in/example.ani +40 -0
- package/modules/motion/capabilities/animation-elastic-in/example.animl +9 -0
- package/modules/motion/capabilities/animation-elastic-in-out/example.ani +40 -0
- package/modules/motion/capabilities/animation-elastic-in-out/example.animl +9 -0
- package/modules/motion/capabilities/animation-host-loop-override/example.ani +28 -0
- package/modules/motion/capabilities/animation-host-loop-override/example.animl +6 -0
- package/modules/motion/capabilities/animation-host-play-pause-stop/example.ani +28 -0
- package/modules/motion/capabilities/animation-host-play-pause-stop/example.animl +6 -0
- package/modules/motion/capabilities/animation-keys-reference/example.ani +55 -0
- package/modules/motion/capabilities/animation-keys-reference/example.animl +12 -0
- package/modules/motion/capabilities/animation-keys-signed-integer/example.ani +46 -0
- package/modules/motion/capabilities/animation-keys-signed-integer/example.animl +11 -0
- package/modules/motion/capabilities/animation-keys-string/example.ani +46 -0
- package/modules/motion/capabilities/animation-keys-string/example.animl +11 -0
- package/modules/motion/capabilities/animation-keys-unsigned-integer/example.ani +46 -0
- package/modules/motion/capabilities/animation-keys-unsigned-integer/example.animl +11 -0
- package/modules/motion/capabilities/animation-mix-discrete/example.ani +38 -0
- package/modules/motion/capabilities/animation-mix-discrete/example.animl +9 -0
- package/modules/motion/capabilities/animation-mix-numeric/example.ani +42 -0
- package/modules/motion/capabilities/animation-mix-numeric/example.animl +11 -0
- package/modules/motion/capabilities/animation-quantized-playback/example.ani +41 -0
- package/modules/motion/capabilities/animation-quantized-playback/example.animl +9 -0
- package/modules/motion/capabilities/animation-reverse/example.ani +47 -0
- package/modules/motion/capabilities/animation-reverse/example.animl +12 -0
- package/modules/motion/capabilities/animation-seek/example.ani +28 -0
- package/modules/motion/capabilities/animation-seek/example.animl +6 -0
- package/modules/motion/capabilities/animation-work-area/example.ani +42 -0
- package/modules/motion/capabilities/animation-work-area/example.animl +9 -0
- package/modules/motion/capabilities/data-bind-keyframes/example.ani +52 -0
- package/modules/motion/capabilities/data-bind-keyframes/example.animl +15 -0
- package/modules/motion/capabilities/host-api-animation-metadata/example.ani +31 -0
- package/modules/motion/capabilities/host-api-animation-metadata/example.animl +6 -0
- package/modules/motion/capabilities/host-api-low-level-animation/example.ani +28 -0
- package/modules/motion/capabilities/host-api-low-level-animation/example.animl +6 -0
- package/modules/motion/capabilities/host-api-low-level-scene-query/example.ani +41 -0
- package/modules/motion/capabilities/host-api-low-level-scene-query/example.animl +9 -0
- package/modules/motion/capabilities/host-api-multi-state-machine/example.ani +62 -0
- package/modules/motion/capabilities/host-api-multi-state-machine/example.animl +22 -0
- package/modules/motion/capabilities/host-api-scrub/example.ani +29 -0
- package/modules/motion/capabilities/host-api-scrub/example.animl +6 -0
- package/modules/motion/capabilities/state-machine-exit-time-seconds/example.ani +60 -0
- package/modules/motion/capabilities/state-machine-exit-time-seconds/example.animl +15 -0
- package/modules/motion/capabilities/transforms-origin-animation/example.ani +45 -0
- package/modules/motion/capabilities/transforms-origin-animation/example.animl +10 -0
- package/modules/motion/examples/README.md +42 -0
- package/modules/motion/examples/data-keys.ani +52 -0
- package/modules/motion/examples/data-keys.animl +15 -0
- package/modules/motion/examples/evidence/hover.jsonl +8 -0
- package/modules/motion/examples/evidence/phase.jsonl +9 -0
- package/modules/motion/examples/evidence/reduced.jsonl +6 -0
- package/modules/motion/examples/evidence/rest.jsonl +1 -0
- package/modules/motion/examples/evidence/spin.jsonl +6 -0
- package/modules/motion/examples/evidence/stop.jsonl +10 -0
- package/modules/motion/examples/evidence/sweep.jsonl +26 -0
- package/modules/motion/examples/scripts/hover.txt +21 -0
- package/modules/motion/examples/scripts/phase.txt +28 -0
- package/modules/motion/examples/scripts/reduced.txt +16 -0
- package/modules/motion/examples/scripts/rest.txt +2 -0
- package/modules/motion/examples/scripts/spin.txt +14 -0
- package/modules/motion/examples/scripts/stop.txt +26 -0
- package/modules/motion/examples/scripts/sweep.txt +71 -0
- package/modules/motion/examples/sync.ani +272 -0
- package/modules/motion/examples/sync.animl +64 -0
- package/modules/motion/examples/verify.py +71 -0
- package/modules/motion/manifest.source +67 -0
- package/modules/nested/SPEC.md +79 -0
- package/modules/nested/capabilities/components-leaf-fit/example.ani +50 -0
- package/modules/nested/capabilities/components-leaf-fit/example.animl +9 -0
- package/modules/nested/capabilities/components-nested-quantize/example.ani +65 -0
- package/modules/nested/capabilities/components-nested-quantize/example.animl +15 -0
- package/modules/nested/capabilities/components-nested-remap/example.ani +65 -0
- package/modules/nested/capabilities/components-nested-remap/example.animl +15 -0
- package/modules/nested/capabilities/components-stateful-output/example.ani +51 -0
- package/modules/nested/capabilities/components-stateful-output/example.animl +12 -0
- package/modules/nested/capabilities/data-property-artboard/example.ani +63 -0
- package/modules/nested/capabilities/data-property-artboard/example.animl +14 -0
- package/modules/nested/capabilities/state-machine-literal-artboard/example.ani +76 -0
- package/modules/nested/capabilities/state-machine-literal-artboard/example.animl +19 -0
- package/modules/nested/examples/component-guard.ani +76 -0
- package/modules/nested/examples/component-guard.animl +19 -0
- package/modules/nested/examples/controls.ani +100 -0
- package/modules/nested/examples/controls.animl +21 -0
- package/modules/nested/manifest.source +39 -0
- package/modules/paint/capabilities/drawing-paint-gradient-angular/example.ani +33 -0
- package/modules/paint/capabilities/drawing-paint-gradient-angular/example.animl +8 -0
- package/modules/paint/capabilities/drawing-paint-gradient-transform/example.ani +34 -0
- package/modules/paint/capabilities/drawing-paint-gradient-transform/example.animl +8 -0
- package/modules/paint/capabilities/drawing-paint-image-fit-resize/example.ani +49 -0
- package/modules/paint/capabilities/drawing-paint-image-fit-resize/example.animl +12 -0
- package/modules/paint/capabilities/drawing-paint-image-wrap-clamp/example.ani +50 -0
- package/modules/paint/capabilities/drawing-paint-image-wrap-clamp/example.animl +12 -0
- package/modules/paint/capabilities/drawing-paint-image-wrap-mirror/example.ani +50 -0
- package/modules/paint/capabilities/drawing-paint-image-wrap-mirror/example.animl +12 -0
- package/modules/paint/capabilities/drawing-paint-image-wrap-repeat/example.ani +50 -0
- package/modules/paint/capabilities/drawing-paint-image-wrap-repeat/example.animl +12 -0
- package/modules/paint/manifest.source +77 -0
- package/modules/pathfollow/capabilities/rigging-follow-path/example.ani +16 -0
- package/modules/pathfollow/capabilities/rigging-follow-path/example.animl +7 -0
- package/modules/pathfollow/capabilities/rigging-list-follow-path/example.ani +16 -0
- package/modules/pathfollow/capabilities/rigging-list-follow-path/example.animl +9 -0
- package/modules/pathfollow/examples/orbit.ani +130 -0
- package/modules/pathfollow/examples/orbit.animl +17 -0
- package/modules/pathfollow/manifest.source +35 -0
- package/modules/posepad/examples/look.ani +124 -0
- package/modules/posepad/examples/look.animl +22 -0
- package/modules/posepad/manifest.source +43 -0
- package/modules/rig/AUTHORING.md +24 -0
- package/modules/rig/capabilities/rigging-bone-chains/example.ani +61 -0
- package/modules/rig/capabilities/rigging-bone-chains/example.animl +22 -0
- package/modules/rig/capabilities/rigging-constraint-order/example.ani +123 -0
- package/modules/rig/capabilities/rigging-constraint-order/example.animl +33 -0
- package/modules/rig/capabilities/rigging-constraint-strength/example.ani +105 -0
- package/modules/rig/capabilities/rigging-constraint-strength/example.animl +28 -0
- package/modules/rig/capabilities/rigging-distance-closer/example.ani +160 -0
- package/modules/rig/capabilities/rigging-distance-closer/example.animl +44 -0
- package/modules/rig/capabilities/rigging-distance-exact/example.ani +160 -0
- package/modules/rig/capabilities/rigging-distance-exact/example.animl +44 -0
- package/modules/rig/capabilities/rigging-distance-further/example.ani +160 -0
- package/modules/rig/capabilities/rigging-distance-further/example.animl +44 -0
- package/modules/rig/capabilities/rigging-root-bones/example.ani +63 -0
- package/modules/rig/capabilities/rigging-root-bones/example.animl +22 -0
- package/modules/rig/capabilities/rigging-rotation/example.ani +143 -0
- package/modules/rig/capabilities/rigging-rotation/example.animl +32 -0
- package/modules/rig/capabilities/rigging-scale/example.ani +144 -0
- package/modules/rig/capabilities/rigging-scale/example.animl +32 -0
- package/modules/rig/capabilities/rigging-transform/example.ani +132 -0
- package/modules/rig/capabilities/rigging-transform/example.animl +30 -0
- package/modules/rig/capabilities/rigging-translation/example.ani +248 -0
- package/modules/rig/capabilities/rigging-translation/example.animl +54 -0
- package/modules/rig/examples/arm.ani +120 -0
- package/modules/rig/examples/arm.animl +29 -0
- package/modules/rig/manifest.source +55 -0
- package/modules/scroll/capabilities/rigging-scroll/example.ani +16 -0
- package/modules/scroll/capabilities/rigging-scroll/example.animl +11 -0
- package/modules/scroll/examples/list.ani +115 -0
- package/modules/scroll/examples/list.animl +15 -0
- package/modules/scroll/manifest.source +31 -0
- package/modules/skin/capabilities/rigging-cubic-weights/example.ani +66 -0
- package/modules/skin/capabilities/rigging-cubic-weights/example.animl +17 -0
- package/modules/skin/capabilities/rigging-skin/example.ani +165 -0
- package/modules/skin/capabilities/rigging-skin/example.animl +33 -0
- package/modules/skin/capabilities/rigging-weights/example.ani +147 -0
- package/modules/skin/capabilities/rigging-weights/example.animl +37 -0
- package/modules/skin/examples/sleeve.ani +91 -0
- package/modules/skin/examples/sleeve.animl +14 -0
- package/modules/skin/manifest.source +41 -0
- package/modules/surface/manifest.source +52 -0
- package/modules/text/SPEC.md +269 -0
- package/modules/text/capabilities/data-bind-text/example.ani +63 -0
- package/modules/text/capabilities/data-bind-text/example.animl +8 -0
- package/modules/text/capabilities/data-property-text-style/example.ani +63 -0
- package/modules/text/capabilities/data-property-text-style/example.animl +8 -0
- package/modules/text/capabilities/host-api-nested-text-run/example.ani +44 -0
- package/modules/text/capabilities/host-api-nested-text-run/example.animl +10 -0
- package/modules/text/capabilities/host-api-text-run/example.ani +27 -0
- package/modules/text/capabilities/host-api-text-run/example.animl +6 -0
- package/modules/text/capabilities/text-fit-baseline/example.ani +30 -0
- package/modules/text/capabilities/text-fit-baseline/example.animl +6 -0
- package/modules/text/capabilities/text-fit-layout-measure/example.ani +30 -0
- package/modules/text/capabilities/text-fit-layout-measure/example.animl +6 -0
- package/modules/text/capabilities/text-flatten/example.ani +16 -0
- package/modules/text/capabilities/text-flatten/example.animl +6 -0
- package/modules/text/capabilities/text-font-size/example.ani +59 -0
- package/modules/text/capabilities/text-font-size/example.animl +15 -0
- package/modules/text/capabilities/text-horizontal-alignment/example.ani +28 -0
- package/modules/text/capabilities/text-horizontal-alignment/example.animl +6 -0
- package/modules/text/capabilities/text-line-spacing/example.ani +28 -0
- package/modules/text/capabilities/text-line-spacing/example.animl +6 -0
- package/modules/text/capabilities/text-origin/example.ani +32 -0
- package/modules/text/capabilities/text-origin/example.animl +6 -0
- package/modules/text/capabilities/text-overflow-clipped/example.ani +29 -0
- package/modules/text/capabilities/text-overflow-clipped/example.animl +6 -0
- package/modules/text/capabilities/text-overflow-ellipsis/example.ani +29 -0
- package/modules/text/capabilities/text-overflow-ellipsis/example.animl +6 -0
- package/modules/text/capabilities/text-overflow-fit/example.ani +29 -0
- package/modules/text/capabilities/text-overflow-fit/example.animl +6 -0
- package/modules/text/capabilities/text-overflow-fit-font-size/example.ani +29 -0
- package/modules/text/capabilities/text-overflow-fit-font-size/example.animl +6 -0
- package/modules/text/capabilities/text-overflow-hidden/example.ani +29 -0
- package/modules/text/capabilities/text-overflow-hidden/example.animl +7 -0
- package/modules/text/capabilities/text-overflow-visible/example.ani +29 -0
- package/modules/text/capabilities/text-overflow-visible/example.animl +6 -0
- package/modules/text/capabilities/text-paint/example.ani +30 -0
- package/modules/text/capabilities/text-paint/example.animl +6 -0
- package/modules/text/capabilities/text-paragraph-spacing/example.ani +28 -0
- package/modules/text/capabilities/text-paragraph-spacing/example.animl +7 -0
- package/modules/text/capabilities/text-rich-runs/example.ani +34 -0
- package/modules/text/capabilities/text-rich-runs/example.animl +8 -0
- package/modules/text/capabilities/text-run-list/example.ani +59 -0
- package/modules/text/capabilities/text-run-list/example.animl +7 -0
- package/modules/text/capabilities/text-script-hit-testing/example.ani +47 -0
- package/modules/text/capabilities/text-script-hit-testing/example.animl +10 -0
- package/modules/text/capabilities/text-sizing-auto-height/example.ani +28 -0
- package/modules/text/capabilities/text-sizing-auto-height/example.animl +6 -0
- package/modules/text/capabilities/text-sizing-auto-width/example.ani +27 -0
- package/modules/text/capabilities/text-sizing-auto-width/example.animl +6 -0
- package/modules/text/capabilities/text-sizing-fixed/example.ani +26 -0
- package/modules/text/capabilities/text-sizing-fixed/example.animl +6 -0
- package/modules/text/capabilities/text-style-reference/example.ani +31 -0
- package/modules/text/capabilities/text-style-reference/example.animl +7 -0
- package/modules/text/capabilities/text-tracking/example.ani +28 -0
- package/modules/text/capabilities/text-tracking/example.animl +6 -0
- package/modules/text/capabilities/text-vertical-alignment/example.ani +28 -0
- package/modules/text/capabilities/text-vertical-alignment/example.animl +6 -0
- package/modules/text/capabilities/text-vertical-trim/example.ani +29 -0
- package/modules/text/capabilities/text-vertical-trim/example.animl +6 -0
- package/modules/text/capabilities/text-word-break/example.ani +28 -0
- package/modules/text/capabilities/text-word-break/example.animl +6 -0
- package/modules/text/capabilities/text-wrap/example.ani +28 -0
- package/modules/text/capabilities/text-wrap/example.animl +6 -0
- package/modules/text/manifest.source +92 -0
- package/modules/text-effects/SPEC.md +134 -0
- package/modules/text-effects/capabilities/text-background/example.ani +32 -0
- package/modules/text-effects/capabilities/text-background/example.animl +7 -0
- package/modules/text-effects/capabilities/text-modifier-follow-path/example.ani +34 -0
- package/modules/text-effects/capabilities/text-modifier-follow-path/example.animl +8 -0
- package/modules/text-effects/capabilities/text-modifier-opacity/example.ani +34 -0
- package/modules/text-effects/capabilities/text-modifier-opacity/example.animl +8 -0
- package/modules/text-effects/capabilities/text-modifier-origin/example.ani +36 -0
- package/modules/text-effects/capabilities/text-modifier-origin/example.animl +8 -0
- package/modules/text-effects/capabilities/text-modifier-rotation/example.ani +34 -0
- package/modules/text-effects/capabilities/text-modifier-rotation/example.animl +8 -0
- package/modules/text-effects/capabilities/text-modifier-scale/example.ani +35 -0
- package/modules/text-effects/capabilities/text-modifier-scale/example.animl +8 -0
- package/modules/text-effects/capabilities/text-modifier-translation/example.ani +35 -0
- package/modules/text-effects/capabilities/text-modifier-translation/example.animl +8 -0
- package/modules/text-effects/capabilities/text-modifier-variation/example.ani +36 -0
- package/modules/text-effects/capabilities/text-modifier-variation/example.animl +8 -0
- package/modules/text-effects/capabilities/text-range-combine/example.ani +47 -0
- package/modules/text-effects/capabilities/text-range-combine/example.animl +11 -0
- package/modules/text-effects/capabilities/text-range-easing/example.ani +38 -0
- package/modules/text-effects/capabilities/text-range-easing/example.animl +8 -0
- package/modules/text-effects/capabilities/text-range-falloff/example.ani +37 -0
- package/modules/text-effects/capabilities/text-range-falloff/example.animl +8 -0
- package/modules/text-effects/capabilities/text-range-run/example.ani +37 -0
- package/modules/text-effects/capabilities/text-range-run/example.animl +8 -0
- package/modules/text-effects/capabilities/text-range-units/example.ani +37 -0
- package/modules/text-effects/capabilities/text-range-units/example.animl +8 -0
- package/modules/text-effects/capabilities/text-script-direction/example.ani +30 -0
- package/modules/text-effects/capabilities/text-script-direction/example.animl +7 -0
- package/modules/text-effects/examples/title-wave.ani +68 -0
- package/modules/text-effects/examples/title-wave.animl +15 -0
- package/modules/text-effects/manifest.source +58 -0
- package/modules/text-input/SPEC.md +189 -0
- package/modules/text-input/capabilities/text-input-placeholder/example.ani +16 -0
- package/modules/text-input/capabilities/text-input-placeholder/example.animl +13 -0
- package/modules/text-input/capabilities/text-input-selection/example.ani +66 -0
- package/modules/text-input/capabilities/text-input-selection/example.animl +13 -0
- package/modules/text-input/examples/search-card.ani +195 -0
- package/modules/text-input/examples/search-card.animl +49 -0
- package/modules/text-input/examples/search.txt +6 -0
- package/modules/text-input/manifest.source +79 -0
- package/modules/tooling/capabilities/tooling-benchmark/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-benchmark/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-data-breakpoints/example.ani +75 -0
- package/modules/tooling/capabilities/tooling-data-breakpoints/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-data-debugger/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-data-debugger/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-command-session/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-command-session/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-data/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-data/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-data-dump/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-data-dump/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-data-filter/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-data-filter/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-data-stream/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-data-stream/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-gamepad/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-gamepad/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-key/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-key/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-pointer/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-pointer/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-touch/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-touch/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-headless-wheel/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-headless-wheel/example.animl +13 -0
- package/modules/tooling/capabilities/tooling-seed/example.ani +76 -0
- package/modules/tooling/capabilities/tooling-seed/example.animl +21 -0
- package/modules/tooling/capabilities/tooling-test-assertions/example.ani +16 -0
- package/modules/tooling/capabilities/tooling-test-assertions/example.animl +13 -0
- package/modules/tooling/examples/agent-tools.ani +75 -0
- package/modules/tooling/examples/agent-tools.animl +13 -0
- package/modules/tooling/manifest.source +15 -0
- package/package.json +58 -4
- package/packages/cli/package.json +4 -0
- package/packages/cli/src/asset-info.d.ts +8 -0
- package/packages/cli/src/asset-info.js +122 -0
- package/packages/cli/src/assets.d.ts +8 -0
- package/packages/cli/src/assets.js +66 -0
- package/packages/cli/src/audio.d.ts +3 -0
- package/packages/cli/src/audio.js +77 -0
- package/packages/cli/src/bench.d.ts +32 -0
- package/packages/cli/src/bench.js +46 -0
- package/packages/cli/src/build-assets.d.ts +10 -0
- package/packages/cli/src/build-assets.js +139 -0
- package/packages/cli/src/bundle.d.ts +7 -0
- package/packages/cli/src/bundle.js +53 -0
- package/packages/cli/src/cli.d.ts +19 -0
- package/packages/cli/src/cli.js +1532 -0
- package/packages/cli/src/commands.d.ts +47 -0
- package/packages/cli/src/commands.js +608 -0
- package/packages/cli/src/compare-png.d.ts +21 -0
- package/packages/cli/src/compare-png.js +39 -0
- package/packages/cli/src/compare.d.ts +7 -0
- package/packages/cli/src/compare.js +84 -0
- package/packages/cli/src/completion.d.ts +2 -0
- package/packages/cli/src/completion.js +153 -0
- package/packages/cli/src/discover/api.d.ts +55 -0
- package/packages/cli/src/discover/api.js +185 -0
- package/packages/cli/src/discover/build.d.ts +14 -0
- package/packages/cli/src/discover/build.js +590 -0
- package/packages/cli/src/discover/synonyms.d.ts +1 -0
- package/packages/cli/src/discover/synonyms.js +68 -0
- package/packages/cli/src/docs-view.d.ts +4 -0
- package/packages/cli/src/docs-view.js +279 -0
- package/packages/cli/src/doctor.d.ts +11 -0
- package/packages/cli/src/doctor.js +151 -0
- package/packages/cli/src/encode.d.ts +9 -0
- package/packages/cli/src/encode.js +100 -0
- package/packages/cli/src/font/README.md +45 -0
- package/packages/cli/src/font/binary.d.ts +8 -0
- package/packages/cli/src/font/binary.js +54 -0
- package/packages/cli/src/font/browser.d.ts +4 -0
- package/packages/cli/src/font/browser.js +54 -0
- package/packages/cli/src/font/cff.d.ts +6 -0
- package/packages/cli/src/font/cff.js +154 -0
- package/packages/cli/src/font/container.d.ts +5 -0
- package/packages/cli/src/font/container.js +97 -0
- package/packages/cli/src/font/document.d.ts +28 -0
- package/packages/cli/src/font/document.js +143 -0
- package/packages/cli/src/font/features.d.ts +1 -0
- package/packages/cli/src/font/features.js +1 -0
- package/packages/cli/src/font/flatten.d.ts +5 -0
- package/packages/cli/src/font/flatten.js +146 -0
- package/packages/cli/src/font/import.d.ts +12 -0
- package/packages/cli/src/font/import.js +92 -0
- package/packages/cli/src/font/index.d.ts +8 -0
- package/packages/cli/src/font/index.js +27 -0
- package/packages/cli/src/font/measure.d.ts +37 -0
- package/packages/cli/src/font/measure.js +131 -0
- package/packages/cli/src/font/reader.d.ts +13 -0
- package/packages/cli/src/font/reader.js +26 -0
- package/packages/cli/src/font/subset.d.ts +6 -0
- package/packages/cli/src/font/subset.js +87 -0
- package/packages/cli/src/font/svg.d.ts +3 -0
- package/packages/cli/src/font/svg.js +109 -0
- package/packages/cli/src/font/truetype.d.ts +3 -0
- package/packages/cli/src/font/truetype.js +38 -0
- package/packages/cli/src/font/variation.d.ts +1 -0
- package/packages/cli/src/font/variation.js +1 -0
- package/packages/cli/src/font/woff2.d.ts +3 -0
- package/packages/cli/src/font/woff2.js +166 -0
- package/packages/cli/src/glyphs.d.ts +5 -0
- package/packages/cli/src/glyphs.js +59 -0
- package/packages/cli/src/help.d.ts +5 -0
- package/packages/cli/src/help.js +149 -0
- package/packages/cli/src/html.d.ts +5 -0
- package/packages/cli/src/html.js +119 -0
- package/packages/cli/src/import-lottie.d.ts +2 -0
- package/packages/cli/src/import-lottie.js +274 -0
- package/packages/cli/src/import-svg.d.ts +8 -0
- package/packages/cli/src/import-svg.js +300 -0
- package/packages/cli/src/index.d.ts +1 -0
- package/packages/cli/src/index.js +4 -0
- package/packages/cli/src/inspect-summary.d.ts +7 -0
- package/packages/cli/src/inspect-summary.js +158 -0
- package/packages/cli/src/library.d.ts +6 -0
- package/packages/cli/src/library.js +67 -0
- package/packages/cli/src/locate.d.ts +13 -0
- package/packages/cli/src/locate.js +118 -0
- package/packages/cli/src/main.d.ts +2 -0
- package/packages/cli/src/main.js +13 -0
- package/packages/cli/src/playground.d.ts +11 -0
- package/packages/cli/src/playground.js +169 -0
- package/packages/cli/src/project.d.ts +15 -0
- package/packages/cli/src/project.js +160 -0
- package/packages/cli/src/render-options.d.ts +6 -0
- package/packages/cli/src/render-options.js +40 -0
- package/packages/cli/src/render.d.ts +30 -0
- package/packages/cli/src/render.js +90 -0
- package/packages/cli/src/scaffold.d.ts +17 -0
- package/packages/cli/src/scaffold.js +96 -0
- package/packages/cli/src/schema-view.d.ts +9 -0
- package/packages/cli/src/schema-view.js +493 -0
- package/packages/cli/src/session.d.ts +8 -0
- package/packages/cli/src/session.js +96 -0
- package/packages/cli/src/suggest.d.ts +3 -0
- package/packages/cli/src/suggest.js +62 -0
- package/packages/cli/src/video.d.ts +2 -0
- package/packages/cli/src/video.js +73 -0
- package/packages/core/src/script.source +897 -0
- package/spec/ANIML.md +598 -0
- package/spec/MODULES.md +406 -0
- package/spec/SPEC.md +4083 -0
package/spec/SPEC.md
ADDED
|
@@ -0,0 +1,4083 @@
|
|
|
1
|
+
# The `.ani` format — 0.1
|
|
2
|
+
|
|
3
|
+
Status: **0.1, frozen for the first implementation.** Sections marked *(normative)* define behaviour
|
|
4
|
+
every runtime must reproduce exactly; the conformance suite in `spec/conformance/` is the tie-breaker
|
|
5
|
+
when prose and tests disagree. §12 records the decisions taken for 0.1 and what is deferred.
|
|
6
|
+
|
|
7
|
+
`examples/bell.ani` is the reference document. Every construct below appears in it unless marked
|
|
8
|
+
*(not in bell)*.
|
|
9
|
+
|
|
10
|
+
## 1. Goals
|
|
11
|
+
|
|
12
|
+
1. **Agent-authored.** A language model writes and edits `.ani` files directly as text, checks them
|
|
13
|
+
with `anima validate`, and sees them with `anima sample` / `anima render`. Every mistake an author
|
|
14
|
+
can make is either impossible to express or a load error naming the JSON pointer and the fix.
|
|
15
|
+
2. **Icon-scale and Glyphy-native.** Coordinates are glyph grid units. Glyphy's path data is embedded
|
|
16
|
+
byte for byte and animated in place: nodes pick subpaths and declare pivots instead of rewriting
|
|
17
|
+
geometry. The resting frame is identical to the static Glyphy SVG.
|
|
18
|
+
3. **Deterministic.** One `step(dt)` function, fully specified, so a headless run and a browser run of
|
|
19
|
+
the same inputs produce the same values.
|
|
20
|
+
4. **Small and web-first.** The reference runtime renders retained SVG DOM, themes with CSS, sleeps
|
|
21
|
+
when nothing moves, and is accessible by default.
|
|
22
|
+
|
|
23
|
+
Non-goals for 0.x: bones/meshes, text layout, scripting, nested artboards/components, raster images,
|
|
24
|
+
a binary encoding, importing `.riv`.
|
|
25
|
+
|
|
26
|
+
## 2. Document
|
|
27
|
+
|
|
28
|
+
A `.ani` file is UTF-8 JSON (RFC 8259) with media type `application/vnd.anima+json`. Keys named
|
|
29
|
+
`$comment` are allowed on any object and ignored. `$schema` is allowed at the top level.
|
|
30
|
+
|
|
31
|
+
| Key | Type | Required | Meaning |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| `anima` | `"0.1"` | yes | Format version. A 0.1 runtime loads exactly `"0.1"`; before 1.0 every minor version may change the format, so any other value is `version-unsupported`. |
|
|
34
|
+
| `name` | kebab-case string | yes | Identifier, `^[a-z0-9]+(-[a-z0-9]+)*$`. Must not end in `-<digits>` (Glyphy reserves that for variants). |
|
|
35
|
+
| `title` | string | no | Human name. |
|
|
36
|
+
| `grid` | number > 0 | yes | Glyph grid size in units (Glyphy's `style.grid`, usually 20). |
|
|
37
|
+
| `viewBox` | `[x, y, w, h]` | no | Drawing area in grid units, `w, h > 0`; defaults to `[0, 0, grid, grid]`. May be larger than the grid so motion has room (bell: `[-2, -2, 24, 24]`). |
|
|
38
|
+
| `requires` | string[] | no | Optional features the file uses. A runtime that lacks one refuses the file (`unknown-feature`). 0.1 defines `"knockout"`; listing it is allowed but not needed. |
|
|
39
|
+
| `meta` | object | no | Free-form metadata (strings, numbers, booleans, arrays and objects of them) that runtimes ignore and tools preserve, e.g. Glyphy's `concept`, `kind`, `stroke`. |
|
|
40
|
+
| `colors` | `{slot: color}` | no | Colour slots, see §6. |
|
|
41
|
+
| `glyphs` | `{name: Glyph}` | no | Embedded path data, see §3. |
|
|
42
|
+
| `a11y` | object | no | See §10. |
|
|
43
|
+
| `inputs` | `{name: Input}` | no | See §8. |
|
|
44
|
+
| `nodes` | Node[] | yes | Scene tree, see §4. |
|
|
45
|
+
| `hit` | `{ "circle": [cx, cy, r] }` or `{ "rect": [x, y, w, h] }` | no | Hit area of the whole icon, see §9. Default: the `viewBox`. |
|
|
46
|
+
| `timelines` | `{name: Timeline}` | no | See §5. |
|
|
47
|
+
| `machine` | object | no | See §8. |
|
|
48
|
+
| `listeners` | Listener[] | no | See §9. |
|
|
49
|
+
|
|
50
|
+
**Names.** Node ids, input, slot, glyph, timeline, layer, state and event names all match
|
|
51
|
+
`^[a-z][a-zA-Z0-9-]*$` (`bad-name`). Names starting with `@` are reserved for built-ins.
|
|
52
|
+
|
|
53
|
+
**Order.** JSON member order is significant in exactly one place: `machine.layers` (layer order
|
|
54
|
+
decides who writes last, §7). Tools must preserve member order. Everything else that is ordered is
|
|
55
|
+
an array.
|
|
56
|
+
|
|
57
|
+
**Strictness** *(normative)*. Unknown keys, unknown node types, unknown properties, duplicate ids,
|
|
58
|
+
dangling references, type mismatches (a colour keyframe on a number property), non-increasing
|
|
59
|
+
keyframe times and unreachable references are **load errors**. A runtime never partially loads a
|
|
60
|
+
document: it either loads with no errors or reports every error it found and loads nothing.
|
|
61
|
+
Every diagnostic has a `code`, a `severity` (`error` | `warning`), a JSON `pointer` (RFC 6901) to the
|
|
62
|
+
offending value, a `message` and, where possible, a `hint` (`did you mean "rotate"?`). Warnings never
|
|
63
|
+
stop a load. The codes are listed in §11.3. A size-optimised runtime build may drop message and
|
|
64
|
+
hint text (the message is then the code) and skip computing warnings; it still reports every error
|
|
65
|
+
with its code, severity and pointer, and `anima validate` gives the full text. Duplicate object keys are invalid JSON for `.ani`;
|
|
66
|
+
`anima validate` reports them (`duplicate-key`), while runtimes built on a standard JSON parser may
|
|
67
|
+
keep the last value. Keys reserved for a later version (§12) are reported as `reserved`, not as
|
|
68
|
+
unknown keys, so the message can say which version brings them.
|
|
69
|
+
|
|
70
|
+
For an unavailable optional feature, verbose diagnostics include the web module import,
|
|
71
|
+
for example `import '@anima-format/web/machine' before loading this document`. CLI tools
|
|
72
|
+
register the full feature set; an accepted CLI document still needs its declared modules in the browser.
|
|
73
|
+
|
|
74
|
+
A document declaring `components` may have an empty `nodes` array: it is a library container
|
|
75
|
+
whose exported components can be instantiated as host roots. Ordinary documents need a node.
|
|
76
|
+
|
|
77
|
+
**Limits.** Loaders refuse documents beyond these limits with `limit-exceeded`: 1 MiB of text, nesting
|
|
78
|
+
depth 32 nodes, 96 levels of JSON nesting anywhere (free-form values such as `meta` included),
|
|
79
|
+
2 048 nodes, 20 000 keyframes in total, 64 layers, 256 states per layer.
|
|
80
|
+
|
|
81
|
+
**Units** *(normative)*: lengths in grid units, y down; time in **seconds**; angles in **degrees**,
|
|
82
|
+
positive clockwise (y down), 0° along +x; opacity, scale and blend weights are plain numbers
|
|
83
|
+
(1 = 100 %). There are no frames or radians. The optional paint module adds percentage dash lengths (see its chapter).
|
|
84
|
+
|
|
85
|
+
## 3. Glyphs
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
"glyphs": { "bell": { "source": "glyphy", "sha256": "…", "d": "M9.493 4.473C…Z" } }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
| Key | Required | Meaning |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `d` | yes | SVG path data. Glyphy writes absolute `M L C Z` only; runtimes accept the full SVG 1.1 path grammar (all commands, relative forms, implicit repeats, arcs) but tools emit Glyphy's subset. |
|
|
94
|
+
| `source` | no | Free text naming where the data came from (`"glyphy"`). |
|
|
95
|
+
| `pick` | no | Glyphy's variant stem (`"bell-1"`). |
|
|
96
|
+
| `sha256` | no | Hex SHA-256 of the `d` string's UTF-8 bytes. Tools use it to detect a re-traced glyph; a mismatch is the warning `hash-mismatch` from `anima validate`, not a load error. |
|
|
97
|
+
|
|
98
|
+
Subpaths are numbered from 0 in the order their `M`/`m` commands appear. A path node takes
|
|
99
|
+
`"subpaths": [i, …]` (distinct, in range, any order — they are always drawn in `d` order); omitted
|
|
100
|
+
means all.
|
|
101
|
+
|
|
102
|
+
## 4. Nodes
|
|
103
|
+
|
|
104
|
+
Every node has `id` (unique across the document) and `type`. Paint order is **document order, later
|
|
105
|
+
on top** (SVG order — the reverse of Rive).
|
|
106
|
+
|
|
107
|
+
Common properties (all animatable unless noted):
|
|
108
|
+
|
|
109
|
+
| Property | Type | Default | Meaning |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `x`, `y` | number | 0 | Translation. |
|
|
112
|
+
| `rotate` | number | 0 | Degrees about `pivot`. |
|
|
113
|
+
| `scale` | number | 1 | Uniform scale about `pivot`; multiplies `scaleX`/`scaleY`. |
|
|
114
|
+
| `scaleX`, `scaleY` | number | 1 | Axis scales about `pivot`. |
|
|
115
|
+
| `opacity` | number | 1 | Group opacity, clamped to 0..1 when resolved. |
|
|
116
|
+
| `visible` | boolean | true | Hold-only. An invisible node draws nothing, hits nothing and cuts nothing. |
|
|
117
|
+
| `pivot` | `[x, y]` | `[0, 0]` | **Not animatable.** Point the transform turns and scales about. |
|
|
118
|
+
|
|
119
|
+
**Transform** *(normative)*: children are authored in the same grid coordinates as their parent (no
|
|
120
|
+
re-origining, so Glyphy geometry is used as-is). A node's local matrix is
|
|
121
|
+
|
|
122
|
+
`T(x, y) · T(px, py) · R(rotate) · S(scale·scaleX, scale·scaleY) · T(−px, −py)`
|
|
123
|
+
|
|
124
|
+
and its world matrix is `parent.world · local` (the root's parent is the identity in `viewBox`
|
|
125
|
+
coordinates). Because nothing is re-origined, the pivot is the same point in the node's and its
|
|
126
|
+
parent's coordinates at rest.
|
|
127
|
+
|
|
128
|
+
Types:
|
|
129
|
+
|
|
130
|
+
| `type` | Own keys | Notes |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `group` | `children` (Node[]) | |
|
|
133
|
+
| `path` | `glyph` + optional `subpaths`, **or** inline `d` | Exactly one of `glyph` and `d` (`path-source`). |
|
|
134
|
+
| `circle` | `center` `[x, y]`, `r` (animatable, ≥ 0) | Its outline starts at angle 0 and runs in the direction of increasing angle (matters for trims). |
|
|
135
|
+
| `rect` | `x0`, `y0`, `w`, `h`, `rx` (`w`, `h`, `rx` animatable, ≥ 0; `rx` defaults to 0) | *(not in bell)* Outline starts at `(x0 + rx, y0)`, clockwise. |
|
|
136
|
+
| `arc` | `center`, `r`, `from`, `to` (`r`, `from`, `to` animatable) | Open circular arc, angles in degrees, drawn from `from` to `to` in the direction of increasing angle. At rest `0 < to − from ≤ 360` (`bad-value`). Stroke only (`arc-fill`). |
|
|
137
|
+
|
|
138
|
+
Paint (on `path`, `circle`, `rect`, `arc`):
|
|
139
|
+
|
|
140
|
+
- `fill`: `{ "color": slot-or-color, "rule": "nonzero" | "evenodd" }` — `rule` defaults to `nonzero`;
|
|
141
|
+
Glyphy glyphs need `evenodd`.
|
|
142
|
+
- `stroke`: `{ "color", "width", "cap", "join", "trimStart", "trimEnd", "trimOffset" }`.
|
|
143
|
+
`width` defaults to 1, `cap` (`butt`|`round`|`square`) and `join` (`miter`|`round`|`bevel`) default
|
|
144
|
+
to **`round`** — Glyphy styles carry no cap or join, and the reference sets are round. Trims are
|
|
145
|
+
fractions of the outline's length: the visible part runs from `trimStart + trimOffset` to
|
|
146
|
+
`trimEnd + trimOffset`, wrapping around 1 (defaults 0, 1, 0; start and end clamp to 0..1 when
|
|
147
|
+
resolved). When `trimEnd − trimStart ≤ 0` nothing is stroked (no dots from round caps). Trims are
|
|
148
|
+
allowed on `circle`, `rect`, `arc` and single-subpath `path`s (`trim-multi-subpath`).
|
|
149
|
+
- Animatable paint addresses: `<id>.fill.color`, `<id>.stroke.color`, `<id>.stroke.width`,
|
|
150
|
+
`<id>.stroke.trimStart|trimEnd|trimOffset`.
|
|
151
|
+
- A painted node with neither `fill` nor `stroke` draws nothing (warning `no-paint`); it can still be
|
|
152
|
+
a listener target.
|
|
153
|
+
- **Stroke width scales with the world transform** *(normative)*, as SVG and Glyphy ink do (§12.1).
|
|
154
|
+
|
|
155
|
+
**Knockout** *(normative)*: `"knockout": w` (number ≥ 0) on a painted node cuts the node's painted
|
|
156
|
+
area — its fill area if it has a fill, plus its stroke area if it has a stroke — grown by `w` units
|
|
157
|
+
(as if stroked with width `2w`, round joins and caps), out of every **earlier sibling** in the same
|
|
158
|
+
parent, following the node's animated transform. The cut's strength is the node's own resolved
|
|
159
|
+
`opacity`; an invisible node cuts nothing. Later knockouts cut earlier knockout nodes too. This
|
|
160
|
+
replaces the "paper-coloured disc" trick, so badges work on any background. (bell: the
|
|
161
|
+
notification dot cuts the body and clapper.)
|
|
162
|
+
|
|
163
|
+
## 5. Timelines
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
"timelines": { "ring": { "duration": 1.4, "loop": "once", "reducedMotion": "end",
|
|
167
|
+
"tracks": { "swing.rotate": [[0, 0, "cubic(0.2, 0, 0.3, 1)"], [0.116667, 17.1887, "…"], [1.1, 0]] },
|
|
168
|
+
"events": [[0.05, "ding"]] } }
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- `duration` seconds (> 0). `loop`: `once` (default) | `loop` | `pingpong`. `reducedMotion`: §10.
|
|
172
|
+
- `tracks` maps a **property address** `"<node-id>.<property>"` to keyframes `[time, value, ease?]`
|
|
173
|
+
(at least one). Times strictly increase (`key-order`) and lie in `[0, duration]` (`key-range`).
|
|
174
|
+
The value type must match the property (number, colour string, boolean) (`value-type`).
|
|
175
|
+
- `events` *(not in bell)*: `[time, name]` pairs, `time` in `[0, duration]`, reported when playback
|
|
176
|
+
crosses them (§7).
|
|
177
|
+
|
|
178
|
+
**Clock boundaries** *(normative, every runtime)*. Time calculations use IEEE-754 binary64
|
|
179
|
+
seconds. Use the absolute constant `ε = 1e-9` seconds in the clock being compared (state,
|
|
180
|
+
fade, or unquantized timeline seconds, after speed/offset conversion). A clock `t` has
|
|
181
|
+
**reached** a boundary `b` iff `t ≥ b − ε`; it is strictly before it otherwise. This
|
|
182
|
+
applies to event crossings, key sampling, `@after`/`@end`, exit/exitFraction/exitNext,
|
|
183
|
+
fade completion (including entry carry), trigger-buffer expiry and settled detection.
|
|
184
|
+
A strict "later than" comparison means `!reached(b, t)` (`b < t − ε`). Never scale ε with
|
|
185
|
+
elapsed time, frame rate, speed or duration; never round the accumulated clock or `dt` to a time grid.
|
|
186
|
+
Carry remains `max(0, t − b)`, so early recognition introduces no negative target time.
|
|
187
|
+
Events, sampling, once completion and `exit`/`exitFraction` compare in source-timeline
|
|
188
|
+
seconds; `@after`, fades, buffer ages and armed `exitNext` state-time gates compare in
|
|
189
|
+
state/fade seconds. Authored times, input/value comparisons and validation ranges retain
|
|
190
|
+
their exact meaning.
|
|
191
|
+
|
|
192
|
+
For forward events use `!reached(previous, event) && reached(now, event)`; on entry replace
|
|
193
|
+
the first test with `reached(event, previous)`. For reverse events use
|
|
194
|
+
`!reached(event, previous) && reached(event, now)`, likewise including the previous bound
|
|
195
|
+
on entry. Thus an event recognized within ε fires once, including across `step(0)`;
|
|
196
|
+
nearby distinct authored events retain their identity and authored ordering. A pingpong
|
|
197
|
+
endpoint has one occurrence per full period: enumerate it with the authored endpoint
|
|
198
|
+
plus an integer multiple of the period, never both the forward and reflected formulas
|
|
199
|
+
(they can differ by a binary64 rounding bit). This does not merge distinct authored events.
|
|
200
|
+
|
|
201
|
+
For sampling choose the last key reached by `t` (or the first if none), snapping `t`
|
|
202
|
+
to that key when within ε before interpolating. For repeating clocks and work areas,
|
|
203
|
+
snap the offset from work start to the nearest integer multiple of the work span when
|
|
204
|
+
within ε, before folding; a loop boundary folds to the start and a pingpong turn to
|
|
205
|
+
its endpoint. Grid-index floor operations recognize that integer tick directly,
|
|
206
|
+
avoiding a second division rounding below it. Quantize similarly recognizes a sample
|
|
207
|
+
grid point within ε; otherwise it floors as usual. Halfway ties choose the greater
|
|
208
|
+
grid index. This local comparison rule preserves arbitrary authored sub-microsecond
|
|
209
|
+
times without imposing integer-microsecond rounding on all frames or changing clock speed.
|
|
210
|
+
|
|
211
|
+
**Sampling** *(normative)*, for track keys `k0 … kn` at local time `t`:
|
|
212
|
+
- `t ≤ t0` → `v0`; `t ≥ tn` → `vn`.
|
|
213
|
+
- Otherwise find `i` with `ti ≤ t < ti+1`; `u = (t − ti) / (ti+1 − ti)`; the **ease on key `i`**
|
|
214
|
+
(the left key of the segment) maps `u → w`; result `vi + (vi+1 − vi) · w`. The ease on the last key
|
|
215
|
+
is never read and should be omitted.
|
|
216
|
+
- Booleans take `vi` for the whole segment; their keys may only carry `hold` or no ease (`bad-ease`).
|
|
217
|
+
- **Colours** interpolate per channel in non-premultiplied sRGB, channels 0..255 including alpha:
|
|
218
|
+
`c = vi.c + (vi+1.c − vi.c) · w`, then `round(c) = floor(c + 0.5)` and clamp to 0..255 (§12.2).
|
|
219
|
+
|
|
220
|
+
**Eases** *(normative)*, written as strings:
|
|
221
|
+
|
|
222
|
+
| Ease | Definition |
|
|
223
|
+
|---|---|
|
|
224
|
+
| omitted / `linear` | `w = u` |
|
|
225
|
+
| `hold` | `w = 0` |
|
|
226
|
+
| `in`, `out`, `inOut` | `cubic(0.42, 0, 1, 1)`, `cubic(0, 0, 0.58, 1)`, `cubic(0.42, 0, 0.58, 1)` |
|
|
227
|
+
| `cubic(x1, y1, x2, y2)` | CSS `cubic-bezier`: with `Bx`, `By` the Bézier through `(0,0) (x1,y1) (x2,y2) (1,1)`, find the parameter `s ∈ [0, 1]` with `Bx(s) = u` and return `By(s)`. `x1, x2 ∈ [0, 1]` (so the root is unique); `y` unbounded (overshoot allowed). Implementations must be within 1e-7 of the exact root (Newton from the linear guess, falling back to bisection, is sufficient). `w(0) = 0` and `w(1) = 1` exactly. |
|
|
228
|
+
| `elastic(a, p)` | Ease-out elastic, `a ≥ 0`, `p > 0`: with `A = max(a, 1)`, `s = p / (2π) · asin(1 / A)`; `w = A · 2^(−10u) · sin((u − s) · 2π / p) + 1`; `w(0) = 0`, `w(1) = 1` exactly. |
|
|
229
|
+
|
|
230
|
+
Arguments are JSON numbers separated by commas; spaces are optional. `spring(…)` is reserved for a
|
|
231
|
+
later minor version (`reserved`). Golden vectors for every ease live in
|
|
232
|
+
`spec/conformance/eases.json`.
|
|
233
|
+
|
|
234
|
+
**Local time** *(normative)*. A timeline played for `τ` seconds (state time × `speed`, §8) is at local
|
|
235
|
+
time: `once` → `min(τ, d)`; `loop` → `τ − d·⌊τ/d⌋`; `pingpong` → with `q = τ − 2d·⌊τ/2d⌋`,
|
|
236
|
+
`q ≤ d ? q : 2d − q`. A `once` timeline has **finished** when `τ ≥ d`.
|
|
237
|
+
|
|
238
|
+
## 6. Colours
|
|
239
|
+
|
|
240
|
+
`colors` declares named slots: `{ "ink": "currentColor", "accent": "#d97757" }`. A paint `color` is
|
|
241
|
+
either a slot name or a literal (`#rgb`, `#rrggbb`, `#rrggbbaa`, case-insensitive, or `currentColor`);
|
|
242
|
+
anything else is `bad-color`, and a name that is not a declared slot is `unknown-color-slot`.
|
|
243
|
+
In the web runtime a slot renders as `var(--anima-<slot>, <default>)`, so pages theme icons with CSS:
|
|
244
|
+
`anima-icon { color: …; --anima-accent: … }`. Static renders use the slot's default unless the
|
|
245
|
+
caller overrides it.
|
|
246
|
+
|
|
247
|
+
Slots are not animatable in 0.1. A paint colour that any track or pose writes must have a hex
|
|
248
|
+
literal as its document value, and every keyframe and pose value for it must be a hex literal
|
|
249
|
+
(`color-slot-animated`).
|
|
250
|
+
|
|
251
|
+
### Optional slot mixing (`colormix`)
|
|
252
|
+
|
|
253
|
+
With `requires: ["colormix"]`, a node fill or stroke can replace `color` with
|
|
254
|
+
`"mix": { "a": "off", "b": "on", "t": 0.3 }`. The endpoints are hex literals or declared
|
|
255
|
+
slots with hex defaults; they remain static references. `currentColor` defaults are rejected
|
|
256
|
+
(`colormix-color`) because static renderers cannot resolve page inheritance. Page overrides may
|
|
257
|
+
still use `currentColor`. Slots themselves remain non-animatable.
|
|
258
|
+
|
|
259
|
+
The number address `<id>.<fill|stroke>.mix.t` is animatable and bindable through the ordinary
|
|
260
|
+
numeric channels. Its rest value must be in 0..1 (`colormix-range`); sampled, blended and bound
|
|
261
|
+
values clamp to that interval after resolution. Ordinary numeric key/pose validation applies.
|
|
262
|
+
AniML uses a child `<colorMix a="off" b="on" t="0.3"/>` (default fill), or
|
|
263
|
+
`<colorMix paint="stroke" .../>`; install `@anima-format/web/markup-colormix` for browser
|
|
264
|
+
compilation. Stroke width/cap/join keep their usual spelling. This module adds no syntax to the
|
|
265
|
+
base AniML colour parser. JSON Schema and CLI schema describe `fill.mix` and `stroke.mix`.
|
|
266
|
+
|
|
267
|
+
SVG renders `color-mix(in srgb, var(--anima-off, <default>), var(--anima-on, <default>) 30%)`.
|
|
268
|
+
The second colour has weight t and the first 1−t. Literal-only and static SVG renders resolve the
|
|
269
|
+
colours first; static renders use defaults or supplied slot overrides. Canvas resolves page slots
|
|
270
|
+
from its owning host, including offscreen effects surfaces. All interpolate **premultiplied** sRGB
|
|
271
|
+
channels and alpha, then unpremultiply (zero alpha gives zero RGB), round channels to the nearest
|
|
272
|
+
8-bit value with ties upward, and clamp to 0..255. This matches CSS `color-mix(in srgb)`; it does
|
|
273
|
+
not change §5's existing non-premultiplied colour-track interpolation.
|
|
274
|
+
|
|
275
|
+
`mix` cannot accompany `color`, gradient `type`, or component `paint`, and is currently a node
|
|
276
|
+
fill/stroke source, not an ordered `paints` entry (`colormix-source`). Invalid endpoint names or
|
|
277
|
+
non-hex defaults are `colormix-color` at `/nodes/…/<paint>/mix/a` or `/b`, with a hex/default hint.
|
|
278
|
+
An out-of-range rest weight is `colormix-range` at `…/mix/t`, with a 0..1 hint. Malformed objects,
|
|
279
|
+
missing arguments, incorrect argument types and unknown fields retain registry diagnostics
|
|
280
|
+
(`type`, `missing-key`, `unknown-key`) at the object/argument. Absent declarations use
|
|
281
|
+
`undeclared-feature` at `…/mix`. A paint still needs exactly one colour/gradient/source.
|
|
282
|
+
|
|
283
|
+
## 7. Playback, blending and the step function *(normative)*
|
|
284
|
+
|
|
285
|
+
A **player** holds: input values, pending triggers, and per layer: the current state, its **state
|
|
286
|
+
time** (seconds since entry), and an optional crossfade (a frozen *from* snapshot, its duration, ease
|
|
287
|
+
and elapsed time). A new player has every input at its default and every layer in its initial state
|
|
288
|
+
at time 0 with no crossfade; its values are resolved as by steps 3–4 below, and nothing else is
|
|
289
|
+
evaluated until the first `step`, which reports the initial states' entry events (`emit`s and
|
|
290
|
+
timeline events at time 0) before anything else.
|
|
291
|
+
|
|
292
|
+
**Resolve rule.** Each layer is a function from the values below it to the values above it. Start
|
|
293
|
+
from the **rest values** (each property's document value, or its registry default). Let `S(state)`
|
|
294
|
+
be the set of addresses a state writes: its pose's keys, or its timeline's tracks. A layer that is
|
|
295
|
+
not crossfading writes `sample(state, p)` for every `p ∈ S(state)` and passes every other property
|
|
296
|
+
through. A layer crossfading from snapshot `F` to state `T` with eased progress `w` writes, for every
|
|
297
|
+
`p ∈ dom(F) ∪ S(T)`, `lerp(a, b, w)` with `a = F[p]` if `p ∈ dom(F)` else the value below, and
|
|
298
|
+
`b = sample(T, p)` if `p ∈ S(T)` else the value below. So a property the target state does not set
|
|
299
|
+
blends back to what is below it — for a single layer, its rest value. `lerp` is linear for numbers,
|
|
300
|
+
per channel with rounding for colours (§5), and for booleans `a || b` while the crossfade runs and
|
|
301
|
+
`b` once it ends. Later layers win where two layers write the same property.
|
|
302
|
+
|
|
303
|
+
`step(dt)` with finite `dt ≥ 0` seconds does, in order:
|
|
304
|
+
|
|
305
|
+
1. **Inputs.** Apply queued writes in arrival order (`set`, `fire`, `toggle`). Number writes clamp to
|
|
306
|
+
the input's `min`/`max`. A fired trigger becomes pending (firing a pending trigger again is a
|
|
307
|
+
no-op). Listener `emit` actions add their events now. A queued listener action evaluates its
|
|
308
|
+
expression-valued sets here, when that action is reached, using one pre-action input snapshot
|
|
309
|
+
for all its sets (§bind). Later actions see earlier writes. With `derive`, recompute
|
|
310
|
+
computed inputs once after this queue, before clocks and machine guards (derive chapter).
|
|
311
|
+
2. **Clocks.** Motion clock expressions and `exitNext` gates prepare here (motion chapter). Advance every layer's state time and crossfade elapsed time by `dt`. A crossfade whose
|
|
312
|
+
elapsed time reaches its duration ends (using §5 clock boundaries).
|
|
313
|
+
3. **Layers**, in declaration order, each against the values resolved by the layers before it:
|
|
314
|
+
1. *Timeline events.* Report the current state's timeline events crossed by step 2 (below).
|
|
315
|
+
2. *Transitions.* Unless a non-interruptible crossfade is running (§8), evaluate the current
|
|
316
|
+
state's `on` list in order; take the first transition whose
|
|
317
|
+
condition holds. Taking it: (a) freeze the layer's current output for `dom(F) ∪ S(current)` as
|
|
318
|
+
the new snapshot `F` (this is also what makes interrupting a crossfade smooth); (b) mark the
|
|
319
|
+
triggers the condition named as consumed by this layer; (c) enter the target: state time =
|
|
320
|
+
**carry** (the overshoot `t − threshold` of the `@end`/`@after` term that made the condition
|
|
321
|
+
hold, the largest one if several; else 0), crossfade elapsed = carry; a `blend` of 0 drops the
|
|
322
|
+
snapshot; (d) run the source exit, transition actions and target entry blocks (§bind), then
|
|
323
|
+
report the target's `emit`, then its timeline events in `[0, carry]`. Then
|
|
324
|
+
evaluate the new state's `on` list the same way. A layer takes at most **16** transitions per
|
|
325
|
+
step; reaching the cap reports the event `@limit` for that layer and stops its transitions for
|
|
326
|
+
this step. A trigger already consumed by this layer does not hold for it again in this step.
|
|
327
|
+
3. *Write* the layer's output over the values (resolve rule).
|
|
328
|
+
4. **Bindings, then clamp.** Evaluate declared `bind` expressions against the current inputs,
|
|
329
|
+
overriding the state-machine values (see the bind chapter). Clamp resolved values to their property's range (`opacity`, trims 0..1; `r`, `w`, `h`,
|
|
330
|
+
`rx`, `stroke.width` ≥ 0).
|
|
331
|
+
**Layout stage (optional).** After bindings and clamping, resolve participating boxes using
|
|
332
|
+
the layout contract, including nested-player/post-step updates, before drawing. Guards read
|
|
333
|
+
the previous step's boxes; layout never reruns transitions in the current step.
|
|
334
|
+
**Rig stage (optional).** After layout and before drawing, solve parent/constraint dependencies,
|
|
335
|
+
publish rest-relative bone and constrained transforms, then call the optional pose stage.
|
|
336
|
+
A pose write requests exactly one additional solve; see the rig chapter.
|
|
337
|
+
5. **Triggers.** A pending trigger that some layer consumed this step is cleared. One that no layer
|
|
338
|
+
consumed is cleared too, unless its input declares `"buffer": seconds`: then it stays pending until
|
|
339
|
+
it has been pending for longer than `buffer` seconds of step time, or is consumed.
|
|
340
|
+
6. Return `{ events, settled }`.
|
|
341
|
+
|
|
342
|
+
**Timeline events.** A play state reports each event whose time, mapped through the loop mode, lies
|
|
343
|
+
in `(τprev, τnow]` of played time, plus events at time 0 when the state is entered; for `loop` an
|
|
344
|
+
event at `te` occurs at `te + k·d`, for `pingpong` at `te + 2kd` and `2(k+1)d − te` (counted once
|
|
345
|
+
when those coincide); a `once` timeline reports nothing after it finishes. Events are reported in
|
|
346
|
+
time order. Each event is `{ name, layer, time }`: `time` is the event's timeline time, or 0 for an
|
|
347
|
+
`emit`; listener events have `layer: null`. Order within one step: listener events, then layer by
|
|
348
|
+
layer in declaration order as described in step 3.
|
|
349
|
+
|
|
350
|
+
**Settled.** `settled` is true when no trigger is pending and every layer is at rest: no crossfade
|
|
351
|
+
is running, the current state is a pose or a finished `once` timeline (or a timeline frozen by
|
|
352
|
+
reduced motion, §10), and none of the current state's transitions has an `@after(s)` term with
|
|
353
|
+
`s` greater than the state time. A settled player's values cannot change until an input changes, so
|
|
354
|
+
it needs no frames until then.
|
|
355
|
+
|
|
356
|
+
Large `dt` is processed in one step; runtimes clamp `dt` to 0.25 s per frame for display, but the
|
|
357
|
+
headless `sample` command does not.
|
|
358
|
+
|
|
359
|
+
## 8. Inputs and the state machine
|
|
360
|
+
|
|
361
|
+
```json
|
|
362
|
+
"inputs": { "ring": { "type": "trigger" }, "hover": { "type": "bool" }, "level": { "type": "number", "default": 0, "min": 0, "max": 1 } }
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Types: `bool` (`default` false), `number` (`default` 0, optional `min` ≤ `max`; the default must lie
|
|
366
|
+
in range), `trigger` (optional `buffer` ≥ 0 seconds, default 0). Inputs are the **only** host
|
|
367
|
+
interface: the page sets them, listeners set them, conditions read them. (Rive moved from
|
|
368
|
+
state-machine inputs to view models; at icon scale one flat typed namespace does both jobs.) Host
|
|
369
|
+
writes to an unknown input or with the wrong type are rejected by the API, never ignored.
|
|
370
|
+
|
|
371
|
+
```json
|
|
372
|
+
"machine": { "layers": { "ring": { "initial": "idle", "states": {
|
|
373
|
+
"idle": { "pose": { "dot.scale": 0 }, "on": [{ "when": "ring", "to": "ringing" }] },
|
|
374
|
+
"ringing": { "play": "ring", "emit": "rang", "on": [{ "when": "@end", "to": "rested" }] } } } } }
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
- A **layer** has `initial` (a state name) and `states`.
|
|
378
|
+
- A **state** has exactly one of `pose` (a map of property address → value, i.e. a timeline of one
|
|
379
|
+
instant; `{}` writes nothing) or `play` (a timeline name, optional `speed` > 0, default 1), plus
|
|
380
|
+
optional `emit` (event name on entry) and `on` (transitions) (`state-kind`).
|
|
381
|
+
- Entering a `play` state restarts its timeline at 0; its played time is state time × `speed`.
|
|
382
|
+
- A **transition** `{ "when", "to", "blend", "ease", "interruptible" }`: `when` and `to` are
|
|
383
|
+
required; `blend` is a crossfade duration in seconds (default 0); `ease` shapes the crossfade
|
|
384
|
+
(default `linear`). `interruptible` (default `true`): when `false`, the layer takes no transitions
|
|
385
|
+
while the crossfade this transition started is running, so the target pose is fully reached first
|
|
386
|
+
(a quick click still shows its press). Triggers are cleared as usual meanwhile — give the input a
|
|
387
|
+
`buffer` if a fire must survive the crossfade. `to` may
|
|
388
|
+
name the current state (re-entering it restarts its timeline) only if the condition contains a
|
|
389
|
+
trigger, `@end` or `@after` term (`self-transition`), so it cannot re-fire every step.
|
|
390
|
+
- **Conditions** (`when`), a deliberately tiny grammar (`bad-condition` otherwise):
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
condition := term ( "&&" term )*
|
|
394
|
+
term := "!"? NAME | NAME OP NUMBER | EXPR OP EXPR | "@end" | "@after(" NUMBER ")"
|
|
395
|
+
OP := "==" | "!=" | "<" | "<=" | ">" | ">="
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
`name` holds when the trigger is pending (and not yet consumed by this layer this step) or the
|
|
399
|
+
bool is true · `!name` when the bool is false · `name OP number` compares a number input ·
|
|
400
|
+
`@end` when the state's `once` timeline has finished · `@after(s)` when the state time is at least
|
|
401
|
+
`s`. All terms must hold. There is no `||`: write two transitions. Whitespace between tokens is
|
|
402
|
+
ignored. `EXPR OP EXPR` requires `bind` and uses its numeric expression language; legacy
|
|
403
|
+
`NAME OP NUMBER` keeps its number-input-only type rule, including when bind is declared.
|
|
404
|
+
Arithmetic comparisons may be joined with existing trigger/time/bool terms.
|
|
405
|
+
- Load errors: unknown input (`unknown-input`); a negated trigger, a bare number or a compared
|
|
406
|
+
bool/trigger (`input-type`); `@end` on a pose state or a `loop`/`pingpong` timeline (`bad-end`).
|
|
407
|
+
Warnings: states no transition can reach (`unreachable-state`), inputs nothing reads or writes
|
|
408
|
+
(`unused-input`).
|
|
409
|
+
|
|
410
|
+
## 9. Listeners
|
|
411
|
+
|
|
412
|
+
```json
|
|
413
|
+
"listeners": [ { "on": "click", "fire": "ring" }, { "on": "enter", "set": { "hover": true } } ]
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
- `on`: `enter` | `leave` | `down` | `up` | `click` | `move` *(drag: later)*.
|
|
417
|
+
- `target`: a node id, or omitted for the **whole icon** (the top-level `hit` shape if given, else
|
|
418
|
+
the `viewBox`). A node target hits where its painted area is: fill area by its fill rule, plus the
|
|
419
|
+
stroke within half its (world) width, of the node or, for a group, of any painted descendant,
|
|
420
|
+
grown by the listener's optional `"hitPadding"` (grid units, ≥ 0). Invisible nodes do not hit.
|
|
421
|
+
Hit testing may approximate curves, but must be exact for points farther than 0.05 units from an
|
|
422
|
+
edge.
|
|
423
|
+
- Actions, at least one: `fire` (trigger name or list), `set` (`{ input: value }` for bool and
|
|
424
|
+
number inputs; expression strings additionally require `bind`), `toggle` (bool input name or list) *(not in bell)*, `emit` (event name)
|
|
425
|
+
*(not in bell)*. Action keys run in that order: `fire`, `set`, `toggle`, `emit`.
|
|
426
|
+
- **Pointer model** *(normative)*. The host reports pointer events in grid coordinates:
|
|
427
|
+
`move(x, y)`, `down(x, y)`, `up(x, y)` (primary button only) and `leave()` (pointer left the icon
|
|
428
|
+
or was cancelled). For each event, the player updates which targets are under the pointer, then
|
|
429
|
+
runs listeners, in document order within each group: first `leave` for targets no longer under
|
|
430
|
+
the pointer, then `enter` for targets newly under it, then `move`/`down`/`up` for targets under it,
|
|
431
|
+
then `click`. `click` fires on `up` for a target that was also under the `down`, when the pointer
|
|
432
|
+
moved less than 0.5 grid units in between. `leave()` cancels a press and leaves every target.
|
|
433
|
+
- Listener actions are queued as input writes for the next `step` (§7.1); runtimes call `step(0)`
|
|
434
|
+
immediately after each pointer event so a press is visible in the same frame.
|
|
435
|
+
|
|
436
|
+
## 10. Accessibility and motion
|
|
437
|
+
|
|
438
|
+
### Web element sizing
|
|
439
|
+
|
|
440
|
+
The SVG `<anima-icon>` host defaults to a width of `1em` and an automatic height
|
|
441
|
+
with CSS `aspect-ratio` from the loaded model's viewBox width/height. Selecting a
|
|
442
|
+
root `component` uses that component's viewBox. Author CSS width, height and
|
|
443
|
+
aspect-ratio declarations take precedence over these shadow stylesheet defaults;
|
|
444
|
+
setting both dimensions keeps the authored box. Reloading or changing the shown
|
|
445
|
+
component refreshes the default ratio. Fit/alignment control the drawing within
|
|
446
|
+
that box. In the default grid/bleed mapping the host's long axis spans `grid`
|
|
447
|
+
units and its short axis follows the viewBox aspect. For a host `W×H` with the
|
|
448
|
+
viewBox aspect `w/h`, the uniform scale is `s=W/grid` when `w>=h`, otherwise
|
|
449
|
+
`s=H/grid`. The SVG viewport is `(x*s,y*s,w*s,h*s)` relative to the host and
|
|
450
|
+
retains the source aspect. If author CSS gives the host another aspect, a
|
|
451
|
+
non-square source still uses the same source-long-axis scale rule and keeps its
|
|
452
|
+
own aspect; square sources retain their legacy per-axis viewport placement.
|
|
453
|
+
Canvas uses the same placement; pointer coordinates
|
|
454
|
+
invert the drawing transform and semantic overlay bounds use it forwards.
|
|
455
|
+
Legacy square-viewBox grid offsets remain unchanged. The shadow SVG exposes
|
|
456
|
+
`part="svg"`; the canvas backend's canvas exposes `part="canvas"`. These hooks must
|
|
457
|
+
survive every shadow rebuild, including text-run changes, reloads and renderer switches,
|
|
458
|
+
so page `::part()` rules continue to apply.
|
|
459
|
+
|
|
460
|
+
### Host input queue
|
|
461
|
+
|
|
462
|
+
Web-host `set(name,value)`, `fire(name)` and `toggle(name)` calls made without a player are queued
|
|
463
|
+
in call order. They are applied once to the next player created by that host,
|
|
464
|
+
before its first step, including when asset preparation deferred creation.
|
|
465
|
+
Superseded load completions do not consume the queue. Names and values are validated
|
|
466
|
+
by the player's normal methods when the queue is applied.
|
|
467
|
+
|
|
468
|
+
### Host settling
|
|
469
|
+
|
|
470
|
+
`icon.settle()` resolves current inputs, blends and once-timelines by stepping the
|
|
471
|
+
player first at `dt=0`, then at `dt=0.25` until settled or 60 simulated seconds have
|
|
472
|
+
elapsed. It returns whether rest was reached. Events from these steps are discarded;
|
|
473
|
+
no `anima:event` or `anima:state` is dispatched, and the settled state notification
|
|
474
|
+
baseline is updated so the next ordinary frame does not replay a suppressed change.
|
|
475
|
+
The result is drawn and ARIA updated. It preserves pause, speed and reduced-motion
|
|
476
|
+
settings, resets the display clock and re-arms normal playback when still busy.
|
|
477
|
+
Before a player exists it queues once, in order with host input calls, returns false,
|
|
478
|
+
and is resolved before that player's first draw. This supports mounting a control
|
|
479
|
+
already at rest after `set()` without animating from its initial state.
|
|
480
|
+
|
|
481
|
+
### Semantics and motion
|
|
482
|
+
|
|
483
|
+
The optional browser semantic overlay, native controls, focus synchronization and
|
|
484
|
+
host tree/diff API are specified in [a11y-dom/SPEC.md](../modules/a11y-dom/SPEC.md).
|
|
485
|
+
It consumes node accessibility; root document `a11y` below is unchanged.
|
|
486
|
+
|
|
487
|
+
`"a11y": { "role": "button" | "switch" | "img", "label": "…", "activate": "<trigger or bool>", "pressed": "<bool>", "held": "<bool>" }`
|
|
488
|
+
|
|
489
|
+
- `button`/`switch`: the element is focusable and exposes the role and `label`. **Enter** activates
|
|
490
|
+
on key down (not on repeats); **Space** activates on key up — as native buttons do.
|
|
491
|
+
Activation fires the trigger named by `activate`, or toggles it if it is a bool.
|
|
492
|
+
While either key is held, the bool named by `held` (if any) is true. `pressed` names a bool input
|
|
493
|
+
that `aria-pressed` (`button`) or `aria-checked` (`switch`, where it is required) follows.
|
|
494
|
+
- `img` or no `a11y`: `role="img"` with the label, or `aria-hidden` when there is no label.
|
|
495
|
+
- `activate`, `pressed` and `held` must name inputs of the right type (`unknown-input`, `input-type`).
|
|
496
|
+
- **Reduced motion.** Each timeline declares `reducedMotion`: `play` | `end` | `skip`, default `end`.
|
|
497
|
+
With reduced motion on, entering a play state whose timeline says `end` freezes it at local time
|
|
498
|
+
`d`, reporting all its events at once; `skip` freezes it at local time 0 and reports none; `play`
|
|
499
|
+
plays normally. A frozen `once` timeline counts as finished (so `@end` holds at once) and a frozen
|
|
500
|
+
timeline of any loop mode counts as at rest. Every crossfade takes 0 s. The web runtime follows
|
|
501
|
+
Switching reduced motion on re-resolves all active play states at the next step: `end`
|
|
502
|
+
freezes at `d`, reporting only events in (current local time, `d`] once (the rest of the
|
|
503
|
+
current cycle for loops); `skip` freezes at 0 without events; `play` is unchanged.
|
|
504
|
+
Motion work ranges and reverse playback use the same start/finish endpoint as state entry.
|
|
505
|
+
In-progress crossfades complete immediately. Switching off resumes frozen loops from
|
|
506
|
+
their frozen local time; frozen `once` states stay finished. This implements the stop
|
|
507
|
+
request behind WCAG 2.2.2 even when the preference changes mid-animation (see
|
|
508
|
+
`docs/research/rive-web-runtime.md`, recommendation 7). The web runtime follows
|
|
509
|
+
`prefers-reduced-motion` unless the element sets `motion="full"` (or `motion="reduce"` to force it).
|
|
510
|
+
|
|
511
|
+
The host and semantic overlay do not stop DOM event propagation. Page listeners on
|
|
512
|
+
ancestors receive keyboard and pointer events that originate on an icon. Anima calls
|
|
513
|
+
`preventDefault()` only when its own handling consumes the browser default; internal
|
|
514
|
+
host routing guards prevent the same event from being handled twice.
|
|
515
|
+
|
|
516
|
+
## 11. Conformance
|
|
517
|
+
|
|
518
|
+
### 11.1 Cases
|
|
519
|
+
|
|
520
|
+
`spec/conformance/<case>/` holds `case.animl` (the authored source), `case.ani` (the compiled build
|
|
521
|
+
output), `script.txt` and `expected.jsonl`. `case.ani` is the runtime's normative input; hand-written
|
|
522
|
+
`.ani` remains valid. `npm run build:animl` rebuilds the sibling `.ani` files, and
|
|
523
|
+
`node tools/build-animl.ts --check` checks that they are current. Every runtime and the CLI run the
|
|
524
|
+
whole suite in CI. Numbers are compared at 1e-6, everything else exactly.
|
|
525
|
+
|
|
526
|
+
### 11.2 Script language
|
|
527
|
+
|
|
528
|
+
One command per line; `#` starts a comment. The same language drives `anima sample`.
|
|
529
|
+
|
|
530
|
+
| Command | Does |
|
|
531
|
+
|---|---|
|
|
532
|
+
| `advance <seconds> [in <n>]` | `step(seconds / n)` `n` times (default 1). |
|
|
533
|
+
| `set <input> <true\|false\|number>` | Queue a write. |
|
|
534
|
+
| `fire <input>` / `toggle <input>` | Queue a fire / toggle. |
|
|
535
|
+
| `pointer move\|down\|up <x> <y>` | Report a pointer event in grid units, then `step(0)`. |
|
|
536
|
+
| `pointer click <x> <y>` | `move`, `down`, `up` at that point, each followed by `step(0)`. |
|
|
537
|
+
| `pointer leave` | Report `leave()`, then `step(0)`. |
|
|
538
|
+
| `focus target <id>` / `focus clear` | Change logical focus, then step(0); requires focus. |
|
|
539
|
+
| `focus traverse <direction>` | Traverse next/previous/left/right/up/down, then step(0). |
|
|
540
|
+
| `key down\|up <key> [shift]` | Deliver a deterministic focus key, then step(0); input module integration extends this dispatch. |
|
|
541
|
+
| `semantics [id]` | Print the semantic tree or subtree as one JSON line; requires a11y. |
|
|
542
|
+
| `semantic <id> tap\|increase\|decrease\|focus` | Perform an advertised semantic action, then step(0); requires a11y-actions. |
|
|
543
|
+
| `motion reduce\|full` | Turn reduced motion on or off at the next step, including active states. |
|
|
544
|
+
| `touch down\|move\|up <id> <x> <y>` | Input-module pointer API with an integer id ≥ 0, in grid units; `up` also leaves. Each delivery is followed by `step(0)`. |
|
|
545
|
+
| `pointer drag <x0> <y0> <x1> <y1> [in <n>]` | Move/down at start, `n` equally spaced moves, up at end; each delivery steps at 0. Default `n=1`. |
|
|
546
|
+
| `contact move\|down\|up <id> <x> <y> [button]` | Input-module pointer, preserving pointer ids. `contact leave\|cancel <id>` ends a contact. |
|
|
547
|
+
| `key down\|up\|repeat <key\|Space> [shift+control+alt+meta\|-] [target]` | Extended key API when input is required; otherwise the focus-only spelling above. |
|
|
548
|
+
| `scroll <x> <y> <dx> <dy> [start\|move\|end\|cancel] [seconds]` | Input-module scroll API in grid units, followed by `step(0)`. |
|
|
549
|
+
| `gamepads <JSON-array>` | Poll the input-module gamepad API, then `step(0)`; records have index, connected, optional mapping, axes and buttons. |
|
|
550
|
+
| `pointer-enable <id> <true\|false>` / `scroll-cancel` | Enable a pointer or cancel scroll latching, then `step(0)`. |
|
|
551
|
+
| `data set <$path> <value>` | Queue a typed public host-data handle write; applies on the next step. JSON strings, bools, numbers, colors and lists are accepted according to the property type. |
|
|
552
|
+
| `data dump [prefix …]` | Print `{data: {path: value}}` as one JSON line. No prefixes selects all; multiple prefixes form a union. Includes named instance paths and default scalar aliases. |
|
|
553
|
+
| `data watch <$path …>` | Remember current values. Each later snapshot carries a `data` object of watched values changed since the previous snapshot (or watch registration). |
|
|
554
|
+
| `data break <$path> [== value]` | Preflight each next step by instrumented replay on an isolated player. Stop before applying the step live at the first write (including same-value or overwritten writes, optionally matching the condition), reporting `script-breakpoint`, old/written values and the actual writer. |
|
|
555
|
+
| `data break clear` | Clear all breakpoints; a session can then advance the pending step. |
|
|
556
|
+
| `expect <address\|$path\|input> ==\|!=\|<\|>\|~ <value>` | Compare applied values; `~` is absolute numeric error ≤ 1e-6. Failure emits `script-expect` with pointer and hint; execution continues. |
|
|
557
|
+
| `group <name>` / `end` | Open/close a named assertion group; nested groups label assertion diagnostics. Session groups may span requests. |
|
|
558
|
+
| `expect-error <code>` | Assert that the next nonempty command throws or emits that diagnostic code. A missing or different code emits `script-expect-error`; matching diagnostics are consumed. |
|
|
559
|
+
| `seed <n>` | Reset the shared core uint32 random source for this player and nested stepping to n. The player constructor option `seed` and CLI `--seed` initialize the same source. Without an explicit seed, existing deterministic machine/converter defaults are preserved. |
|
|
560
|
+
| `snapshot [--all] [--semantics] [address …]` | Print one JSON line. `--all` adds unanimated animatable properties and persistent inputs. `--semantics` appends `player.semantics()` or errors clearly if unavailable. Explicit addresses accept static properties too. |
|
|
561
|
+
|
|
562
|
+
`instance <id> <timeline> [mix]`, `timeline <name> play|pause|stop|seek|scrub|speed|mix|loop [value]`
|
|
563
|
+
and `machine <name> play|pause|stop|mix|fire|set [value …]` retain the motion module's control API.
|
|
564
|
+
JSON quoted strings can contain spaces and `#`; unquoted hex colors are values rather than comments.
|
|
565
|
+
Data commands require data in the document and the host module loaded; scripts without data commands
|
|
566
|
+
do not access host data. Commands add no authoring fields or registry spellings. Both `expect` and selected
|
|
567
|
+
`snapshot` addresses resolve AniML property spellings (for example `volumeFill.width` →
|
|
568
|
+
`volumeFill.w`) through the registry, including slash-scoped nested addresses. Exact inputs
|
|
569
|
+
and JSON addresses take precedence over aliases. The CLI groups unknown `$path-number`
|
|
570
|
+
diagnostics at the same pointer into one `data-path` diagnostic with a spaced subtraction
|
|
571
|
+
hint; declared hyphenated paths retain their meaning.
|
|
572
|
+
|
|
573
|
+
`anima sample` prints outputs in script order, emits assertion/breakpoint diagnostics on stderr,
|
|
574
|
+
and returns 1 for those diagnostics, 2 for invalid arguments/script commands. Targeting an absent input with set/fire/toggle or a hidden/absent semantic node with
|
|
575
|
+
semantic emits `script-target`, a script pointer and inspection guidance. Other script syntax
|
|
576
|
+
errors carry `script-usage`, `/script/<zero-based-line>`, a message and hint.
|
|
577
|
+
|
|
578
|
+
`anima session <file>` keeps the interpreter alive. Each stdin line is one command or a JSON
|
|
579
|
+
object with a string `cmd`; each receives exactly one JSON reply containing `ok`, outputs,
|
|
580
|
+
snapshots and diagnostics. A bad request does not end the process. `quit` replies then exits;
|
|
581
|
+
`reload` reads and validates the source again, resets time/inputs/control state, restores compatible
|
|
582
|
+
data values through public handles and steps at 0. Its reply lists restored/skipped paths; failed
|
|
583
|
+
reload retains the old interpreter. Breakpoints pause the current command, and the next request
|
|
584
|
+
may inspect/edit data or clear them. Preflight replays all prior operations, so its cost grows with
|
|
585
|
+
history and includes the next step. Tooling instruments instance setters and queued operations in
|
|
586
|
+
the replay player, capturing writes before mutation, including same-value and transient writes.
|
|
587
|
+
Writers identify a script line, listener pointer, state/transition action pointer or the actual
|
|
588
|
+
binding write function. Timeline-driven reverse writes identify the binding that stores the value.
|
|
589
|
+
Instrumentation is absent from shipped data/host bundles.
|
|
590
|
+
|
|
591
|
+
`anima bench <file> [-s script] [--frames N] [--format=json]` measures source compilation/loading
|
|
592
|
+
once, then renderer-neutral `Player.step(1/60)` and `drawFrame` separately for N frames (default
|
|
593
|
+
600, integer 1–1,000,000). It warms 50 frames on a separate player. Output includes `compileMs`,
|
|
594
|
+
frame count and step/draw median and p95 in microseconds, using sorted samples at indices
|
|
595
|
+
floor(N×0.5) and floor(N×0.95), capped at N−1. Script setup, filesystem I/O except source loading,
|
|
596
|
+
DOM and raster/GPU work are not frame costs. This follows the measurement boundary in
|
|
597
|
+
`tools/perf.ts`; see also `docs/research/rive-web-runtime.md` on native/GPU versus host overhead.
|
|
598
|
+
Bench additionally reports `memory` with `kind: "js-heap"`, `beforeBytes`, `afterBytes`,
|
|
599
|
+
`growthBytes` (after minus before), and `bytesPerFrame` (growth divided by N). Samples bracket
|
|
600
|
+
the measured frame loop after warming; GC can make growth negative. These are JS heap observations,
|
|
601
|
+
not retained allocation or WASM measurements. Both session and bench accept `--seed`, `--all`, `--semantics` and `--reduced-motion`; seed has
|
|
602
|
+
the shared source semantics above. Debugging and audio capture instrumentation are tooling-only.
|
|
603
|
+
|
|
604
|
+
A snapshot line is `{"time": t, "values": {address: value}, "states": {layer: state},
|
|
605
|
+
"events": [...], "settled": bool}`: `time` is the total step time so far, `values` holds every
|
|
606
|
+
address any track or pose writes (or the listed ones), `events` holds the events reported since the
|
|
607
|
+
previous snapshot, and `settled` is the last step's result.
|
|
608
|
+
Explicit input names appear in `values` under their own names (for example,
|
|
609
|
+
`snapshot dark knob.x`). They report the currently applied value; queued `set`/`toggle`
|
|
610
|
+
writes take effect only after a step. Triggers have no persistent value and cannot be
|
|
611
|
+
selected. Selected addresses accept both JSON paths and AniML property spellings (for example
|
|
612
|
+
`ring.stroke.trimEnd` and `ring.trimEnd`). Snapshot keys always use JSON paths, for both
|
|
613
|
+
selected and complete snapshots, including slash-scoped component addresses. Exact input
|
|
614
|
+
names and JSON paths take precedence over aliases. An unknown address or input is a script error. With no arguments, `snapshot`
|
|
615
|
+
continues to include only animated addresses, never inputs (unless `--all` is set).
|
|
616
|
+
|
|
617
|
+
### 11.3 Diagnostic codes
|
|
618
|
+
|
|
619
|
+
| Code | Severity | When |
|
|
620
|
+
|---|---|---|
|
|
621
|
+
| `json-syntax` | error | The text is not JSON. The message carries line and column. |
|
|
622
|
+
| `duplicate-key` | error | An object repeats a key (`anima validate`). |
|
|
623
|
+
| `type` | error | A value has the wrong JSON type. |
|
|
624
|
+
| `missing-key` | error | A required key is absent. |
|
|
625
|
+
| `unknown-key` | error | A key is not defined here. Hint: the closest valid key. |
|
|
626
|
+
| `reserved` | error | A key, ease or address reserved for a later version. |
|
|
627
|
+
| `version-unsupported` | error | `anima` is not `"0.1"`. |
|
|
628
|
+
| `undeclared-feature` | error | A module key or address is used without its feature in `requires`; hint: add the feature to `requires`. |
|
|
629
|
+
| `unknown-feature` | error | `requires` names a feature this runtime lacks. |
|
|
630
|
+
| `bad-name` | error | A name breaks its pattern. |
|
|
631
|
+
| `duplicate-id` | error | Two nodes share an id. |
|
|
632
|
+
| `unknown-node-type` | error | `type` is not a node type. |
|
|
633
|
+
| `bad-value` | error | A value is out of range or not an allowed option. |
|
|
634
|
+
| `bad-color` | error | A colour string does not parse. |
|
|
635
|
+
| `unknown-color-slot` | error | A paint names a slot that is not declared. |
|
|
636
|
+
| `unknown-glyph` | error | A path names a glyph that is not declared. |
|
|
637
|
+
| `bad-path` | error | Path data does not parse. |
|
|
638
|
+
| `bad-subpath` | error | A subpath index is out of range or repeated. |
|
|
639
|
+
| `path-source` | error | A path has both or neither of `glyph` and `d`. |
|
|
640
|
+
| `arc-fill` | error | An arc has a fill. |
|
|
641
|
+
| `trim-multi-subpath` | error | A stroke with trims on a path of several subpaths. |
|
|
642
|
+
| `unknown-address` | error | A track or pose names an unknown node or property. |
|
|
643
|
+
| `not-animatable` | error | A track or pose writes a property that cannot be animated. |
|
|
644
|
+
| `value-type` | error | A keyframe or pose value has the wrong type. |
|
|
645
|
+
| `color-slot-animated` | error | An animated colour uses a slot or `currentColor`. |
|
|
646
|
+
| `empty-track` | error | A track has no keyframes. |
|
|
647
|
+
| `key-order` | error | Keyframe times do not strictly increase. |
|
|
648
|
+
| `key-range` | error | A keyframe or event time lies outside `[0, duration]`. |
|
|
649
|
+
| `bad-ease` | error | An ease does not parse, has invalid arguments, or eases a boolean. |
|
|
650
|
+
| `unknown-timeline` | error | A state plays a timeline that is not declared. |
|
|
651
|
+
| `unknown-state` | error | `initial` or `to` names a state that is not in the layer. |
|
|
652
|
+
| `state-kind` | error | A state has both or neither of `pose` and `play`, or `speed` without `play`. |
|
|
653
|
+
| `bad-condition` | error | A condition does not parse. |
|
|
654
|
+
| `guard-literal` | error | A module literal function uses the wrong property, name or operator; use ==/!= with a matching typed property. |
|
|
655
|
+
| `data-keyframe` | error | A key source path or live channel value has the wrong type; use compatible scalar data. |
|
|
656
|
+
| `unknown-input` | error | A condition, listener or `a11y` names an input that is not declared. |
|
|
657
|
+
| `input-type` | error | An input is used in a way its type does not allow (`a11y.activate` needs a trigger or bool; `pressed` and `held` need bools). |
|
|
658
|
+
| `bad-end` | error | `@end` on a pose state or a looping timeline. |
|
|
659
|
+
| `self-transition` | error | A transition to the current state with only level terms. |
|
|
660
|
+
| `unknown-target` | error | A listener targets a node that does not exist. |
|
|
661
|
+
| `limit-exceeded` | error | A §2 limit is exceeded. |
|
|
662
|
+
| `hash-mismatch` | warning | A glyph's `sha256` does not match its `d`. |
|
|
663
|
+
| `no-paint` | warning | A painted node has neither fill nor stroke. |
|
|
664
|
+
| `unreachable-state` | warning | No transition reaches a state. |
|
|
665
|
+
| `unused-input` | warning | Nothing reads or writes an input. |
|
|
666
|
+
|
|
667
|
+
### 11.4 Local playground
|
|
668
|
+
|
|
669
|
+
`anima playground [file.animl] [--port N]` serves a local authoring inspector on `127.0.0.1`
|
|
670
|
+
(default port 4519, next free if occupied). With no file it opens the reference bell as a read-only
|
|
671
|
+
scratch example; Save cannot change it. AniML edits use the strict compiler and its source positions
|
|
672
|
+
(`spec/ANIML.md` §7). Errors keep the last valid document visible, marked as stale. The live stage
|
|
673
|
+
uses the web runtime and the same inputs, listeners and player semantics as a hosted icon.
|
|
674
|
+
|
|
675
|
+
Timeline scrubbing samples the chosen timeline at the clamped time `[0, duration]` using §5 over
|
|
676
|
+
the document rest pose. It pauses live playback and input controls; returning to live mode resumes
|
|
677
|
+
the saved live state. It does not advance the state machine or emit timeline events. This is an
|
|
678
|
+
inspector view, not a new player stepping mode. Save writes only the opened file; a changed disk
|
|
679
|
+
revision must be reloaded before saving. Details and keyboard controls: `docs/PLAYGROUND.md`.
|
|
680
|
+
|
|
681
|
+
## 12. Decisions for 0.1, and what is deferred
|
|
682
|
+
|
|
683
|
+
These were the open questions of the draft. Each is settled for 0.1; the deferred ones are reserved
|
|
684
|
+
so a 0.1 loader rejects them with `reserved` and a pointer to the version that brings them.
|
|
685
|
+
|
|
686
|
+
1. **Non-scaling strokes — no flag; strokes always scale.** Glyphy ink is a filled outline, which
|
|
687
|
+
scales with its node, so an authored stroke (the bell's waves) must scale the same way to keep
|
|
688
|
+
matching it. Glyphy's own check allows `stroke ± 0.12`, and the bell's poses (0.95–1.08) stay in
|
|
689
|
+
it (`docs/research/glyphy-compat.md` §4.5). SVG's `vector-effect: non-scaling-stroke` works in
|
|
690
|
+
screen pixels, not grid units, so it could not express "keep 1 unit" anyway. Revisit with a
|
|
691
|
+
grid-unit definition if an icon needs it.
|
|
692
|
+
2. **Colour interpolation — non-premultiplied sRGB, 8-bit channels, `floor(c + 0.5)`, clamped.**
|
|
693
|
+
It is what CSS transitions and Rive do, so authors' expectations and the Rive reference agree,
|
|
694
|
+
and the rule is exactly reproducible. A timeline key `colorSpace` (e.g. `"oklab"`) is reserved for 0.2.
|
|
695
|
+
(Rive's lossy float round-trip, `docs/research/rive-animation-and-state.md` Lesson A.5, cannot
|
|
696
|
+
happen: values are integers.)
|
|
697
|
+
3. **Keyboard → `down` — explicit.** `a11y.held` names the bool that is true while Enter or Space is
|
|
698
|
+
held; nothing is inferred from input names. The bell sets `"held": "down"`.
|
|
699
|
+
4. **Additive layers — deferred to 0.2** (reserved layer key `mode`). 0.1 layers override; the
|
|
700
|
+
resolve rule (§7) passes unwritten properties through, so layers that write disjoint properties
|
|
701
|
+
compose already, which covers a breathing idle on one node under a hover tilt on another.
|
|
702
|
+
5. **Path morphing** is available through the optional paint module for compatible command lists
|
|
703
|
+
(see the paint chapter). Automatic matching of differing Glyphy traces remains deferred.
|
|
704
|
+
6. **Data binding.** Input expressions are available in the optional `bind` module below.
|
|
705
|
+
View models and general data sources remain for P4e.
|
|
706
|
+
7. **Draw-on for Glyphy outlines — deferred to 0.2.** Trims work on authored strokes in 0.1. For
|
|
707
|
+
glyph ink the likely 0.2 design is a `reveal` mask: an authored centreline subpath, stroked wider
|
|
708
|
+
than the ink and trimmed, used as a mask so the visible ink stays the exact outline
|
|
709
|
+
(`docs/research/glyphy-compat.md` §4.5a). It needs a Glyphy centreline export to be practical.
|
|
710
|
+
|
|
711
|
+
Other decisions made while freezing 0.1:
|
|
712
|
+
|
|
713
|
+
- **Clocks advance before transitions** (§7). The draft evaluated transitions first, so a finished
|
|
714
|
+
timeline could report `settled` while its `@end` transition was still pending.
|
|
715
|
+
- **Transitions chain within a step** (cap 16), with time carried over from `@end`/`@after`, so one
|
|
716
|
+
large `dt` and many small ones reach the same state (Rive allows chains too, capped at 100;
|
|
717
|
+
`rive-animation-and-state.md` §2.3).
|
|
718
|
+
- **The crossfade source is always a frozen snapshot** of the layer's output at the moment of the
|
|
719
|
+
transition, not a still-playing timeline. One rule covers both normal transitions and
|
|
720
|
+
interruptions, and only one timeline is sampled per layer.
|
|
721
|
+
- **Triggers are consumed per layer** and dropped at the end of the step unless the input declares
|
|
722
|
+
`buffer` (Lesson A.11).
|
|
723
|
+
- **`hit`** is a top-level circle or rect for whole-icon listeners (bell: the Rive file's hit disc,
|
|
724
|
+
radius 10.8 around the glyph centre).
|
|
725
|
+
- **Exact version match** before 1.0, rather than minor-version feature negotiation.
|
|
726
|
+
- **Cap and join default to `round`**, matching the Glyphy reference sets rather than SVG's `butt`.
|
|
727
|
+
|
|
728
|
+
## paint
|
|
729
|
+
|
|
730
|
+
The optional `paint` module (MODULES.md) extends 0.1 documents with `requires: ["paint"]`.
|
|
731
|
+
An unregistered module is `unknown-feature` at `/requires/<index>`, before structural checks.
|
|
732
|
+
Using its keys or animation addresses without declaring it is `undeclared-feature` at that key
|
|
733
|
+
or address, with hint `add "paint" to "requires"`. Ordinary 0.1 documents keep their behaviour.
|
|
734
|
+
|
|
735
|
+
### Image nodes
|
|
736
|
+
|
|
737
|
+
With `requires: ["paint", "assets"]`, an image node has `id`, `type: "image"`, `asset`,
|
|
738
|
+
`x0`, `y0`, positive `width` and `height`, and optional `fit`, `align`, `sampling`.
|
|
739
|
+
It accepts the common transforms, opacity and visibility. Width and height are animatable
|
|
740
|
+
(nonnegative resolved values); layout options and the asset name are static.
|
|
741
|
+
`unknown-asset` at the node's `/asset` names an absent declaration; `asset-type` at that
|
|
742
|
+
pointer rejects a declaration whose type is not `image`.
|
|
743
|
+
|
|
744
|
+
Let the box be W×H and the intrinsic size I×J. The image is scaled and clipped to the box:
|
|
745
|
+
|
|
746
|
+
| fit | scale (x and y unless specified) |
|
|
747
|
+
|---|---|
|
|
748
|
+
| `contain` (default) | min(W/I, H/J) |
|
|
749
|
+
| `cover` | max(W/I, H/J) |
|
|
750
|
+
| `fill`, `resize` | x=W/I, y=H/J |
|
|
751
|
+
| `none` | 1 |
|
|
752
|
+
| `scale-down` | min(1, W/I, H/J) |
|
|
753
|
+
| `fit-width` | W/I |
|
|
754
|
+
| `fit-height` | H/J |
|
|
755
|
+
|
|
756
|
+
`align` is `center` (default), `top-left`, `top`, `top-right`, `left`, `right`,
|
|
757
|
+
`bottom-left`, `bottom`, or `bottom-right`. Each axis places the scaled image at 0,
|
|
758
|
+
(box−image)/2, or box−image. Overflow is always clipped, including `none`.
|
|
759
|
+
The fitted image rectangle has geometric edge coverage even at fractional device coordinates;
|
|
760
|
+
nearest sampling selects texels without snapping that rectangle to whole pixels.
|
|
761
|
+
`sampling` is `bilinear` (default) or `nearest`; SVG maps these to `image-rendering="auto"`
|
|
762
|
+
and `image-rendering="pixelated"`. The browser's final display scaling remains host-controlled.
|
|
763
|
+
|
|
764
|
+
AniML: `<image id="photo" asset="photo" x0="0" y0="0" width="24" height="24"
|
|
765
|
+
fit="contain" align="center" sampling="bilinear"/>`. `x0`/`y0` specify the untransformed
|
|
766
|
+
box origin; common `x`/`y` remain transform translations. `fit` and `align` deliberately keep
|
|
767
|
+
their JSON spellings: SVG `preserveAspectRatio="xMidYMid meet"` maps to contain/center,
|
|
768
|
+
`slice` to cover and `none` to fill. No `preserveAspectRatio` input alias exists, since SVG
|
|
769
|
+
cannot express all seven fit modes. Backends receive `Geometry.kind: "image"`, box coordinates,
|
|
770
|
+
and renderer-neutral image metadata (asset, source, intrinsic size, fit, align, sampling).
|
|
771
|
+
SVG uses a nested clipped viewport and an image leaf; transforms stay on the node's group.
|
|
772
|
+
The host resolves missing intrinsic dimensions before rendering; core draw data keeps them absent.
|
|
773
|
+
|
|
774
|
+
### Image wrapping (P5b)
|
|
775
|
+
|
|
776
|
+
`wrap` is `clamp`, `repeat` or `mirror`. `wrapX`/`wrapY` (AniML `wrap-x`/`wrap-y`)
|
|
777
|
+
override their axis independently; an axis without its own value takes `wrap`, and `wrap` defaults to
|
|
778
|
+
`clamp` once any wrap control is authored. An image with **no** wrap control draws only its fitted
|
|
779
|
+
source rectangle, as before P5b (decided for compatibility: existing contain/none renders and their
|
|
780
|
+
conformance cases are unchanged). Fit and alignment first establish the fitted source rectangle.
|
|
781
|
+
Clamp extends the edge texels outside the fitted source rectangle to the box boundary. Repeat
|
|
782
|
+
tiles it in both directions; mirror reflects every odd tile, including negative tile indices.
|
|
783
|
+
Width/height animation
|
|
784
|
+
recomputes fit and pattern extent. Nearest clamp extends isolated edge texels on both backends,
|
|
785
|
+
including mixed periodic/clamped axes and centred alignment. `resize` is the `fill` alias: both axes stretch independently to the box.
|
|
786
|
+
The optional browser entry `@anima-format/web/paint-sampling` imports paint and installs SVG
|
|
787
|
+
wrapping. The ordinary paint entry declines explicit wrap controls with `unsupported-render` and an import
|
|
788
|
+
hint; it does not bundle pattern code. CLI/static SVG includes the backend. SVG uses user-space patterns and transformed groups for
|
|
789
|
+
reflected tiles, keeping transforms off
|
|
790
|
+
image leaves. Canvas uses equivalent source tiles. Filtering remains nearest/bilinear as above;
|
|
791
|
+
SVG declines bilinear `repeat` with `unsupported-render` at the repeating axis, a reason about
|
|
792
|
+
independent image filtering, and a nearest/canvas hint. A native UV-mesh probe measured maximum
|
|
793
|
+
channel delta 188 and 36,852/57,600 pixels above 16 before declining this combination (see
|
|
794
|
+
modules/assets/probes/native-measurements.json). Nearest repeat and mirror pass crisp native probes.
|
|
795
|
+
SVG also declines bilinear clamp when the fitted source leaves margins: stretching a clipped
|
|
796
|
+
texel would interpolate adjacent colours rather than hold the edge colour. This reports
|
|
797
|
+
`unsupported-render` at `/nodes/<index>/sampling` with a nearest/canvas hint. Canvas isolates and
|
|
798
|
+
pads texels before filtering; it implements bilinear clamp and mixed-axis wrapping.
|
|
799
|
+
GPU/rasterizer edge differences are not a claim of bit-identical rendering. Invalid wrap enums
|
|
800
|
+
use the existing `bad-value` structural diagnostic at the authored property. No new player state is
|
|
801
|
+
introduced. Both source syntax spellings and deterministic width tracks have conformance probes.
|
|
802
|
+
|
|
803
|
+
### Gradients
|
|
804
|
+
|
|
805
|
+
A `fill` or `stroke` contains either `color` or a gradient, never both. A gradient has `type`:
|
|
806
|
+
|
|
807
|
+
| Type | Required coordinates (grid units) | Optional coordinates |
|
|
808
|
+
|---|---|---|
|
|
809
|
+
| `linear` | `x1`, `y1`, `x2`, `y2` (start and end) | — |
|
|
810
|
+
| `angular` | `x1`, `y1`, `x2`, `y2` (centre and initial ray) | — |
|
|
811
|
+
| `radial` | `cx`, `cy`, `r` (centre and radius, `r ≥ 0`) | `fx`, `fy` (focal point, each defaults to the corresponding resolved centre coordinate) |
|
|
812
|
+
|
|
813
|
+
All have `stops`, an array of at least two `{ position, color }` objects. `position` is 0..1,
|
|
814
|
+
nondecreasing at rest; repeated positions are permitted (hard edges). `color` follows §6, including
|
|
815
|
+
slots, `currentColor` and hex alpha. Fill rule and stroke width/cap/join/trim/dashes remain on the
|
|
816
|
+
paint object. Coordinates belonging to the other gradient type are errors.
|
|
817
|
+
|
|
818
|
+
Animatable addresses are `<id>.<fill|stroke>.<coordinate>`,
|
|
819
|
+
`<id>.<fill|stroke>.stops.<index>.position` and
|
|
820
|
+
`<id>.<fill|stroke>.stops.<index>.color`. Indices are zero-based decimal integers without leading
|
|
821
|
+
zeros and must exist. Coordinates must belong to the authored gradient; optional focal coordinates
|
|
822
|
+
may be animated even when omitted, with their rest defaults equal to the authored centre.
|
|
823
|
+
`type`, the stop array's length/order and the dash array are not animatable.
|
|
824
|
+
|
|
825
|
+
Coordinates and positions use §5 number sampling and §7 blending; positions clamp to 0..1 and
|
|
826
|
+
radius to ≥0 after resolving. Stop colours use the existing non-premultiplied sRGB channel sampling
|
|
827
|
+
(including alpha), blending, dirty slots and hex-only animation restriction (§6). A static slot stop
|
|
828
|
+
remains themeable. In SVG, stop positions that cross during animation are displayed at the maximum
|
|
829
|
+
of that position and all earlier positions; sampled values retain their independently clamped values.
|
|
830
|
+
|
|
831
|
+
Gradients render as `<linearGradient>` / `<radialGradient>` defs, unique per node, paint and icon,
|
|
832
|
+
with `gradientUnits="userSpaceOnUse"`, sRGB colour interpolation and SVG's default `spreadMethod`
|
|
833
|
+
(`pad`). A paint references its def. Coordinates are in the node's untransformed grid space;
|
|
834
|
+
the node transform carries the paint with its geometry. Degenerate gradients and focal points
|
|
835
|
+
outside the radius follow SVG gradient semantics. Knockout geometry and hit tests stay as in §4/§9;
|
|
836
|
+
they do not acquire gradient alpha sampling.
|
|
837
|
+
|
|
838
|
+
### Angular gradients and affine gradient transforms
|
|
839
|
+
|
|
840
|
+
`type: "angular"` uses `(x1,y1)` as its centre and the ray to `(x2,y2)` as
|
|
841
|
+
position zero. Positions increase clockwise in SVG's y-down local coordinates over one full
|
|
842
|
+
turn. Coincident endpoints use the positive x axis. Stops pad to the first/last colour and
|
|
843
|
+
may repeat; the seam joins position one back to zero. Coordinates/stops animate like linear
|
|
844
|
+
gradients. AniML uses `<angularGradient x1="…" y1="…" x2="…" y2="…">` with `<stop>` children.
|
|
845
|
+
Canvas uses `createConicGradient`. SVG refuses angular gradient resources with
|
|
846
|
+
`unsupported-render`, a paint `/type` pointer and a hint to use canvas; it never substitutes
|
|
847
|
+
linear/radial interpolation or a finite wedge approximation.
|
|
848
|
+
|
|
849
|
+
Every gradient accepts static `gradientTransform` (AniML `gradient-transform`), six finite
|
|
850
|
+
numbers `[a,b,c,d,e,f]`. It maps gradient coordinates by
|
|
851
|
+
`(x,y) → (a*x+c*y+e,b*x+d*y+f)` before the node transform, affecting paint only.
|
|
852
|
+
Translations are grid units; linear coefficients are dimensionless (rotation coefficients
|
|
853
|
+
are cosine/sine of the desired angle). A singular matrix is `bad-value` at
|
|
854
|
+
`/gradientTransform`, with an invertibility hint. Non-gradient paints reject the field.
|
|
855
|
+
SVG emits `gradientTransform="matrix(…)"`; canvas paints the transformed gradient into a layer masked by the independently drawn
|
|
856
|
+
geometry, preserving stroke widths under nonuniform matrices. The matrix is static and theme slots still resolve normally.
|
|
857
|
+
|
|
858
|
+
### Dashes
|
|
859
|
+
|
|
860
|
+
`stroke.dash` is a nonempty array, all nonnegative grid-unit numbers (`[2, 1]`), or all decimal
|
|
861
|
+
percentage strings (`["10%", "5%"]`). Percentage syntax is `0` or a positive decimal integer,
|
|
862
|
+
optionally followed by a decimal fraction, then `%`; signs and exponents are not accepted.
|
|
863
|
+
A percentage refers to the entire outline length, **not** the SVG viewport diagonal. Units cannot
|
|
864
|
+
be mixed, and at least one length must be positive. Odd arrays repeat twice (SVG behaviour).
|
|
865
|
+
The array is static. Each subpath restarts the pattern as in SVG.
|
|
866
|
+
|
|
867
|
+
`stroke.dashOffset` is an animatable number, default 0, positive or negative. With numeric dashes
|
|
868
|
+
it is in grid units; with percentage dashes it is a fraction of total path length (0.1 = 10%).
|
|
869
|
+
It uses the existing number sampling and blending. Every backend resolves relative patterns to
|
|
870
|
+
natural local outline lengths using the core length algorithm below. Live and static SVG emit
|
|
871
|
+
absolute dasharray and dashoffset values, without `pathLength` normalization: browser engines'
|
|
872
|
+
internal primitive-length approximations must not change authored dash or trim lengths.
|
|
873
|
+
|
|
874
|
+
**Trim wins:** when resolved `trimEnd − trimStart < 1`, §4's trim dasharray and offset replace the
|
|
875
|
+
authored dashes and offset. A nonpositive trim suppresses the stroke entirely. A full trim permits
|
|
876
|
+
the authored pattern again, using its own units. Animating the offset still samples normally while
|
|
877
|
+
a trim overrides its display. With no dash array, `dashOffset` has no visual effect.
|
|
878
|
+
|
|
879
|
+
### Diagnostics and implementation
|
|
880
|
+
|
|
881
|
+
Registry structural errors retain §11.3 codes (`type`, `bad-value`, `missing-key`, `unknown-key`).
|
|
882
|
+
Missing coordinates/stops/paint kind are `missing-key` at the paint object with an add-key hint.
|
|
883
|
+
Unsorted stops, mixed/all-zero dashes, incompatible coordinates and flat colour plus gradient are
|
|
884
|
+
`bad-value` at the offending key with a corrective hint where possible. Invalid stop colours use
|
|
885
|
+
`bad-color` / `unknown-color-slot`; animated slot stops use `color-slot-animated`. Invalid addresses
|
|
886
|
+
are `unknown-address`, and static paint fields are `not-animatable`.
|
|
887
|
+
|
|
888
|
+
Module algorithms are independently implemented against SVG semantics. The registry approach
|
|
889
|
+
continues ARCHITECTURE §core.1 and the ideas recorded in
|
|
890
|
+
[format and graph research](../docs/research/rive-format-and-graph.md); the scene keeps the retained
|
|
891
|
+
DOM/dirty-node strategy in [web runtime research](../docs/research/rive-web-runtime.md).
|
|
892
|
+
No algorithm here closely follows Rive runtime source; no additional NOTICE attribution is needed.
|
|
893
|
+
|
|
894
|
+
### Parametric outlines
|
|
895
|
+
|
|
896
|
+
The paint module adds painted ellipse (cx,cy,rx,ry), polygon (cx,cy,sides,radius,
|
|
897
|
+
rotation=0,cornerRadius=0), star (cx,cy,points,radius,innerRadius,cornerRadius=0),
|
|
898
|
+
and triangle (x0,y0,width,height). All listed numeric parameters are animatable;
|
|
899
|
+
all lengths use local grid units. AniML element and attribute names equal these names.
|
|
900
|
+
Radii and dimensions are nonnegative. Authored sides are integers 3..1024 and points
|
|
901
|
+
integers 2..1024; sampled counts clamp to these bounds and round to nearest integer,
|
|
902
|
+
ties upward. Polygon vertices advance clockwise from the top, rotated by rotation
|
|
903
|
+
degrees. Star vertices alternate outer/inner radii clockwise from the top outer vertex.
|
|
904
|
+
Triangle vertices are (x0+width/2,y0), (x0+width,y0+height), (x0,y0+height).
|
|
905
|
+
Ellipse paths use two half-ellipse SVG arcs; zero axes produce SVG degenerate outlines.
|
|
906
|
+
|
|
907
|
+
Polygon/star rounding cuts back cornerRadius on both incident edges, independently
|
|
908
|
+
limited to half each edge's length, then joins the cut points with a quadratic Bézier
|
|
909
|
+
controlled by the original vertex. Straight edges connect the corners. The parameter
|
|
910
|
+
is a cutback distance, rather than an osculating circle radius; concave vertices use
|
|
911
|
+
that same construction. All new shapes render as SVG paths. Hit tests use the current
|
|
912
|
+
outline and the existing 12-chord cubic flattening convention, including morphs.
|
|
913
|
+
Invalid geometry uses registry bad-value diagnostics at the parameter/item; missing
|
|
914
|
+
required geometry uses missing-key. New types without paint use undeclared-feature at type.
|
|
915
|
+
|
|
916
|
+
### Rectangle corner radii
|
|
917
|
+
|
|
918
|
+
The small optional `corners` module also accepts this geometry without importing `paint`.
|
|
919
|
+
Declare either `paint` or `corners` (registry feature `paint|corners`); existing paint scenes
|
|
920
|
+
keep the same geometry and addresses. The browser entry is `@anima-format/web/corners`.
|
|
921
|
+
|
|
922
|
+
Rect corners is a four-item nonnegative array [tl,tr,br,bl], AniML corners="1 1 0 0".
|
|
923
|
+
Each value is animatable as tile.corners.0 through .3 (indices without leading zeros).
|
|
924
|
+
The rectangle renders as a path with circular quarter arcs approximated by cubic Béziers,
|
|
925
|
+
using k = 4(√2−1)/3 ≈ 0.5522847498. Each radius is independently clamped to half
|
|
926
|
+
the adjacent width/height; unequal clamped axes produce quarter ellipses. At each corner,
|
|
927
|
+
the two controls move k of the distance from each edge endpoint toward the corner vertex.
|
|
928
|
+
Polygon/star rounding keeps the quadratic construction above. This is an independent
|
|
929
|
+
standard circle approximation; no external runtime algorithm is ported.
|
|
930
|
+
Corners takes precedence over rx; rx remains unchanged without corners. An absent
|
|
931
|
+
corners array cannot be animated. Invalid addresses use unknown-address; invalid array
|
|
932
|
+
length or negative entries use bad-value at the array or offending item.
|
|
933
|
+
|
|
934
|
+
### Compatible path morphs
|
|
935
|
+
|
|
936
|
+
The paint-module animation address pathId.d accepts SVG path strings, including when
|
|
937
|
+
rest geometry comes from a glyph and selected subpaths. Keys contain path data, not
|
|
938
|
+
glyph identifiers; compatible glyph data may be used verbatim. Static d needs no module.
|
|
939
|
+
Rest, all keys and all pose values require identical expanded command lists:
|
|
940
|
+
same case, letters and repetition count. Implicit coordinate repetitions expand; extra moveto pairs become
|
|
941
|
+
lineto. No reversal, resampling or automatic matching occurs. Numeric arguments
|
|
942
|
+
interpolate with eased weight, including relative coordinates and arc parameters.
|
|
943
|
+
Arc flags choose 0 below 0.5, otherwise 1. State blending uses the same rule.
|
|
944
|
+
Intermediate strings use the SVG formatter's five decimal places; exact endpoint
|
|
945
|
+
samples retain their authored strings. Hit tests follow the sampled outline.
|
|
946
|
+
|
|
947
|
+
A state pose may set pathId.d to an SVG path string, using the same validation and
|
|
948
|
+
interpolation as tracks. Invalid paths produce bad-path at the key or pose value with a
|
|
949
|
+
valid-path hint; non-strings produce value-type. Incompatible commands produce morph-mismatch at the key or pose value,
|
|
950
|
+
with a hint naming the first differing zero-based segment and expected/actual command
|
|
951
|
+
(or end). Every key and pose value is checked against rest, keeping state blends compatible.
|
|
952
|
+
AniML encloses path values in JSON double quotes, for example:
|
|
953
|
+
<track target="a.d">0 "M0 0L2 2"; 1 "M0 0L4 4"</track>.
|
|
954
|
+
In a pose attribute, use the path directly: <pose a.d="M0 0L4 4"/>.
|
|
955
|
+
The compiler, formatter and decompiler preserve spaces inside path values.
|
|
956
|
+
|
|
957
|
+
### Transform-independent stroke
|
|
958
|
+
|
|
959
|
+
Stroke scale is normal (default) or fixed, AniML stroke-scale="fixed". It is static.
|
|
960
|
+
Fixed emits SVG vector-effect="non-scaling-stroke": the numeric width passes unchanged
|
|
961
|
+
while node and ancestor scale, rotation and shear transform the outline without
|
|
962
|
+
scaling its stroke. The host SVG viewport follows SVG vector-effect semantics,
|
|
963
|
+
including viewport resizing. This is not an inverse adjustment by one scalar for
|
|
964
|
+
nonuniform transforms. Geometry coordinates and sampled width stay unchanged.
|
|
965
|
+
Invalid scale values produce bad-value at the stroke scale field.
|
|
966
|
+
|
|
967
|
+
### Shared outline lengths
|
|
968
|
+
|
|
969
|
+
All renderers use natural local outline lengths for relative dashes and trims: both
|
|
970
|
+
dasharray components and dashoffset multiply by core's length before output rounding.
|
|
971
|
+
SVG omits `pathLength`. The `toSVG` option `staticLengths` remains accepted for compatibility;
|
|
972
|
+
absolute lengths are now unconditional, including live retained SVG and default `toSVG`.
|
|
973
|
+
Measurements are cached per retained geometry owner; path/radius/dimension/corner changes
|
|
974
|
+
invalidate the measurement, while transforms, colours and trim/offset changes reuse it.
|
|
975
|
+
Lengths include all subpaths and closing edges. Circles use 2πr; rectangles account for their
|
|
976
|
+
clamped elliptical corners; other outlines use current path data, including morphs.
|
|
977
|
+
|
|
978
|
+
Core pathLength parses into absolute lines/cubics, then recursively subdivides cubics
|
|
979
|
+
by de Casteljau at t=0.5 until control polygon length minus chord is at most 0.0001
|
|
980
|
+
grid units per accepted piece, or depth 20. Accepted pieces contribute the mean of
|
|
981
|
+
polygon and chord length. Arc conversion first uses at most 90° cubic pieces (§3),
|
|
982
|
+
so this tolerance describes subdivision flatness, not the initial arc approximation.
|
|
983
|
+
These are independent constructions, extending the shared scene and registry designs
|
|
984
|
+
in the format/graph and web-runtime research cited above. No algorithm follows Rive
|
|
985
|
+
runtime source; no additional NOTICE attribution is required.
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
## composite
|
|
989
|
+
|
|
990
|
+
The optional `composite` module extends 0.1 documents with `requires: ["composite"]`.
|
|
991
|
+
It owns blend modes, source masks, geometry clips, ordered paints/paint visibility,
|
|
992
|
+
stroke placement and sibling draw order. Gradients, dashes, additional geometry,
|
|
993
|
+
corners, morphs and fixed strokes belong to `paint`; using both sets requires
|
|
994
|
+
`requires: ["paint", "composite"]` (AniML `requires="paint composite"`).
|
|
995
|
+
An unavailable module is `unknown-feature`; using an undeclared feature is
|
|
996
|
+
`undeclared-feature` at the offending key/address with an add-feature hint (§11.3).
|
|
997
|
+
Composite alone supports ordinary 0.1 geometry and solid paints.
|
|
998
|
+
|
|
999
|
+
### Blend modes
|
|
1000
|
+
|
|
1001
|
+
`node.blend` (AniML `mix-blend-mode`) selects normal (default), multiply, screen, overlay,
|
|
1002
|
+
darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue,
|
|
1003
|
+
saturation, color or luminosity. It applies CSS `mix-blend-mode` to the node's group, compositing
|
|
1004
|
+
against earlier siblings using SVG/CSS semantics. Blend is static. Additive is deferred: SVG
|
|
1005
|
+
renderers do not consistently implement CSS `plus-lighter`; source-over is not a faithful substitute.
|
|
1006
|
+
|
|
1007
|
+
### Source masks
|
|
1008
|
+
|
|
1009
|
+
`mask: { source, mode, sourceDraws }` names a node, selects alpha, luminance, inverted-alpha or
|
|
1010
|
+
inverted-luminance, and explicitly controls whether the source also draws in its ordinary place.
|
|
1011
|
+
Any false `sourceDraws` use hides that source's ordinary drawing for the document's life.
|
|
1012
|
+
The source subtree is shared through a retained definition and SVG `use`; its geometry, local transforms,
|
|
1013
|
+
opacity and visibility animate normally. Its ancestors contribute coordinate transforms, but not
|
|
1014
|
+
ancestor opacity or visibility. Mask space is the target's parent space. The target transform
|
|
1015
|
+
stays inside a permanent, untransformed mask wrapper. Masks cover three viewBoxes, as knockouts do.
|
|
1016
|
+
Luminance uses SVG luminance masking. Inversion uses an SVG filter with `feComponentTransfer`
|
|
1017
|
+
alpha table `1 0`; inverted luminance first uses `feColorMatrix type="luminanceToAlpha"`, and
|
|
1018
|
+
then multiplies that alpha by `SourceGraphic` alpha with `feComposite operator="in"` before
|
|
1019
|
+
inverting it and using alpha masking. This preserves source opacity and antialias coverage,
|
|
1020
|
+
which `luminanceToAlpha` alone discards. Filter interpolation is sRGB. Source drawing is independent of hiding
|
|
1021
|
+
its ordinary copy. Unknown sources, self references and cycles are load errors.
|
|
1022
|
+
|
|
1023
|
+
### Geometry clips
|
|
1024
|
+
|
|
1025
|
+
`clip: { source, rule? }` names a painted node's outline; optional `rule` is `nonzero`
|
|
1026
|
+
(default) or `evenodd`. A painted node or a group may take a clip. A group clip applies
|
|
1027
|
+
to its entire child drawing, including nested groups; it does not change the source outline.
|
|
1028
|
+
Fill, stroke, visibility, opacity and source compositing do not affect the clip. Source geometry and
|
|
1029
|
+
world transforms animate; coordinates are projected into the target parent space. The SVG
|
|
1030
|
+
`clipPath` contains an untransformed path (curves transformed control-point by control-point).
|
|
1031
|
+
This avoids nonportable nested groups in clip paths while preserving the Firefox retained-leaf
|
|
1032
|
+
rules. Singular target parent transforms yield an empty clip. SVG arcs are converted to cubic
|
|
1033
|
+
curves by Anima's existing parser. A group source is `clip-source-not-painted`.
|
|
1034
|
+
|
|
1035
|
+
### Ordered paints and visibility
|
|
1036
|
+
|
|
1037
|
+
A painted node may replace single `fill` and `stroke` with a nonempty `paints` array. Each entry
|
|
1038
|
+
has exactly one `fill` or `stroke` object, using the same flat/gradient/width/dash fields; later
|
|
1039
|
+
entries paint over earlier entries. `visible` on any paint defaults true and is hold-only
|
|
1040
|
+
animatable. Addresses are `<id>.paints.<index>.<fill|stroke>.<field>`, including gradient stops.
|
|
1041
|
+
Indices must exist, and array structure is static. Single paint visibility addresses are
|
|
1042
|
+
`<id>.fill.visible` and `<id>.stroke.visible`. Multiple strokes currently require center position;
|
|
1043
|
+
noncenter positions on multiple paints are rejected. Paints and single fill/stroke cannot coexist.
|
|
1044
|
+
|
|
1045
|
+
### Stroke placement
|
|
1046
|
+
|
|
1047
|
+
Single `stroke.position` is center (default), inside or outside. Center uses the original width.
|
|
1048
|
+
Inside doubles the width and clips the stroke to the shape's filled outline. Outside doubles the
|
|
1049
|
+
width and luminance-masks it by a white viewport with a black filled outline. Fill is drawn once,
|
|
1050
|
+
before the stroke. Definition geometry samples the same animated dimensions as the visible shape.
|
|
1051
|
+
Masks stay attached for the document's life. Open contours use SVG's implicit closing for the
|
|
1052
|
+
filled outline; fill rules apply to both clipping and exclusion. Position is static.
|
|
1053
|
+
|
|
1054
|
+
### Sibling draw order
|
|
1055
|
+
|
|
1056
|
+
`drawOrder: { target, position, active? }` moves a node before or after a named sibling when
|
|
1057
|
+
active (default true). `drawOrder.active` is an ordinary hold-only boolean animation address;
|
|
1058
|
+
changing it reapplies rules to authored sibling order. Rules run in authored node order, once
|
|
1059
|
+
per frame, removing then inserting their node relative to the target. Multiple rules therefore
|
|
1060
|
+
have deterministic precedence. Cyclic target chains and nonsibling targets are rejected.
|
|
1061
|
+
Rules involving knockout siblings are rejected because knockout permanently groups earlier nodes.
|
|
1062
|
+
Static SVG serializes the current order; web reorders existing DOM siblings without replacing
|
|
1063
|
+
nodes or detaching their masks. The module's draw hook resolves sibling ranks before rendering.
|
|
1064
|
+
For models using `drawOrder`, composite supplies `Player.paintRank(node)`: the node's index
|
|
1065
|
+
in a pre-order tree walk whose child lists use those resolved sibling ranks, cached per player
|
|
1066
|
+
until `drawRevision` changes. Without `drawOrder` the hook is absent and node indices give the
|
|
1067
|
+
same authored pre-order walk. Input hit ordering, opaque blocking and pointer focus use this
|
|
1068
|
+
same key: **foremost** means latest painted. A nested player retains its local paint order
|
|
1069
|
+
inside its host instance's rank.
|
|
1070
|
+
Its registry factory binds to the active runtime's shared registry (MODULES §3.1).
|
|
1071
|
+
Cross-node draw changes conservatively invalidate dependent draw entries, keeping animated
|
|
1072
|
+
source references correct across node and ancestor boundaries.
|
|
1073
|
+
|
|
1074
|
+
### Compositing diagnostics
|
|
1075
|
+
|
|
1076
|
+
`unknown-source` points at mask/clip source or draw target; `self-reference` and `reference-cycle`
|
|
1077
|
+
point at the referencing object. `clip-source-not-painted`, `bad-draw-target`, and `paint-conflict`
|
|
1078
|
+
identify incompatible sources, ordering and paint combinations. Each includes a corrective hint.
|
|
1079
|
+
Structural validation retains the ordinary registry diagnostics and JSON pointers. These features
|
|
1080
|
+
are independently implemented from SVG semantics; no Rive code or algorithms were used.
|
|
1081
|
+
The explicit references and retained ordering follow the lessons in
|
|
1082
|
+
[format/graph research](../docs/research/rive-format-and-graph.md) §6; the sleeping renderer remains
|
|
1083
|
+
as described in [web research](../docs/research/rive-web-runtime.md).
|
|
1084
|
+
|
|
1085
|
+
## bind
|
|
1086
|
+
|
|
1087
|
+
Bind expressions include numeric comparisons `< <= > >=`, typed equality `== !=`, and boolean
|
|
1088
|
+
`and or not`. Ordering requires number operands; equality requires identical operand types.
|
|
1089
|
+
Logical operands must be bool. Precedence from low to high is or, and, not, comparison,
|
|
1090
|
+
addition/subtraction, multiplication/division, unary signs. Evaluation short-circuits logical
|
|
1091
|
+
operators and produces bool, coercible to 0/1 for number destinations. These rules apply to
|
|
1092
|
+
bindings, set actions and derive expressions. Guard scalar expressions use the same parser when
|
|
1093
|
+
bind is declared; trigger and clock guard terms retain their specialized semantics.
|
|
1094
|
+
|
|
1095
|
+
The optional `bind` module requires `requires: ["bind"]`. At document level,
|
|
1096
|
+
`bind: { "ring.stroke.trimEnd": "progress" }` maps animatable addresses to expressions.
|
|
1097
|
+
Property bindings evaluate after all state layers resolve and before property clamping (§7),
|
|
1098
|
+
so a binding overrides a pose, timeline or crossfade on the same address. Resolved bound
|
|
1099
|
+
addresses appear in `Player.values()`, `anima sample` snapshots, dirty slots and `anima inspect`
|
|
1100
|
+
(the `bound` list). Inputs still apply in queued arrival order. A settled binding sleeps until
|
|
1101
|
+
an input write wakes the player; its value changes on the next step, including `step(0)`.
|
|
1102
|
+
|
|
1103
|
+
A motion play state may also use `time: "expression"`; see **Declarative timeline control**.
|
|
1104
|
+
These are clock expressions evaluated before sampling, rather than property bindings.
|
|
1105
|
+
|
|
1106
|
+
### Expressions and types
|
|
1107
|
+
|
|
1108
|
+
Sources are declared scalar input names (including `$` data paths when `data` is declared),
|
|
1109
|
+
finite JSON number literals, hex colours
|
|
1110
|
+
(`#rgb`, `#rrggbb`, `#rrggbbaa`), parentheses, unary `+`/`-`, binary `+ - * /`, and the functions
|
|
1111
|
+
`min(a,b)`, `max(a,b)`, `clamp(x,lo,hi)`, `mix(a,b,t)`, `floor(x)`, `mod(a,b)`,
|
|
1112
|
+
`contains(text,part)`, `lower(text)`. Double-quoted strings, typed comparisons and `and or not` use the grammar above.
|
|
1113
|
+
No scene property reads, boolean literals, assignments or arbitrary calls are allowed. Identifiers use
|
|
1114
|
+
the input-name grammar; a hyphen is part of a name, so write `level - other` for subtraction
|
|
1115
|
+
between inputs. Multiplication/division precede addition/subtraction; binary operators associate
|
|
1116
|
+
left; unary signs have highest precedence. Whitespace is insignificant between tokens.
|
|
1117
|
+
|
|
1118
|
+
A bare bool input retains its bool type for boolean targets. Numeric operations and number
|
|
1119
|
+
targets convert bools to 0/1. Numbers do not implicitly become bools. `min`, `max`, `clamp` and
|
|
1120
|
+
numeric `mix` return numbers; `mix` also accepts two hex colours and a numeric/bool weight,
|
|
1121
|
+
returning a colour. Mixing a number and colour is invalid. Colour interpolation is §5 sRGB
|
|
1122
|
+
channel interpolation (including alpha, rounding and channel clamping); weights may extrapolate.
|
|
1123
|
+
`floor(x)` returns the greatest integer ≤ x. `mod(a,b)` is `a - b*floor(a/b)`
|
|
1124
|
+
(the result has the divisor's sign; a zero divisor resolves to zero). Numeric `mix` is
|
|
1125
|
+
`a + (b-a)*t`; `clamp` is `min(max(x,lo),hi)`. Non-finite arithmetic results
|
|
1126
|
+
(including division by zero) resolve to zero when consumed by another operation or the target,
|
|
1127
|
+
before property clamping. Trigger inputs cannot
|
|
1128
|
+
be values. Evaluators accept a scope map of named inputs, reusable for component input bindings.
|
|
1129
|
+
|
|
1130
|
+
`contains(text, part)` requires two strings and returns bool: true exactly when `part` is a
|
|
1131
|
+
contiguous substring of `text` (case-sensitive ECMAScript `String.prototype.includes`, UTF-16
|
|
1132
|
+
code units). An empty `part` returns true, including for empty `text`.
|
|
1133
|
+
`lower(text)` requires one string and returns a string using Unicode default case conversion
|
|
1134
|
+
(ECMAScript `String.prototype.toLowerCase`, locale-independent). It does not normalize Unicode,
|
|
1135
|
+
trim whitespace or perform full case folding: for example, `lower("İ")` is `"i\u0307"`, and
|
|
1136
|
+
`lower("ẞ")` is `"ß"`, not `"ss"`. Use `contains(lower($label), lower($query))` for
|
|
1137
|
+
case-insensitive filtering. No number/bool/color-to-string coercion occurs. Both calls work in
|
|
1138
|
+
bindings, set actions, derive expressions and scalar guards through the same typed parser.
|
|
1139
|
+
A standalone bool call in a guard uses the existing parenthesized scalar form, e.g.
|
|
1140
|
+
`(contains(lower($label), lower($query)))`.
|
|
1141
|
+
|
|
1142
|
+
### Validation
|
|
1143
|
+
|
|
1144
|
+
`bind-type` rejects incompatible operands or target types; `unknown-input` rejects unknown names.
|
|
1145
|
+
`bind-syntax` rejects malformed expressions, unknown calls, wrong arity or limits (512 tokens,
|
|
1146
|
+
64 nested expressions). Diagnostics point to `/bind/<escaped-address>`, include the 1-based
|
|
1147
|
+
character offset in the expression message, and give a corrective hint. Parser nodes/errors
|
|
1148
|
+
retain zero-based UTF-16 offsets. `bind-cycle` rejects attempted cyclic property references,
|
|
1149
|
+
including self references. Valid expressions read only inputs and cannot form property cycles;
|
|
1150
|
+
acyclic property references are also rejected. Targets use the ordinary `unknown-address`,
|
|
1151
|
+
`not-animatable` and `undeclared-feature` diagnostics. The old object-valued `from/map` form is
|
|
1152
|
+
not supported. `anima schema bind` describes both spellings from the registry.
|
|
1153
|
+
|
|
1154
|
+
The grammar and evaluator are independently written. Strict unknown-name checks and a single
|
|
1155
|
+
post-state pass follow the lessons about silent unresolved bindings and feedback loops in
|
|
1156
|
+
[animation/state research](../docs/research/rive-animation-and-state.md) §C. No Rive source,
|
|
1157
|
+
documentation text or binary format was used.
|
|
1158
|
+
|
|
1159
|
+
<!-- capability: data.bind-listener-actions -->
|
|
1160
|
+
### Input expressions in actions
|
|
1161
|
+
|
|
1162
|
+
Declare `bind`. Every action set (listener, bind machine block, or machine-module action group)
|
|
1163
|
+
uses this single expression/type/snapshot contract. Listener `set` values may be expression strings, e.g.
|
|
1164
|
+
`{"set":{"count":"count+1","previous":"count"}}`. Numeric/bool JSON literals keep their
|
|
1165
|
+
0.1 meaning and type checks; a string is always an expression, never an implicit literal.
|
|
1166
|
+
Number targets accept numeric or bool expressions (bool becomes 0/1); bool targets require bool
|
|
1167
|
+
expressions. Colour expressions remain valid for colour properties and typed event fields;
|
|
1168
|
+
there are no colour inputs, so setting a bool/number input to a colour is `bind-type`.
|
|
1169
|
+
Unknown inputs are `unknown-input`; assigning a trigger is `input-type` (use `fire` instead).
|
|
1170
|
+
Trigger reads and incompatible result types are `bind-type`; parser/arity errors are
|
|
1171
|
+
`bind-syntax`. Diagnostics point to the set value, condition, or event field, with an expression
|
|
1172
|
+
character offset where applicable and a hint. No property reads or assignments are added to the
|
|
1173
|
+
expression language. No runtime dependencies or evaluator copies are introduced: the optional
|
|
1174
|
+
bind module supplies a compile hook to core, returning pure input-scope closures. Merely importing
|
|
1175
|
+
bind does not enable expressions in a document without `requires: ["bind"]`.
|
|
1176
|
+
|
|
1177
|
+
A listener is queued intact. When its action runs at §7.1, evaluate every set expression against
|
|
1178
|
+
one input snapshot **before any write in that action**, then apply `fire`, all sets (clamping each
|
|
1179
|
+
number result to its target min/max), `toggle`, and `emit`. Non-finite arithmetic becomes zero
|
|
1180
|
+
before clamping. Separate actions run sequentially and see prior writes, including host writes
|
|
1181
|
+
queued before them. Queuing two increments before one step increments twice.
|
|
1182
|
+
|
|
1183
|
+
The existing core machine also accepts state `entry` and `exit` blocks and transition `actions`
|
|
1184
|
+
blocks. Each is an object with the same optional `fire`, `set`, `toggle`, `emit` keys as listeners;
|
|
1185
|
+
State `entry`/`exit` blocks require bind even if their values are literals. Transition `actions`
|
|
1186
|
+
requires either bind or machine; without machine it follows the immediate order below, while
|
|
1187
|
+
with machine it follows that chapter's queued order. Bind blocks target local input names; scoped child writes remain a listener/host feature.
|
|
1188
|
+
On initial construction, run each
|
|
1189
|
+
initial state's entry in layer order before its entry emit and timeline events. On a transition,
|
|
1190
|
+
run source exit then transition actions before the existing state-end/transition-start signals;
|
|
1191
|
+
run target entry after the immediate transition-end signal (if any), before target emit and entry
|
|
1192
|
+
timeline events. Each block is a separate snapshot action; writes are immediate and visible to
|
|
1193
|
+
subsequent blocks, transitions in the chain, and later layers. Exit runs on reentry too; blend
|
|
1194
|
+
completion does not rerun actions. Existing chain limits and trigger consumption apply. Pending
|
|
1195
|
+
triggers fired from entry actions are checked during the next step; triggers fired in a step
|
|
1196
|
+
remain subject to that step's ordinary consumption/expiry rule.
|
|
1197
|
+
|
|
1198
|
+
AniML listeners use `set="count=count+1 previous=count"`; spaces and commas within expressions
|
|
1199
|
+
are allowed. Whitespace followed by an input name and `=` begins the next assignment. Machine
|
|
1200
|
+
blocks are JSON attributes, e.g. `<state name="idle" entry='{"set":{"count":0}}'>` and
|
|
1201
|
+
`<on when="tap" to="playing" actions='{"set":{"count":"count+1"}}'/>`.
|
|
1202
|
+
Arithmetic conditions use `when="count > 0 and mod(count, 5) == 0"`. `anima schema listener`,
|
|
1203
|
+
`state`, `transition` and `--json-schema` expose these forms. Input-only scopes avoid the
|
|
1204
|
+
feedback loops described in [animation/state research](../docs/research/rive-animation-and-state.md)
|
|
1205
|
+
§C; snapshot action maps avoid depending on JSON member order.
|
|
1206
|
+
|
|
1207
|
+
## assets
|
|
1208
|
+
|
|
1209
|
+
The optional `assets` module adds an `assets` map and image draw data. Image nodes also
|
|
1210
|
+
require `paint`. Core validation and `Player.step` perform no IO. Fonts remain declarations until P6;
|
|
1211
|
+
audio/blob consumers are optional host modules.
|
|
1212
|
+
|
|
1213
|
+
Each named asset is `{ type, source, storage?, width?, height? }`. `type` is `image`, `font`,
|
|
1214
|
+
`audio` or `blob`. `source` is required and nonempty: a data URI or URL (relative URLs are
|
|
1215
|
+
relative to the document). Optional `storage` is `embedded` for data URIs or `referenced`
|
|
1216
|
+
for URLs; omitted storage is inferred. A disagreement is `asset-storage` at `/assets/<name>/storage`.
|
|
1217
|
+
`width` and `height`, when present, are positive intrinsic dimensions, in grid units for
|
|
1218
|
+
layout. Omitted dimensions are decoded by the host before drawing. Declared dimensions override
|
|
1219
|
+
decoded dimensions independently. Decoded pixel counts remain separate host resource information;
|
|
1220
|
+
authored width/height change layout, never the texel count used for clamped edge cropping.
|
|
1221
|
+
Browser and CLI decoders supply these counts without adding a document field. Data URIs use percent encoding or padded standard base64;
|
|
1222
|
+
malformed encoding is `bad-data-uri` at the source. An empty source is `missing-source`;
|
|
1223
|
+
an absent source is `missing-key` at the asset object. Messages carry a repair hint.
|
|
1224
|
+
|
|
1225
|
+
AniML declares `<asset name="photo" type="image" src="photo.png" storage="referenced"/>`
|
|
1226
|
+
at document or component level; registry `source` maps to `src`. All other field spellings match JSON.
|
|
1227
|
+
A component resolves asset names in its own catalog, then its enclosing component/document catalogs
|
|
1228
|
+
up to the root. A local declaration shadows the same outer name, even if its type is wrong.
|
|
1229
|
+
This scope applies to image nodes, image/audio `asset-ref` values and event audio clips. Missing or
|
|
1230
|
+
wrong-kind references report the offending node, event or data property/value JSON pointer;
|
|
1231
|
+
default data asset errors point to `/data/models/<model>/properties/<property>/default`.
|
|
1232
|
+
Image hit targets use their current rectangular bounds, including animated width/height.
|
|
1233
|
+
|
|
1234
|
+
The browser module `@anima-format/web/assets` registers assets and the SVG image backend;
|
|
1235
|
+
image documents also import `@anima-format/web/paint`. Images decode before `anima:load` and
|
|
1236
|
+
initial `ready` resolution. URLs resolve relative to the icon's `src`, or `document.baseURI`
|
|
1237
|
+
for inline/programmatic documents. `icon.assets = (name, info) => url | Blob | Promise<url | Blob>`
|
|
1238
|
+
replaces the source before decode; `info` is a copy of the authored declaration. A resolver rejection
|
|
1239
|
+
or image decode failure empties the icon and dispatches `anima:error` with `asset-load`, the
|
|
1240
|
+
source pointer, message and hint. Embedded data URIs require no network. Resolved data is private
|
|
1241
|
+
to the loaded model; source declarations are preserved. Superseded loads cannot install their scene.
|
|
1242
|
+
Audio/blob byte access and policy are specified below; fonts remain declarations.
|
|
1243
|
+
|
|
1244
|
+
`anima build --embed` embeds local referenced assets (paths relative to the source file), sets
|
|
1245
|
+
storage to `embedded`, and visits root and component-local catalogs, including imported libraries.
|
|
1246
|
+
Imported sources resolve relative to the file declaring that asset; diagnostics keep the complete
|
|
1247
|
+
component asset pointer. The built file's embedded assets work after the source tree is removed. It supports AniML and
|
|
1248
|
+
AniML scripts in HTML. Already embedded assets are unchanged. Other URL schemes cannot be read
|
|
1249
|
+
by this disk build and report `asset-load`; HTTP asset fetching is a browser host responsibility.
|
|
1250
|
+
`anima render` embeds local image sources and decodes intrinsic sizes through resvg before
|
|
1251
|
+
rendering; other asset types remain declarations. IO/decode failures report `asset-load` at
|
|
1252
|
+
`/assets`, with a message and hint. Build inspection and encoding are specified below.
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
### P5b asset pipeline
|
|
1256
|
+
|
|
1257
|
+
Asset declarations also accept `size` (nonnegative integer bytes), `mime` (MIME string),
|
|
1258
|
+
`metadata` (a JSON object, AniML `metadata='{"credit":"self"}'`), and the build-only
|
|
1259
|
+
`encode`, `quality` and `export` options. Resolver metadata includes every authored field;
|
|
1260
|
+
its copy is detached, including nested metadata. Source declarations are never mutated by host IO.
|
|
1261
|
+
`encode` is `png`, `jpeg` or `webp`; `quality` is in [0,1] and requires `encode`.
|
|
1262
|
+
These two options are image-only. PNG ignores quality; JPEG/WebP pass it to the browser encoder.
|
|
1263
|
+
Audio-only `start` and `end` are clip offsets in seconds; `duration` is the full source duration.
|
|
1264
|
+
Omitted start is zero and omitted end is decoded duration. Require `0 <= start < end <= duration`
|
|
1265
|
+
when the relevant bounds are known. Wrong-type options are `asset-option` at the option;
|
|
1266
|
+
invalid bounds are `asset-clip` at `/assets/<name>/start` or `/end`. Both carry repair hints.
|
|
1267
|
+
All fields have identical JSON/AniML names except `source` → `src`.
|
|
1268
|
+
|
|
1269
|
+
`icon.assetPolicy` is `local` (default) or `any`; other assignments throw RangeError.
|
|
1270
|
+
Without a custom resolver, local policy permits data URIs and same-origin HTTP(S)/blob URLs,
|
|
1271
|
+
relative to the document source URL. Any permits other origins, retaining the protocol allowlist.
|
|
1272
|
+
A custom `icon.assets(name, info)` resolver explicitly authorizes its returned URL or Blob;
|
|
1273
|
+
relative returned URLs still resolve against the document source URL. This policy applies to
|
|
1274
|
+
image loading and event audio; browser CORS and media permissions still apply. Failed policy,
|
|
1275
|
+
network or image decoding emits `asset-load` at the source with a resolver/policy repair hint.
|
|
1276
|
+
Changing policy affects subsequent loads, not already decoded resources.
|
|
1277
|
+
|
|
1278
|
+
Import `@anima-format/web/media` for lazy audio/blob byte access; it imports assets and contributes
|
|
1279
|
+
no new document fields. `icon.asset(name)` returns a promise of ArrayBuffer for audio/blob and
|
|
1280
|
+
source URL for images/fonts. Byte loads use the same resolver/policy, cache by model and asset,
|
|
1281
|
+
retry after a failure, and emit `asset-load` before rejecting with its diagnostics. Unknown names
|
|
1282
|
+
reject with RangeError. Audio decoding remains the consumer's responsibility; events uses Web
|
|
1283
|
+
Audio on demand after activation, as specified in events. Clip events start at `start` and play
|
|
1284
|
+
for `end-start`; invalid decoded bounds emit `event-audio-load`. Neither policy nor media IO changes
|
|
1285
|
+
Player.step output. The assets entry imports a separately budgeted `assetio` host-IO entry automatically, and preloads
|
|
1286
|
+
images in reachable component catalogs as well as the root. Keeping media in its own entry follows the optional consumer principle in
|
|
1287
|
+
`docs/research/rive-web-runtime.md` §1; no native code or document format is copied.
|
|
1288
|
+
|
|
1289
|
+
Build inspects local and embedded PNG/JPEG/WebP/WAV/MP3/FLAC signatures, dimensions or duration.
|
|
1290
|
+
Extension/MIME/signature disagreement or wrong media kind is `asset-format` at the asset source;
|
|
1291
|
+
read/truncation/decode errors are `asset-load`. Metadata `size`, `mime` and audio `duration` are
|
|
1292
|
+
written from inspected bytes; clip bounds are checked against inspected duration. PNG/JPEG pixels
|
|
1293
|
+
are validated with resvg, WebP with cached Chromium. Audio inspection validates container/frame
|
|
1294
|
+
headers, not complete audio decoding; host decoding remains authoritative. Disk build never fetches
|
|
1295
|
+
network URLs. Blob payloads remain opaque: their bytes are not decoded or container-validated,
|
|
1296
|
+
MIME comes from the declaration or URL hint (otherwise application/octet-stream), size is the
|
|
1297
|
+
byte length, and audio-only duration is never attached. Build embedding is byte-preserving without `encode`. PNG encoding runs in-process;
|
|
1298
|
+
WebP input needs Chromium to decode because resvg cannot decode WebP. JPEG/WebP encoding uses the
|
|
1299
|
+
cached headless Chromium canvas. A command owns one lazy browser shared by all images and HTML
|
|
1300
|
+
scripts; each operation closes its page and the command closes its browser on success or failure.
|
|
1301
|
+
PNG-only commands do not launch a browser unless they need WebP input decoding. Missing/unlaunchable Chromium is `build-encoder-unavailable` at
|
|
1302
|
+
`/assets/<name>/encode` (or `/source` for WebP validation), hinting `CHROMIUM_PATH`.
|
|
1303
|
+
`export=false` excludes bytes from embedding/encoding/inspection, preserving the source declaration
|
|
1304
|
+
and resolver name so references remain valid; the host supplies that source. An already embedded
|
|
1305
|
+
source becomes `anima:<name>` with referenced storage, so its bytes are actually excluded; this
|
|
1306
|
+
placeholder requires a resolver. It does not delete
|
|
1307
|
+
referenced names from the asset map. Build transformations apply equally to AniML and HTML scripts.
|
|
1308
|
+
|
|
1309
|
+
`anima import file.svg [-o out.animl]` imports paths, rects, circles, ellipses, lines/polygons,
|
|
1310
|
+
groups, inherited fills/strokes (hex, basic named and RGB/RGBA colours), user-space linear/radial gradients, and translate/scale/rotate
|
|
1311
|
+
or nonskew affine matrix transforms. Transform lists become nested groups to preserve order.
|
|
1312
|
+
Unsupported elements/attributes/paint servers/transforms emit `import-unsupported` warnings with
|
|
1313
|
+
SVG element/attribute paths. Unsupported geometry is omitted with a warning. The import never
|
|
1314
|
+
fetches external references. `anima import file.json` accepts Lottie shape layers, groups, paths,
|
|
1315
|
+
static rects/ellipses, fills/strokes, transforms and scalar/vector/path keyframes with hold or
|
|
1316
|
+
cubic easing; stroke trim paths map to normalized trim fractions. Layer order reverses to preserve
|
|
1317
|
+
Lottie's back-to-front order. Unsupported fields/features (including images, expressions, masks,
|
|
1318
|
+
effects, skew, spatial curves, partial layer time ranges, animated anchors/primitive sizes and
|
|
1319
|
+
paint opacity) warn at their JSON pointer. These warnings describe lossy imports, not exact parity.
|
|
1320
|
+
Malformed XML/JSON or invalid import roots/clocks report `import-invalid` at the empty root
|
|
1321
|
+
pointer with a source-format repair hint. All imported documents are validated before output. JSON command output includes diagnostics and
|
|
1322
|
+
text or output path. The importer is independent of Rive.
|
|
1323
|
+
|
|
1324
|
+
## components
|
|
1325
|
+
|
|
1326
|
+
The optional `components` module requires `requires: ["components"]`. A document's `components`
|
|
1327
|
+
map holds named nested documents: `grid`, optional `viewBox`, `inputs`, `nodes`, `timelines`,
|
|
1328
|
+
`machine`, `listeners`, `colors`, `glyphs`, `a11y`, `hit` and `bind`. Definitions inherit the enclosing
|
|
1329
|
+
`requires` list. References to components resolve in the enclosing document's component map,
|
|
1330
|
+
including from other definitions; all definitions, even unused ones, are validated. Each placement
|
|
1331
|
+
owns a separate player; compiled definitions and static geometry may be shared.
|
|
1332
|
+
Component definition keys and literal references allow colon-separated name segments. Compiled
|
|
1333
|
+
library documents retain their stable lookup aliases in `meta.componentNames`, a map from qualified
|
|
1334
|
+
import name to definition key. Exact keys take precedence; an original name resolves when unique.
|
|
1335
|
+
Ambiguous original names report `ambiguous-component` at the reference, with qualified alternatives
|
|
1336
|
+
in the verbose hint. Legacy generated keys remain valid; a missing `import-<number>-<name>` lookup
|
|
1337
|
+
resolves by its original suffix when unique and reports the same ambiguity otherwise. See
|
|
1338
|
+
[the AniML naming scheme](ANIML.md#11-libraries-and-templates-build-time).
|
|
1339
|
+
|
|
1340
|
+
An instance node has `{id, type: "instance", component, inputs?, speed?, paused?}` and the ordinary
|
|
1341
|
+
node transform, opacity and visibility properties. `component` names a definition. `inputs` maps its
|
|
1342
|
+
bool/number inputs to literals or strings `{expression}`. Expressions use the bind grammar and
|
|
1343
|
+
require `bind`; their scope is the parent's applied inputs. Triggers cannot be initialized with
|
|
1344
|
+
values. Omitted inputs retain their component defaults. Literal initializers apply once, before
|
|
1345
|
+
subsequent queued writes. Number inputs clamp to their declared bounds.
|
|
1346
|
+
|
|
1347
|
+
Component inputs must not use any key in the instance node's registry shape. This includes `id`,
|
|
1348
|
+
`type`, `component`, `inputs`, transform keys, `opacity`, `visible`, `speed`, `paused`, and keys
|
|
1349
|
+
installed by other node modules such as `blend`, `mask` and `clip`. Those names retain their plain
|
|
1350
|
+
instance attributes and `<instanceId>.<property>` addresses. `reserved-input-name` points at the
|
|
1351
|
+
input declaration (`/components/<name>/inputs/<input>`, with source line/column in AniML), with the
|
|
1352
|
+
hint `rename the input; <name> is an instance property (e.g. use pos-x)`. The reserved set is derived
|
|
1353
|
+
from the active registry's instance shape, including module keys; it is not a separate hand list.
|
|
1354
|
+
|
|
1355
|
+
### Stepping, writes and events
|
|
1356
|
+
|
|
1357
|
+
After §7 step 5, the parent evaluates instance input expressions, then resolved parent addresses
|
|
1358
|
+
that name instance inputs, and steps children in node order with `dt * speed` (`speed` defaults to
|
|
1359
|
+
1, must be finite and ≥0). Each child's own state speeds multiply this speed. A `paused` instance
|
|
1360
|
+
(default false) retains its clocks and queued input actions without stepping. An invisible instance
|
|
1361
|
+
whose player is settled and has no queued writes is not stepped. Invisible running players finish
|
|
1362
|
+
normally. Reduced-motion policy propagates from the parent before stepping.
|
|
1363
|
+
|
|
1364
|
+
A parent's actions and host API may address `<instanceId>.<input>` in `set`, `toggle` and `fire`.
|
|
1365
|
+
Bindings, poses and tracks may target a bool/number instance input at the same address; explicit
|
|
1366
|
+
resolved addresses override inline instance expressions. Writes reach the child before its step.
|
|
1367
|
+
Nested action addresses continue with dots (`outer.inner.count`). Invalid inputs/types are load
|
|
1368
|
+
errors, just like local inputs.
|
|
1369
|
+
|
|
1370
|
+
Pointers are transformed through the inverse instance world matrix and routed to each child
|
|
1371
|
+
player. Invisible or singular instances receive `leave`. The child's own hit areas and listeners
|
|
1372
|
+
then operate in local grid units; parent listener targets on instances use their local viewBox.
|
|
1373
|
+
Instances do not establish a viewport clip or automatic fit by default; the optional `nested`
|
|
1374
|
+
module adds explicit fit/align into size. Use composite clipping when a viewport clip is needed.
|
|
1375
|
+
|
|
1376
|
+
A child's events are returned by that parent step as `<instanceId>.<event>`; nested names continue
|
|
1377
|
+
with dots. Event layers use slash scope (`outer/inner/layer`), retaining null for listener events.
|
|
1378
|
+
The parent queues the bubbled event as a one-step trigger and queues matching parent listeners
|
|
1379
|
+
(`on: "badge.selected"`). Those actions and parent transition conditions (`when: "badge.selected"`)
|
|
1380
|
+
are applied on the **next parent step**, including `step(0)`. Child events follow the parent's own
|
|
1381
|
+
events in deterministic instance order. Pending bubbled events/actions keep the parent unsettled.
|
|
1382
|
+
This single pass prevents event feedback from rerunning the state machine in one step. A parent is
|
|
1383
|
+
settled only when its own machine and all unpaused children are settled and no actions are queued.
|
|
1384
|
+
|
|
1385
|
+
### Drawing and inspection
|
|
1386
|
+
|
|
1387
|
+
An instance contributes a renderer-neutral group with its local transform and the child's draw
|
|
1388
|
+
tree as children. Draw entries have scoped ids (`badge/dot`, `outer/inner/dot`); resources and
|
|
1389
|
+
source references are remapped into the enclosing draw tree. SVG uses scoped ids under the host's
|
|
1390
|
+
unique id prefix. Clip, mask, blend and paint behavior use the ordinary draw/backend contract.
|
|
1391
|
+
|
|
1392
|
+
`Player.states()` and `inspect()` include slash-scoped child layers. `Player.values()` and sample
|
|
1393
|
+
snapshots include child inputs (`badge/count`) and animated values (`badge/dot.r`), recursively;
|
|
1394
|
+
selected snapshots accept those same scoped addresses. `anima inspect` lists placed instances and
|
|
1395
|
+
their initial states. The playground's state panel shows live instance states.
|
|
1396
|
+
|
|
1397
|
+
### Limits and diagnostics
|
|
1398
|
+
|
|
1399
|
+
No recursive definitions are allowed: direct/indirect cycles are `component-cycle`. Expanded
|
|
1400
|
+
placement depth is at most 8 and the total number of instances is at most 256, including repeated
|
|
1401
|
+
nested placements (`limit-exceeded`). Ordinary per-document node/key/layer limits still apply to
|
|
1402
|
+
each definition. `unknown-component` points at the instance's component name; `unknown-input` and
|
|
1403
|
+
`input-type` point at bad input initializers/actions. Bind expressions retain `bind-syntax`,
|
|
1404
|
+
`bind-type` and `unknown-input`. `unknown-event` rejects unresolved listener event names;
|
|
1405
|
+
`component-event-collision` rejects a component input and event sharing a scoped address.
|
|
1406
|
+
Diagnostics retain JSON pointers and corrective hints; errors inside definitions point under
|
|
1407
|
+
`/components/<name>`. No partially valid instance tree escapes a failed load.
|
|
1408
|
+
|
|
1409
|
+
The optional `nested` module adds explicit time/fps, fit/align, scalar outputs and component-valued
|
|
1410
|
+
selection. Its normative contract is [modules/nested/SPEC.md](../modules/nested/SPEC.md).
|
|
1411
|
+
|
|
1412
|
+
This implementation is independent. Explicit names, validation before references are used and
|
|
1413
|
+
retained nested transforms follow [graph research](../docs/research/rive-format-and-graph.md) §6;
|
|
1414
|
+
a single ordered update pass avoids the feedback loops discussed in
|
|
1415
|
+
[state research](../docs/research/rive-animation-and-state.md) §C. Sleep behavior follows
|
|
1416
|
+
[web runtime research](../docs/research/rive-web-runtime.md), with no separate instance scheduler.
|
|
1417
|
+
|
|
1418
|
+
## surface
|
|
1419
|
+
|
|
1420
|
+
The optional `surface` module extends `paint` and `assets` with image paints, patch slicing and
|
|
1421
|
+
paint feathering. The independent algorithms below follow SVG semantics, not Rive source.
|
|
1422
|
+
The split keeps paint (5120 B), composite (4096 B) and assets (2048 B) within their existing
|
|
1423
|
+
budgets; surface has an 8192 B incremental budget. Browser import `@anima-format/web/surface`
|
|
1424
|
+
also registers paint; assets and components remain separate imports. This follows the optional
|
|
1425
|
+
backend and sleeping-renderer lessons in [web research](../docs/research/rive-web-runtime.md) §13
|
|
1426
|
+
and the renderer-neutral contract in [MODULES §3.1](MODULES.md#31-the-draw-layer).
|
|
1427
|
+
|
|
1428
|
+
### Image paints
|
|
1429
|
+
|
|
1430
|
+
A fill or stroke with `type: "image"` requires `asset`, `imageWidth`, `imageHeight`; the last two
|
|
1431
|
+
are strictly positive tile sizes in grid units. It also needs `assets` in `requires` and a declared
|
|
1432
|
+
image asset. It replaces `color`, gradient coordinates and `stops`. Usual stroke width, cap, join,
|
|
1433
|
+
dash, trim, visibility and ordered-paint semantics still apply. Optional `imageX`, `imageY`
|
|
1434
|
+
(default 0), `imageRotate` (degrees, default 0), `imageScaleX`, `imageScaleY` (default 1) transform
|
|
1435
|
+
the tile in local coordinates: scale, then rotate about the origin, then translate. Static zero
|
|
1436
|
+
scales are load errors. Transform fields are animatable. The SVG backend uses a repeating
|
|
1437
|
+
`pattern` with `patternUnits="userSpaceOnUse"`, `patternTransform`, and an image stretched to the
|
|
1438
|
+
specified tile size. Intrinsic aspect ratio does not affect image paints.
|
|
1439
|
+
|
|
1440
|
+
Optional `modulate` is a literal hex colour. It multiplies non-premultiplied sRGB R, G, B and alpha
|
|
1441
|
+
channels independently by the colour channels divided by 255; SVG uses an sRGB `feColorMatrix`
|
|
1442
|
+
on the image inside the pattern. It is static. Defaults to identity. No slot modulation is defined.
|
|
1443
|
+
Draw data contains the resolved URL, tile dimensions, transform and normalized modulation channels.
|
|
1444
|
+
AniML uses `<imagePaint asset="tile" image-width="8" image-height="8" paint="stroke"/>`
|
|
1445
|
+
inside the painted node (`paint` defaults to fill); the registry supplies both spellings.
|
|
1446
|
+
See `examples/image-paints.animl` and `spec/draw-conformance/surface-image/`.
|
|
1447
|
+
|
|
1448
|
+
`unknown-asset`, `asset-type`, `undeclared-feature`, `missing-key`, `paint-conflict`, `bad-color`
|
|
1449
|
+
and `bad-value` point at the affected paint/field and give a corrective hint. Image-specific
|
|
1450
|
+
fields on another paint type are errors. Sources resolve through the same assets mechanism as
|
|
1451
|
+
image nodes. No IO occurs during stepping.
|
|
1452
|
+
|
|
1453
|
+
### N-slice patches
|
|
1454
|
+
|
|
1455
|
+
`slice: { x, y, width, height, modes? }` on an image, group or component instance defines a source
|
|
1456
|
+
rectangle using the first and last entries of strictly increasing `x`/`y` cut arrays (2–9 entries
|
|
1457
|
+
per axis, grid units, including both bounds). There are at most 64 patches. `width`/`height` are
|
|
1458
|
+
positive target sizes. `modes` has one entry per patch in row-major order: `stretch`, `repeat` or
|
|
1459
|
+
`hidden`; omission means stretch everywhere. Slice structure and target size are static.
|
|
1460
|
+
|
|
1461
|
+
For four or more cuts on an axis, the first and last bands keep their authored lengths. Interior
|
|
1462
|
+
bands divide the remaining target length in proportion to their source lengths. If the target
|
|
1463
|
+
is smaller than the two border lengths, borders shrink proportionally and all interior bands
|
|
1464
|
+
collapse to zero. Two or three cuts divide the entire target proportionally, since there is no
|
|
1465
|
+
interior band bounded by two distinct border bands. Target origin is the first source cut for
|
|
1466
|
+
groups/instances, or the image's `x0`,`y0` for images. Image source cuts must begin at zero and end
|
|
1467
|
+
at the asset's declared intrinsic width/height; those dimensions are required. Slicing replaces
|
|
1468
|
+
image fit/alignment and node width/height layout. Node transforms apply to the resulting drawing.
|
|
1469
|
+
|
|
1470
|
+
Stretch maps each source rectangle to its target rectangle, independently on each axis. Repeat
|
|
1471
|
+
tiles that rectangle at its original grid size, from the target's upper-left corner, clipping the
|
|
1472
|
+
last partial tile; it does not scale either axis. Hidden contributes no pixels. SVG implements
|
|
1473
|
+
patches using clipped nested SVG viewports and patterns referencing a shared source drawing.
|
|
1474
|
+
|
|
1475
|
+
For vector groups and components the source is the live rendered local subtree, clipped at each
|
|
1476
|
+
cut. Each patch scales the *whole* source appearance, including strokes, filters and compositing.
|
|
1477
|
+
This is rendered-output slicing, not path deformation: geometry, hit testing, component inputs,
|
|
1478
|
+
state and pointer coordinates remain in source space. Child animations remain live. Fixed-width
|
|
1479
|
+
strokes are fixed in the source drawing before patch scaling. True geometry deformation and
|
|
1480
|
+
inverse patch pointer mapping are deferred; duplicating stateful component players per patch
|
|
1481
|
+
would change document semantics. The resolved draw data lists source/target rectangles and modes.
|
|
1482
|
+
|
|
1483
|
+
Diagnostics: `slice-cuts` for unordered cuts; `slice-limit` beyond 9 cuts; `slice-modes` for a wrong
|
|
1484
|
+
mode count; `slice-bounds` for image intrinsic-bound mismatch; `slice-type` for an unsupported node
|
|
1485
|
+
kind. Each points into `slice` and includes a hint. Structural errors use ordinary registry codes.
|
|
1486
|
+
`spec/draw-conformance/surface-slice/` records the 9-patch result, including repeat and hidden patches.
|
|
1487
|
+
AniML uses a `<slice x="…" y="…" width="…" height="…" modes="…"/>` node child.
|
|
1488
|
+
Cut lists decode to numbers and modes to strings. See [ANIML](ANIML.md#surface-module) and
|
|
1489
|
+
`examples/slice.animl` for the syntax and round-trip contract.
|
|
1490
|
+
|
|
1491
|
+
### Feather falloff
|
|
1492
|
+
|
|
1493
|
+
A paint may have `feather: { inner?, outer?, offset?, enabled?, space? }`. Amounts are nonnegative
|
|
1494
|
+
grid distances, default 0. Offset is a two-number local vector, default `[0,0]`; enabled defaults
|
|
1495
|
+
true and disabling preserves all settings. Space defaults `local`. Inner, outer, offset components
|
|
1496
|
+
and enabled are animatable via `fill.feather.inner`, `stroke.feather.enabled`, etc., including
|
|
1497
|
+
ordered paint addresses. Feathering applies independently to each paint, before node opacity.
|
|
1498
|
+
|
|
1499
|
+
Let P be the premultiplied RGBA paint image, A its original alpha, and T the offset translation.
|
|
1500
|
+
Let B(r) be a Gaussian convolution of T(P) with standard deviation r/3, with B(0)=T(P).
|
|
1501
|
+
The output is `A * B(inner) + (1-A) * B(outer)`, componentwise in premultiplied sRGB; alpha is
|
|
1502
|
+
clamped to [0,1]. For a fully covered hard edge with no offset, Gaussian falloff has its midpoint
|
|
1503
|
+
at the edge and about 99.7% of its transition within three standard deviations on either side.
|
|
1504
|
+
This is an explicit Anima Gaussian model, not a claim of pixel parity with Rive's feather model.
|
|
1505
|
+
At zero inner/outer and zero offset the original paint is unchanged.
|
|
1506
|
+
|
|
1507
|
+
SVG uses `feOffset`, two `feGaussianBlur` primitives (`stdDeviation=amount/3`), `feComposite`
|
|
1508
|
+
in/out with SourceAlpha, then an arithmetic sum (`k2=k3=1`), all with sRGB filter interpolation.
|
|
1509
|
+
The filter region expands the document viewBox by three times the greatest amount plus the
|
|
1510
|
+
largest offset component. Root SVG viewport clipping still applies. Separate paint leaves prevent
|
|
1511
|
+
feathering a fill from blurring an unfeathered stroke.
|
|
1512
|
+
|
|
1513
|
+
World-space feathering cannot preserve isotropic falloff through arbitrary local nonuniform
|
|
1514
|
+
transforms in this SVG implementation. `space: "world"` is represented in draw data; when enabled,
|
|
1515
|
+
the SVG backend throws an error carrying `code: "unsupported-render"`, the source JSON pointer,
|
|
1516
|
+
a message and a hint to use local space. Canvas support is deferred to Pc. Disabled world feathering
|
|
1517
|
+
renders normally. Unknown spaces and negative/nonfinite amounts receive registry diagnostics.
|
|
1518
|
+
`spec/draw-conformance/surface-feather/` defines resolved draw data independent of browser rasterization;
|
|
1519
|
+
Chromium/Firefox pixel tests check the Gaussian halo, disabled state and independent stroke.
|
|
1520
|
+
AniML uses `<feather paint="fill|stroke" …/>` node children, or a feather child within an ordered
|
|
1521
|
+
`fillPaint`/`strokePaint`. It retains the parent paint and preserves empty and disabled settings;
|
|
1522
|
+
see [ANIML](ANIML.md#surface-module) and `examples/feather.animl`.
|
|
1523
|
+
|
|
1524
|
+
## motion
|
|
1525
|
+
|
|
1526
|
+
`requires: ["motion"]` opts into this chapter; AniML uses `requires="motion"`.
|
|
1527
|
+
Loading the module alone does not change a 0.1 document's playback. It adds no renderer,
|
|
1528
|
+
network operations or runtime dependencies. All times and work bounds are seconds.
|
|
1529
|
+
The design follows the independent-playback, explicit composition and unit-consistency
|
|
1530
|
+
lessons in [animation research](../docs/research/rive-animation-and-state.md#1-linear-animations)
|
|
1531
|
+
and the small imperative handle in [web research](../docs/research/rive-web-runtime.md).
|
|
1532
|
+
No Rive code is used. The elastic formulas below are reflections of Anima's existing
|
|
1533
|
+
specified elastic-out formula, not an implementation of Rive's amplitude ramp or period-zero fallback.
|
|
1534
|
+
|
|
1535
|
+
### Curves
|
|
1536
|
+
|
|
1537
|
+
<!-- capability: animation.elastic-in -->
|
|
1538
|
+
<!-- capability: animation.elastic-in-out -->
|
|
1539
|
+
`elastic-in(a,p)` is `1 - elastic(a,p)(1-u)`; `elastic-in-out(a,p)` is
|
|
1540
|
+
`elastic-in(a,p)(2u)/2` for `u < 1/2`, otherwise `1/2 + elastic(a,p)(2u-1)/2`.
|
|
1541
|
+
Both require finite `a >= 0`, `p > 0`, and have exact 0/1 endpoints. The same strings
|
|
1542
|
+
work on left keys and transition eases, with the same arguments in both syntaxes.
|
|
1543
|
+
Invalid motion curves report `bad-ease` at the ease; a missing requirement reports
|
|
1544
|
+
`undeclared-feature`, with a hint to add motion.
|
|
1545
|
+
|
|
1546
|
+
<!-- capability: animation.cubic-value -->
|
|
1547
|
+
`value-cubic(x1,y1,x2,y2)` on a **numeric** left key is a value curve rather than a
|
|
1548
|
+
weight. Solve the existing monotone cubic x curve for parameter `s`; evaluate the
|
|
1549
|
+
cubic with ordinate controls `(leftValue,y1,y2,rightValue)`. The time controls are
|
|
1550
|
+
normalized fractions in `[0,1]`; ordinate controls use the property's units. All
|
|
1551
|
+
arguments must be finite. Equal endpoints can therefore move between the keys.
|
|
1552
|
+
Endpoints, holds, before-first and after-last sampling retain §5 rules. A value curve
|
|
1553
|
+
on colours, discrete channels, or transition eases is `bad-ease`; it cannot shape a
|
|
1554
|
+
crossfade. Invalid controls are `bad-ease` with a syntax/range hint. The sampler is
|
|
1555
|
+
compiled per segment; there is no mutable interpolator shared between tracks.
|
|
1556
|
+
|
|
1557
|
+
### Typed channels and mixing
|
|
1558
|
+
|
|
1559
|
+
<!-- capability: animation.keys-signed-integer -->
|
|
1560
|
+
<!-- capability: animation.keys-unsigned-integer -->
|
|
1561
|
+
<!-- capability: animation.keys-string -->
|
|
1562
|
+
<!-- capability: animation.keys-reference -->
|
|
1563
|
+
A node can declare `channels: { name: { type, value } }`, where `type` is `int`,
|
|
1564
|
+
`uint`, `string`, or `reference`. A channel is addressed `node.channels.name` in
|
|
1565
|
+
tracks and poses. `int` and `uint` hold 32-bit integers (signed or unsigned);
|
|
1566
|
+
`string` holds arbitrary strings, including whitespace and empty strings;
|
|
1567
|
+
`reference` holds an existing node id in this scene. They are typed scene data,
|
|
1568
|
+
readable through node handles; a reference channel does not implicitly swap geometry
|
|
1569
|
+
or a paint. Future consumers must explicitly define what they do with the reference.
|
|
1570
|
+
AniML declares `<channel name="label" type="string" value='"Ready"'/>` inside its
|
|
1571
|
+
node, and uses quoted JSON strings in keyframe text. Numerical channel values use
|
|
1572
|
+
ordinary numbers. A channel value in a pose also uses a quoted JSON string when
|
|
1573
|
+
string-valued. The registry defines both spellings and generates CLI/schema output.
|
|
1574
|
+
|
|
1575
|
+
All four types are hold-only: left value until the exact next key. An explicit ease
|
|
1576
|
+
other than `hold` is `bad-ease`. Invalid integer values are `motion-integer` at the
|
|
1577
|
+
value, with the allowed interval as hint; wrong string values are `value-type`;
|
|
1578
|
+
unresolved references are `motion-reference` with an existing-node hint. Validation
|
|
1579
|
+
covers document values, keyframes and poses. Structural channel/name errors use §11 codes.
|
|
1580
|
+
|
|
1581
|
+
<!-- capability: animation.mix-discrete -->
|
|
1582
|
+
<!-- capability: animation.mix-numeric -->
|
|
1583
|
+
A motion document's discrete channels and bool properties replace the incoming value
|
|
1584
|
+
for **any positive mix**, including at the start of a crossfade. An explicit instance mix of zero writes
|
|
1585
|
+
nothing. Numbers, colours and morphable paths use the existing interpolation rules.
|
|
1586
|
+
An explicit instance writes `mix(incoming, sample, weight)` in composition order;
|
|
1587
|
+
weights are clamped to `[0,1]`. These bool rules apply only to motion documents;
|
|
1588
|
+
ordinary 0.1 bool crossfades still use §7's OR rule. During a state crossfade, a
|
|
1589
|
+
channel absent from the target immediately passes through the value below it.
|
|
1590
|
+
|
|
1591
|
+
### Timeline clock
|
|
1592
|
+
|
|
1593
|
+
<!-- capability: animation.quantized-playback -->
|
|
1594
|
+
<!-- capability: animation.work-area -->
|
|
1595
|
+
<!-- capability: animation.reverse -->
|
|
1596
|
+
A timeline may declare `speed` (finite, default 1), `quantize` (finite samples/second,
|
|
1597
|
+
strictly positive), and `work: [start,end]` with `0 <= start < end <= duration`.
|
|
1598
|
+
AniML uses attributes `speed`, `quantize`, `work="start end"`. Invalid work bounds
|
|
1599
|
+
report `motion-work` at `/timelines/<name>/work`, with an interval hint. Structural
|
|
1600
|
+
nonpositive quantization or nonfinite numbers use the existing range/type diagnostics.
|
|
1601
|
+
|
|
1602
|
+
A play state's speed multiplies the timeline's speed. Negative speed starts at the
|
|
1603
|
+
work end; positive or zero starts at its start. The source clock is this start plus
|
|
1604
|
+
`stateTime * effectiveSpeed`. Fold that clock into the work interval using §5 loop
|
|
1605
|
+
or pingpong rules, or clamp for once. Quantize only the **sample** time down on the
|
|
1606
|
+
absolute timeline grid (`floor(time*quantize)/quantize`), clamped to work start; an
|
|
1607
|
+
exact work end remains exact. Keys outside the work interval remain valid and can
|
|
1608
|
+
influence interpolation inside it. Events outside it never fire.
|
|
1609
|
+
|
|
1610
|
+
Event crossings use the unquantized source clock. Forward intervals exclude the
|
|
1611
|
+
previous time and include the new one; reverse intervals include the new time and
|
|
1612
|
+
exclude the previous time. On entry include both bounds. Loop and pingpong crossings
|
|
1613
|
+
retain carry, emit each authored event once per crossing, deduplicate pingpong turns,
|
|
1614
|
+
and sort in travel order; coincident events retain authored time/order. Start and end
|
|
1615
|
+
are distinct authored events even when their loop crossings coincide. The existing
|
|
1616
|
+
10,000-period enumeration bound remains unchanged. A once state finishes after
|
|
1617
|
+
`workSpan / abs(effectiveSpeed)` state seconds. Zero speed holds and has infinite
|
|
1618
|
+
completion time. State-machine zero-speed play states stay busy because transitions
|
|
1619
|
+
may still depend on state time; explicit zero-speed timeline instances can sleep.
|
|
1620
|
+
Reduced end/skip uses the directional work endpoint/start; end reports only the
|
|
1621
|
+
work-area events, in travel order, including both ends. Frozen instances sample the
|
|
1622
|
+
endpoint without looping it back to the start.
|
|
1623
|
+
|
|
1624
|
+
<!-- capability: state-machine.exit-time-seconds -->
|
|
1625
|
+
A transition may add `exit` (nonnegative source-animation seconds) or `exitFraction`
|
|
1626
|
+
(a fraction in `[0,1]` of its work span), never both. AniML uses the same names.
|
|
1627
|
+
This is an additional gate on `when`, evaluated before entering the target. It measures
|
|
1628
|
+
absolute source-animation distance since entry, including loops, before quantization,
|
|
1629
|
+
so state and timeline speeds both matter; it is not wall/state time. It requires a
|
|
1630
|
+
play source and must lie within its work span (`motion-exit`, pointing at the exit,
|
|
1631
|
+
with a source/interval hint). Carry is the state-time overshoot of the gate, capped by
|
|
1632
|
+
any existing time-condition carry; an input-only condition uses the gate's carry.
|
|
1633
|
+
This avoids passing target time that predates the last gate becoming eligible.
|
|
1634
|
+
|
|
1635
|
+
### Declarative timeline control
|
|
1636
|
+
|
|
1637
|
+
These forms require `motion`; omitted fields preserve existing playback, including all 0.1
|
|
1638
|
+
behaviour. AniML uses the JSON names verbatim. They extend §7's clock/transition passes,
|
|
1639
|
+
not the post-state property-binding pass.
|
|
1640
|
+
|
|
1641
|
+
- A transition's `sync: true` transfers the **unquantized local time** from source to target,
|
|
1642
|
+
instead of restarting at the directional start. Both states must play the same timeline
|
|
1643
|
+
(`motion-sync` at `/machine/layers/<layer>/states/<state>/on/<index>/sync`, with a same-timeline
|
|
1644
|
+
hint). Work-area folding/clamping happens before transfer; quantization happens only at sample.
|
|
1645
|
+
The target uses its own speed from that time. A once target completes after its remaining
|
|
1646
|
+
directional distance, so `speed: -1` reverses from the current position and entering the forward
|
|
1647
|
+
state again turns around immediately. State time and fade elapsed still reset to carry (§7);
|
|
1648
|
+
interruptible fades still freeze the current output as their source. Sync does not replay
|
|
1649
|
+
timeline entry events at the transfer point; events crossed during target carry are reported.
|
|
1650
|
+
- A play state's `hold: true` freezes its clock at the entry time (directional start normally,
|
|
1651
|
+
transferred local time with `sync`). It continues writing the same sampled tracks. State time,
|
|
1652
|
+
`@after`, input conditions and crossfades keep running. Holding does not itself satisfy `@end`.
|
|
1653
|
+
A held state can settle once its fade and future `@after` waits finish. `hold` and `time` are
|
|
1654
|
+
exclusive; either on a pose reports `motion-control` at the state (conflict at `/time`), with
|
|
1655
|
+
a hint to choose a play clock.
|
|
1656
|
+
- A transition's `exitNext: "step" | "loop"` waits for the next clock boundary while its `when`
|
|
1657
|
+
remains true. At the start of §7 step 2, after queued inputs apply, arm the gate from the
|
|
1658
|
+
source clock **before advancing dt**. `step` uses the next local quantize grid point in the
|
|
1659
|
+
direction of travel, or the work endpoint if earlier; without quantize it uses the work
|
|
1660
|
+
endpoint. `loop` uses the next work endpoint (each turning point for pingpong). A request
|
|
1661
|
+
observed exactly on a boundary is immediately eligible (using the §5 clock-boundary rule).
|
|
1662
|
+
Changing the condition to false cancels the armed gate; a later request
|
|
1663
|
+
computes a new boundary. Frozen reduced-motion and zero-speed clocks are immediately eligible.
|
|
1664
|
+
The source clock and its timeline event interval stop at that boundary, even for a large dt;
|
|
1665
|
+
excess state time carries into the target (and its fade). A synced held target therefore
|
|
1666
|
+
captures the boundary, not the overshooting frame. Transition priority and the 16-transition
|
|
1667
|
+
cap stay as in §7. Locked fades do not arm gates. `exitNext` requires an autonomous play
|
|
1668
|
+
source (no hold/time) and persistent bool/number terms only; it cannot combine with triggers,
|
|
1669
|
+
`@end`, `@after`, `exit` or `exitFraction` (`motion-exit` at `/exitNext`, with an autonomous-source
|
|
1670
|
+
and persistent-condition hint).
|
|
1671
|
+
- A play state's `time: "expression"` additionally requires `bind`. It evaluates the bind
|
|
1672
|
+
grammar against this player's inputs, producing local timeline seconds clamped to its work
|
|
1673
|
+
area. It owns the clock instead of advancing it; speed has no effect. It samples with the
|
|
1674
|
+
timeline's loop and quantize rules (a loop's end folds to its start). An ordinary entry
|
|
1675
|
+
evaluates the expression immediately. A synced entry keeps the transferred time and records
|
|
1676
|
+
the current expression value; the next **different evaluated value** seeks to that value.
|
|
1677
|
+
Thus completion can hold its angle until the author changes `phase`, without a snap to a
|
|
1678
|
+
stale phase on entry. A change that evaluates to the same clamped value is not a seek.
|
|
1679
|
+
Expressions update after inputs apply and before transitions/sample, including `step(0)`;
|
|
1680
|
+
they also initialize the constructor's initial state. Seeking emits no timeline events,
|
|
1681
|
+
does not complete `@end`, and can settle like a held clock. Errors use bind's existing codes
|
|
1682
|
+
at the state's `/time`; missing `bind` reports `undeclared-feature` there with an add-bind hint.
|
|
1683
|
+
|
|
1684
|
+
Held clocks report state `emit`s, but do not synthesize timeline events on entry or while held.
|
|
1685
|
+
The pre-existing `exit`/`exitFraction` gates retain their elapsed-state-time × absolute effective
|
|
1686
|
+
speed measurement; they do not observe seeks or the captured clock offset. `exitNext` instead
|
|
1687
|
+
observes the selected autonomous clock and is restricted to that clock kind above.
|
|
1688
|
+
|
|
1689
|
+
Reduced-motion entry policy takes precedence over these clock controls, using the same
|
|
1690
|
+
fixed directional endpoints and event policy as other motion play states. Time/hold states
|
|
1691
|
+
still obey that frozen entry pose. Ordinary property bindings retain their post-state priority.
|
|
1692
|
+
Named machines and component players own independent control clocks and input scopes.
|
|
1693
|
+
|
|
1694
|
+
Decision: put clock selection on states and clock handoff on transitions, keeping inputs as the
|
|
1695
|
+
single listener interface. This follows the separation of state time, animation time and fade
|
|
1696
|
+
snapshots discussed in [animation/state research](../docs/research/rive-animation-and-state.md)
|
|
1697
|
+
and the sleep-when-settled lesson in [web runtime research](../docs/research/rive-web-runtime.md).
|
|
1698
|
+
No animation is cloned and no property-read feedback is added to bind expressions.
|
|
1699
|
+
|
|
1700
|
+
### Animated transform origin
|
|
1701
|
+
|
|
1702
|
+
<!-- capability: transforms.origin-animation -->
|
|
1703
|
+
`pivotX` and `pivotY` are numeric animatable node properties, with the same AniML names.
|
|
1704
|
+
Each falls back to the corresponding component of the existing `pivot` vector, then
|
|
1705
|
+
zero. They feed exactly the transform in §4, so drawing, world matrices and hit testing
|
|
1706
|
+
use the same animated origin. The vector `pivot` remains a static convenience;
|
|
1707
|
+
tracks address its independent scalar coordinates. Existing bind expressions may
|
|
1708
|
+
also target those coordinates when bind and motion are both required.
|
|
1709
|
+
|
|
1710
|
+
### Explicit runtime instances
|
|
1711
|
+
|
|
1712
|
+
<!-- capability: animation.host-loop-override -->
|
|
1713
|
+
<!-- capability: animation.host-play-pause-stop -->
|
|
1714
|
+
<!-- capability: animation.seek -->
|
|
1715
|
+
<!-- capability: host-api.animation-metadata -->
|
|
1716
|
+
<!-- capability: host-api.low-level-animation -->
|
|
1717
|
+
<!-- capability: host-api.low-level-scene-query -->
|
|
1718
|
+
<!-- capability: host-api.multi-state-machine -->
|
|
1719
|
+
<!-- capability: host-api.scrub -->
|
|
1720
|
+
The optional module installs the following API on `Player` and `<anima-icon>`;
|
|
1721
|
+
see [WEB](../docs/WEB.md#motion-module) for signatures and host scheduling.
|
|
1722
|
+
Missing motion requirements reject calls; absent names and invalid finite-number
|
|
1723
|
+
arguments throw. No implicit timeline instance is created by document timelines.
|
|
1724
|
+
A default `timeline(name)` is created on first use; `createTimeline(name,{name,mix,
|
|
1725
|
+
speed,loop})` creates an independent clock. Instance names are unique across timelines
|
|
1726
|
+
and machines. Instances compose after ordinary layers and bindings, before clamping,
|
|
1727
|
+
in **creation order**. They restart from the values below them on every step; there
|
|
1728
|
+
is no accumulating weighted drift. Each named machine resolves its layers against the values below its instance, including
|
|
1729
|
+
return-to-underlying crossfades. Only the properties actually written by a named
|
|
1730
|
+
machine's active/fading states and bindings contribute, never all of its rest slots.
|
|
1731
|
+
|
|
1732
|
+
`seek(seconds)` clamps into the work area without emitting crossed events or changing
|
|
1733
|
+
play/pause; `scrub(progress)` clamps progress to `[0,1]`, seeks over that area, and
|
|
1734
|
+
pauses. A sought loop end folds to the start when sampled; reduced frozen endpoints
|
|
1735
|
+
are the exception above. `play` retains the clock, `pause` retains its writes, and
|
|
1736
|
+
`stop` resets to the directional start and removes its writes. Setting `loop` overrides
|
|
1737
|
+
the authored mode; null restores it. Negative instance speed reverses from the current
|
|
1738
|
+
clock. `advance(dt)` moves just this clock and returns crossed events; `apply(mix?)`
|
|
1739
|
+
enables its writes. Values resolve on the next `Player.step`, including `step(0)`;
|
|
1740
|
+
explicit manual advance does not dispatch DOM events. `didLoop` is reset on advance
|
|
1741
|
+
and indicates that this advance crossed a work boundary (including once completion).
|
|
1742
|
+
Changing metadata returned by `info` or `animations` cannot change the model.
|
|
1743
|
+
|
|
1744
|
+
`machines: { name: { layers } }` declares named machine definitions; AniML uses
|
|
1745
|
+
`<machine name="name"><layer ...>...</layer></machine>`. `machine(name)` instantiates
|
|
1746
|
+
one lazily with independent inputs and clocks over the shared scene. Its controls
|
|
1747
|
+
support play/pause/stop, mix, set/fire/get and states. Stop removes writes and suspends
|
|
1748
|
+
its state without resetting it; play resumes it. Paused machines hold their writes.
|
|
1749
|
+
Their events use `instance/layer` as layer name. Their states are queried on the
|
|
1750
|
+
instance and also appear as `instance/layer` in the player's states and inspector,
|
|
1751
|
+
so they do not collide with the default machine's layer names. Definitions
|
|
1752
|
+
are validated even before instantiation. `node(id,type?)` returns a typed read handle
|
|
1753
|
+
or undefined for an absent id/type; it reads resolved properties and local/world
|
|
1754
|
+
transforms. It does not fabricate bone or text handles before those node types exist.
|
|
1755
|
+
|
|
1756
|
+
### Conformance, CLI and oracle limits
|
|
1757
|
+
|
|
1758
|
+
Each capability marker above maps to its same-named `modules/motion/capabilities/`
|
|
1759
|
+
example, test, and `spec/conformance/<hyphenated-id>/` case. Expected scalar values
|
|
1760
|
+
are hand-computed, including 6 for the equal-endpoint cubic midpoint, 1/32 for elastic-in
|
|
1761
|
+
at 1/2 with period 1/2, and sequential numeric mixes. Existing unmarked conformance
|
|
1762
|
+
sources, scripts and expectations are unchanged.
|
|
1763
|
+
|
|
1764
|
+
The §11 script also accepts `timeline <name> play|pause|stop`, `timeline <name>
|
|
1765
|
+
seek|scrub|speed|mix <number>`, `timeline <name> loop once|loop|pingpong|authored`,
|
|
1766
|
+
`instance <id> <timeline> [mix]`, and `machine <name> play|pause|stop|mix|set|fire`
|
|
1767
|
+
(with the input/value arguments for set/fire). Host operations resolve on an explicit
|
|
1768
|
+
`advance`, which can be zero. CLI validate/build/decompile/fmt/schema/inspect/sample/render
|
|
1769
|
+
use the same registry and player; no separate motion evaluator is introduced.
|
|
1770
|
+
|
|
1771
|
+
Native frame oracle mappings and unsupported observations are recorded per capability
|
|
1772
|
+
and in the oracle README. A browser/API test is not evidence of a Rive frame match.
|
|
1773
|
+
|
|
1774
|
+
## canvas
|
|
1775
|
+
|
|
1776
|
+
<!-- capability: host-api.device-pixel-ratio -->
|
|
1777
|
+
### Pixel ratio
|
|
1778
|
+
|
|
1779
|
+
The optional web Canvas2D backend reads `window.devicePixelRatio`. The HTML host's `pixel-ratio`
|
|
1780
|
+
attribute overrides it with a finite positive number; `devicePixelRatioUsed` exposes the chosen
|
|
1781
|
+
ratio when this backend is active. Invalid overrides emit `bad-pixel-ratio` at pointer `""` (the
|
|
1782
|
+
host, not a document field), with a message and hint, and fall back to browser DPR. A resolution
|
|
1783
|
+
media query and window resize notification track browser DPR changes, including monitor moves.
|
|
1784
|
+
No document JSON or AniML key, `requires` name, or player property is introduced by canvas.
|
|
1785
|
+
|
|
1786
|
+
<!-- capability: host-api.resize-backing-surface -->
|
|
1787
|
+
### Backing surface
|
|
1788
|
+
|
|
1789
|
+
Canvas viewport CSS placement follows the SVG host's grid/viewBox bleed, fit and alignment.
|
|
1790
|
+
Backing dimensions are the rounded CSS viewport dimensions times the selected ratio, with a
|
|
1791
|
+
one-pixel minimum. A ResizeObserver resizes and redraws when the host's CSS content size changes.
|
|
1792
|
+
Size or pixel-ratio changes redraw without advancing the player or consuming queued input.
|
|
1793
|
+
Repeated player revisions do not redraw; explicit `invalidate()` redraws once and resolves current
|
|
1794
|
+
CSS colour slots. Playback, reduced motion, scheduler sleeping, visibility and hit outlines remain
|
|
1795
|
+
those of the existing web host. Disconnect/destroy removes observers and DPR listeners; reconnect
|
|
1796
|
+
resumes observation. Canvas contents are clipped to the viewBox viewport; SVG overflow is visible.
|
|
1797
|
+
|
|
1798
|
+
<!-- capability: platforms.web-canvas -->
|
|
1799
|
+
### Canvas2D rendering
|
|
1800
|
+
|
|
1801
|
+
Import `@anima-format/web/canvas` and set `renderer="canvas"` on `<anima-icon>`. SVG remains the
|
|
1802
|
+
default (`renderer="svg"` or absent). Selection may change on a loaded element without resetting
|
|
1803
|
+
its player. An unavailable renderer emits `unsupported-render`, pointer `""`, with the renderer
|
|
1804
|
+
name and an import hint. Other rendering diagnostics use the source JSON pointer where available.
|
|
1805
|
+
This is a host selection, not a change to `.ani`, AniML, CLI render or schema.
|
|
1806
|
+
|
|
1807
|
+
The canvas backend consumes DrawFrame (§3.1 in MODULES) directly. Path2D geometry/primitives,
|
|
1808
|
+
ordered fills/strokes, colour slots/currentColor, linear/radial gradients, grid-unit dashes/trims,
|
|
1809
|
+
transforms, group opacity, source-relative clips, alpha/inverted/luminance masks, blend modes,
|
|
1810
|
+
images, image patterns/modulation, components, n-slice patches and feathering use Canvas2D.
|
|
1811
|
+
Group opacity and masks isolate into transparent offscreen surfaces; ordinary opaque groups do
|
|
1812
|
+
not. A canvas is a compositing boundary and cannot blend with page content outside itself.
|
|
1813
|
+
Fixed strokes use CSS viewport pixels (as SVG non-scaling-stroke does), without approximating
|
|
1814
|
+
nonuniform node transforms with a scalar inverse. Trims use core outline lengths.
|
|
1815
|
+
|
|
1816
|
+
Feather follows the existing premultiplied Gaussian formula using `ctx.filter` blur and in/out
|
|
1817
|
+
alpha composition. World space keeps the Gaussian isotropic after node transforms. The current
|
|
1818
|
+
backend declines nonuniform/sheared **local** feathering, nonuniform viewport feathering, and
|
|
1819
|
+
partial trims on fixed strokes with `unsupported-render` naming the feature, a pointer, message
|
|
1820
|
+
and hint. It never silently substitutes an inverse average scale. Disabled feathering is ordinary
|
|
1821
|
+
paint. Cross-origin image sources need CORS permission for canvas readback/patterns; failures emit
|
|
1822
|
+
`asset-load`. CSS slot changes on an idle canvas require `invalidate()`.
|
|
1823
|
+
|
|
1824
|
+
Cross-backend pixel differences are measured and retained by the canvas browser conformance suite;
|
|
1825
|
+
limits and findings are in `modules/canvas/test/web/README.md` and `RESULTS.md`. Renderer support
|
|
1826
|
+
is not a claim of exact rasterizer identity or a passing Rive oracle for host-only operations.
|
|
1827
|
+
|
|
1828
|
+
## focus
|
|
1829
|
+
|
|
1830
|
+
The optional `focus` module declares `requires: ["focus"]`. It adds a static `focus`
|
|
1831
|
+
object to any node: `enabled` (bool, default true), `enabledInput` (optional bool input
|
|
1832
|
+
name), `pointer` (bool, default true), `order` (finite number, default 0), `scope`
|
|
1833
|
+
(bool, default false), `edge` (`stop`, default, `closed-loop`, or `parent-scope`),
|
|
1834
|
+
`label` (string, defaults to node id), `bounds` (local `[x,y,width,height]`, nonnegative
|
|
1835
|
+
sizes; omitted means the local origin), and `tabStops` (bool, default false).
|
|
1836
|
+
A scope container may set `enabled: false` while retaining its scope policy.
|
|
1837
|
+
Unknown enabled inputs report `unknown-input` and non-bool inputs report `input-type`
|
|
1838
|
+
at the node's `/focus/enabledInput`, with a message and corrective hint. Ordinary
|
|
1839
|
+
registry shape/type/range and undeclared-feature diagnostics apply to all fields.
|
|
1840
|
+
|
|
1841
|
+
`Player.focused` is a synchronous, read-only node id or null. This is the hook for
|
|
1842
|
+
machine `condition-focus`: compare the requested id with `player.focused`; no DOM
|
|
1843
|
+
query, step or input write is required. Each player owns independent focus state.
|
|
1844
|
+
`focusTarget(id|null)` changes the target if eligible; unknown/disabled/invisible
|
|
1845
|
+
ids leave it unchanged. `focusClear()` clears it. Both return whether it changed.
|
|
1846
|
+
`focusItems()` returns eligible targets; `focusItems(true)` includes unavailable
|
|
1847
|
+
configured targets for DOM reconciliation. A target needs a focus object, enabled
|
|
1848
|
+
true, enabledInput true when supplied, and visible ancestors (including itself).
|
|
1849
|
+
Opacity does not affect eligibility. Focus lost through a resolved visibility/input
|
|
1850
|
+
change clears after that step and queues blur actions; the step is unsettled until
|
|
1851
|
+
the next step has consumed them.
|
|
1852
|
+
|
|
1853
|
+
`focusTraverse(next|previous|left|right|up|down)` defaults to next. Sequential order
|
|
1854
|
+
is ascending `order`, then document node index. From no focus, forward directions
|
|
1855
|
+
select the first eligible target and backward directions the last. Within a scope,
|
|
1856
|
+
next/previous take the adjacent target. Spatial directions use centers of the local
|
|
1857
|
+
bounds transformed by current world matrices. Candidates strictly ahead on the
|
|
1858
|
+
requested axis are ranked by squared Euclidean distance, then sequential order.
|
|
1859
|
+
The nearest enclosing ancestor with scope true owns the boundary. `stop` retains
|
|
1860
|
+
the target; `closed-loop` wraps sequentially to the opposite end, or spatially to
|
|
1861
|
+
the opposite extreme on that axis (ties use authored node order); `parent-scope`
|
|
1862
|
+
retries in the enclosing scope, including its descendants. The outer icon boundary
|
|
1863
|
+
stops. Traversal returns whether the target changed. Invalid direction names throw.
|
|
1864
|
+
|
|
1865
|
+
Focusable component internals are exposed as dotted instance paths (`save.label`),
|
|
1866
|
+
with each component player's local traversal and scope rules. Root targets precede
|
|
1867
|
+
component targets; instances and their descendants follow authored instance order.
|
|
1868
|
+
Disabling a scope's own focus target does not hide its semantic subtree or disable
|
|
1869
|
+
eligible descendants. Semantic elements remain in the accessibility tree; only
|
|
1870
|
+
nonsemantic focus proxies are hidden when ineligible.
|
|
1871
|
+
|
|
1872
|
+
An instance is an implicit scope: after local traversal stops, its node's focus.edge
|
|
1873
|
+
must be parent-scope to traverse outside it. Nested transforms compose for spatial
|
|
1874
|
+
centers and pointer mapping. Focus changes route to the owning player's local
|
|
1875
|
+
listeners; the parent hook reports the dotted path. Component-local state machines
|
|
1876
|
+
read their local `focused` ids. Instance visibility excludes its focus descendants.
|
|
1877
|
+
|
|
1878
|
+
`focusPointer(x,y)` focuses the foremost eligible target (latest painted) under the point using
|
|
1879
|
+
§9 hit testing, excluding targets with pointer false. Primary pointer down calls it
|
|
1880
|
+
before ordinary pointer listener actions; up, move and leave do not change focus.
|
|
1881
|
+
Ranking uses composite's `Player.paintRank` when present (the pre-order walk with resolved
|
|
1882
|
+
`drawOrder` sibling lists), otherwise the authored node index. Nested players rank locally
|
|
1883
|
+
inside their host instance's paint rank. Opaque blockers use the same key and retain their
|
|
1884
|
+
ancestor chain.
|
|
1885
|
+
Component forwarding does not independently focus other overlapped instances.
|
|
1886
|
+
|
|
1887
|
+
Listeners add `on: "focus"` and `on: "blur"` (requires focus); `target` is a local node
|
|
1888
|
+
id or omitted to observe every local change. Changing targets queues old blur
|
|
1889
|
+
listeners then new focus listeners, each in authored order, with the existing §9
|
|
1890
|
+
action semantics. Re-selecting the same target does nothing. Nested listeners act
|
|
1891
|
+
inside their owning component. These hooks are independent of pointer enter/leave.
|
|
1892
|
+
|
|
1893
|
+
`focusKey(key, down=true, shift=false)` handles ArrowLeft/Right/Up/Down on down;
|
|
1894
|
+
key up is inert. Tab traverses next/previous only with an authored tabStops opt-in.
|
|
1895
|
+
This hook lets the input module share the same deterministic keyboard policy. Input key listeners
|
|
1896
|
+
consume normal keyboard delivery by default (`propagate: true` opts into continued delivery);
|
|
1897
|
+
this does not suppress the subsequent focus navigation hook or native Enter/Space activation.
|
|
1898
|
+
|
|
1899
|
+
### AniML and browser mapping
|
|
1900
|
+
|
|
1901
|
+
AniML uses a child `<focus enabled="true" enabled-input="available" pointer="true"
|
|
1902
|
+
order="0" scope="true" edge="closed-loop" label="Save" bounds="0 0 4 4"
|
|
1903
|
+
tab-stops="false"/>`. Names otherwise match JSON. Registry descriptors drive both
|
|
1904
|
+
spellings, CLI/schema inspection, compilation and decompilation. Browser markup
|
|
1905
|
+
loads focus and markup; the focus entry supplies structural authoring hooks.
|
|
1906
|
+
|
|
1907
|
+
The browser entry `@anima-format/web/focus` maps logical targets to real focusable
|
|
1908
|
+
shadow-DOM elements, with button roles and labels. Focus is roving by default:
|
|
1909
|
+
Tab enters the host's first eligible target; arrows move within it; Tab/Shift+Tab
|
|
1910
|
+
leave to the surrounding document. Leaving clears logical focus. The icon remains
|
|
1911
|
+
one sequential stop. Any authored focus.tabStops true opts the document into native
|
|
1912
|
+
inner Tab stops (one per eligible target); the host then adds no extra stop. A parent
|
|
1913
|
+
icon's a11y role/label/activate/pressed/held and §10 Enter/Space behavior are retained.
|
|
1914
|
+
Pointer default focus is suppressed after logical pointer focus to prevent it from
|
|
1915
|
+
displacing the chosen shadow target. Explicit pause ignores arrow navigation.
|
|
1916
|
+
|
|
1917
|
+
Focus proxies are semantic targets, not extra painted shapes. The small semantic
|
|
1918
|
+
DOM and shared core input policy follow the guidance in
|
|
1919
|
+
`docs/research/rive-web-runtime.md` (lessons 2 and 6); no Rive implementation is used.
|
|
1920
|
+
Headless pixels alone do not observe focus. Native interaction oracles compare logical focus
|
|
1921
|
+
and traversal through semantics dumps and focus/blur view-model reports. Browser tests separately
|
|
1922
|
+
cover DOM focus and native Tab entry/exit; see `modules/input/ORACLE-RESULTS.md`.
|
|
1923
|
+
|
|
1924
|
+
## machine
|
|
1925
|
+
|
|
1926
|
+
|
|
1927
|
+
`requires: ["machine"]` adds the fields below to 0.1 documents. Without that requirement,
|
|
1928
|
+
playback is unchanged, even when this module is installed. The implementation extends the
|
|
1929
|
+
base player's playback policy; it does not replace its input queue, transition chain cap,
|
|
1930
|
+
trigger consumption, interpolation, clamping or frame resolution. `motion`, `bind` and
|
|
1931
|
+
`components` order before machine when installed; none is imported as a runtime dependency.
|
|
1932
|
+
|
|
1933
|
+
This design independently implements the graph and deterministic-frame ideas discussed in
|
|
1934
|
+
`docs/research/rive-animation-and-state.md` §§A.6–A.8 and its pitfalls about shared mutable
|
|
1935
|
+
state. No Rive algorithm or source was copied; no additional NOTICE attribution is needed.
|
|
1936
|
+
|
|
1937
|
+
### State kinds and priority
|
|
1938
|
+
|
|
1939
|
+
States retain exactly one `pose` or `play`. `kind` optionally names `entry`, `exit`, `any`, or
|
|
1940
|
+
`random`; these special states require `pose: {}`. An entry state is an ordinary empty pose
|
|
1941
|
+
whose ordered conditions choose the next state at the first `step`, including `step(0)`.
|
|
1942
|
+
An exit state is terminal and has no `on` list. It retains the outgoing state's own keyed samples at exit time as constant writes; its clock stops. With no outgoing state it writes nothing. Other layers continue, and post-state bindings retain priority.
|
|
1943
|
+
|
|
1944
|
+
An any state is a declaration, cannot be initial or a transition target, and does not run.
|
|
1945
|
+
Its transitions are searched before the current state's transitions, in declaration order of
|
|
1946
|
+
any states and then of their `on` lists. They are available in every nonterminal real state.
|
|
1947
|
+
An any edge targeting the current state is omitted unless it has a trigger or `change` guard,
|
|
1948
|
+
which permits restarting a pop animation on another increase without repeated re-entry.
|
|
1949
|
+
All transitions still count toward the base limit of 16 per layer per step.
|
|
1950
|
+
|
|
1951
|
+
`reset: true` explicitly requests the normal restart on every re-entry. Omission is identical.
|
|
1952
|
+
`reset: false` is rejected (`machine-state`); implicit pause/resume is not specified.
|
|
1953
|
+
|
|
1954
|
+
### Transitions and previous values
|
|
1955
|
+
|
|
1956
|
+
Transition fields (JSON and AniML names are identical):
|
|
1957
|
+
|
|
1958
|
+
- `disabled: boolean`: keep the edge but never select it.
|
|
1959
|
+
- `exitRatio: number` in [0,1]: gate by this fraction of the source play duration. With motion,
|
|
1960
|
+
use its work span and effective speed. Spill the overshoot into the target, as in §7.
|
|
1961
|
+
- `blendRatio: number` ≥ 0: blend for that multiple of source play duration, including work
|
|
1962
|
+
span and effective speed with motion. Exclusive with `blend`.
|
|
1963
|
+
- `continue: boolean`: default false. True advances the outgoing timeline sampler during the
|
|
1964
|
+
crossfade instead of freezing its exit pose. It does not keep the outgoing state active or
|
|
1965
|
+
eligible for transitions. The target clock and transition fade advance as usual. Continued sources honor autonomous motion speed/work/quantization; held, expression-controlled or sync-entered sources are rejected with `machine-clock`. Outgoing timeline events stop at exit.
|
|
1966
|
+
- `change: {input, mode}`: `mode` is `changed`, `increased` or `decreased`. Compare the input's
|
|
1967
|
+
current applied value with its value at the end of the preceding step (defaults before the
|
|
1968
|
+
first step). `changed` accepts bool or number; ordered modes require number. Queued writes
|
|
1969
|
+
are coalesced by the ordinary input stage: only the final applied value is compared.
|
|
1970
|
+
Advance the previous-value baseline once, after all parent layers and before nested players advance, even if no edge consumed it. Scoped input changes are observed when their component stage makes them available.
|
|
1971
|
+
The same change edge may fire once per layer per step, across any-state and self re-entry;
|
|
1972
|
+
every layer sees the same signal. A locked or disabled edge does not buffer changes.
|
|
1973
|
+
|
|
1974
|
+
`when` remains required. For an otherwise unconditional change edge use `when: "@after(0)"`:
|
|
1975
|
+
|
|
1976
|
+
```json
|
|
1977
|
+
{"when":"@after(0)","to":"pop","change":{"input":"count","mode":"increased"}}
|
|
1978
|
+
```
|
|
1979
|
+
|
|
1980
|
+
A single such edge on an any state handles arbitrary counts, including another increment
|
|
1981
|
+
while popping; there is no ladder of numeric states. A zero-duration self-reentry does not
|
|
1982
|
+
produce `@limit`. A held unchanged value never retriggers. Change-gated transitions always enter the target
|
|
1983
|
+
at time zero, including their fade clock: the change is observed in this step, so time spent
|
|
1984
|
+
waiting in the source is not target overshoot, even with `@after`, `@end` or `exitRatio`.
|
|
1985
|
+
For a pulse on every increase, put this edge on an any state, play a once timeline
|
|
1986
|
+
starting at the rest value, and return to idle with `@end`. Another increase during the
|
|
1987
|
+
pulse restarts at its first key; decreases and unchanged values do not restart it.
|
|
1988
|
+
See `modules/machine/examples/blend-pulse.animl`.
|
|
1989
|
+
|
|
1990
|
+
### Typed live guards and the focus seam
|
|
1991
|
+
|
|
1992
|
+
`guard: {left, op, right}` ANDs another predicate with `when` and `change`. Each operand is a
|
|
1993
|
+
number, bool, or exactly one of `{artboard: "width"|"height"|"aspect"}`, `{component: "id.input"}`,
|
|
1994
|
+
`{focus: "node-id"}`. Artboard dimensions are the document's viewBox dimensions in grid units,
|
|
1995
|
+
not CSS pixels. Component operands read a validated scoped bool/number input through
|
|
1996
|
+
`Player.get`, at the component stage already specified by the components module; they do not
|
|
1997
|
+
read rendered geometry or create layout feedback. Equality accepts matching bool or numeric
|
|
1998
|
+
operands; ordered comparisons require numbers. Operators are `== != < <= > >=`.
|
|
1999
|
+
|
|
2000
|
+
The focus module supplies `player.machineFocus = (nodeId: string) => boolean`, a pure live
|
|
2001
|
+
reader returning whether that document node owns focus. It calls `player.onChange?.()` after
|
|
2002
|
+
changes so a settled machine wakes. No focus owner or no installed reader means false.
|
|
2003
|
+
Machine owns neither DOM focus nor focus traversal, and does not import the focus runtime.
|
|
2004
|
+
A standalone headless host can install the same reader; both equality and inequality work.
|
|
2005
|
+
|
|
2006
|
+
### Blends and bound data
|
|
2007
|
+
|
|
2008
|
+
A blend is a play state with `mix: {mode, children, axis?, exit?}`. `children` is a nonempty
|
|
2009
|
+
array of `{play: timelineName, at?: number, weight?: source}`. The state's `play` names the
|
|
2010
|
+
child used for `@end`, exitRatio and blendRatio; `exit`, if present, is that child's zero-based
|
|
2011
|
+
index and must agree with `play`. This makes the exit clock explicit even when its weight is 0.
|
|
2012
|
+
Every child samples its own timeline at state elapsed time × state speed, with its own loop
|
|
2013
|
+
mode. With motion installed and declared, each child also honors its own speed, work area and
|
|
2014
|
+
quantization. A blend's primary state clock must be autonomous: hold/time and incoming sync
|
|
2015
|
+
are rejected with `machine-clock`, rather than silently applying one child's clock to the others. A blend remains unsettled while any child timeline can move, including a zero-weight
|
|
2016
|
+
child. Reduced motion is recorded on entry for every child: end/skip freeze at its directional work endpoint, while play children keep advancing. A zero-speed child is at rest. Missing child tracks
|
|
2017
|
+
contribute the addressed property's rest value, never a value from an earlier frame.
|
|
2018
|
+
|
|
2019
|
+
A source is a number, a numeric input name string, or `{bind: "numeric expression"}`. The last
|
|
2020
|
+
form additionally requires `bind` and compiles the existing bind expression language against
|
|
2021
|
+
inputs. It evaluates at sampling time after queued input writes, before the ordinary
|
|
2022
|
+
post-state property bindings. This is P4c's data-source bridge until P4e view models arrive:
|
|
2023
|
+
there is no implicit view model, scene-property reference or binding feedback.
|
|
2024
|
+
|
|
2025
|
+
- `mode: "1d"`: requires `axis` and an `at` on every child, strictly increasing. Clamp the
|
|
2026
|
+
axis outside the first/last threshold to that endpoint. Within a segment interpolate its
|
|
2027
|
+
neighboring child samples. All other children have weight 0. One child is permitted.
|
|
2028
|
+
- `mode: "direct"`: sample each child in authored order and interpolate the accumulated
|
|
2029
|
+
value toward it by its independently clamped [0,1] weight (default 1), starting at rest.
|
|
2030
|
+
Weights are not normalized. A first child at weight 1 establishes a baseline.
|
|
2031
|
+
- `mode: "additive"`: apply the first child as the full baseline, then interpolate
|
|
2032
|
+
toward each later child's sample by its independent weight, in authored order.
|
|
2033
|
+
This is the direct-blend family exposed as Additive Blend in Rive's editor;
|
|
2034
|
+
it combines animations over a baseline, rather than accumulating numeric offsets
|
|
2035
|
+
from previous frames. The first child's authored weight is ignored.
|
|
2036
|
+
|
|
2037
|
+
The same base interpolation rules apply to colours and discrete values. State output still
|
|
2038
|
+
passes through the base crossfade, layer ordering, property bindings and clamping.
|
|
2039
|
+
|
|
2040
|
+
### Deterministic random selection
|
|
2041
|
+
|
|
2042
|
+
`kind: "random"` selects among eligible outgoing transitions, using nonnegative `weight`
|
|
2043
|
+
(default 1). Ignore disabled, false-condition and zero-weight edges. The first gate evaluation
|
|
2044
|
+
selects once for that search; later probes do not redraw. No eligible edge consumes no random
|
|
2045
|
+
number. A taken edge invalidates that search; another visit can choose again. Trigger names
|
|
2046
|
+
already consumed in this layer/step remain ineligible. Declaration order resolves intervals.
|
|
2047
|
+
|
|
2048
|
+
Every player owns an unsigned 32-bit seed, `machineSeed` at the document root, default
|
|
2049
|
+
`0x6d2b79f5` (1831565813). Before each choice:
|
|
2050
|
+
`seed = (1664525 * seed + 1013904223) mod 2^32`, using integer multiplication;
|
|
2051
|
+
`u = seed / 4294967296`. Choose the first eligible edge whose cumulative weight exceeds
|
|
2052
|
+
`u * sum(weights)`. Without an explicit player/script seed, players, component instances and
|
|
2053
|
+
named-machine players keep independent RNG state. An explicitly seeded root shares its core
|
|
2054
|
+
source with nested stepping in authored order. Identical applied inputs, seed and step sequence produce identical choices. This
|
|
2055
|
+
portable LCG is independent of Rive's unseeded native selection. With an explicit player/script
|
|
2056
|
+
seed, machine and converter draws share one core source in evaluation order; the explicit seed
|
|
2057
|
+
overrides `machineSeed`. Without it, the machine stream and converter stream retain these legacy defaults.
|
|
2058
|
+
|
|
2059
|
+
### Actions and completion
|
|
2060
|
+
|
|
2061
|
+
States support `enterActions` and `exitActions`; transitions support `actions` and `endActions`.
|
|
2062
|
+
Each is an object with the listener actions `fire`, `set`, `toggle`, `emit`.
|
|
2063
|
+
Sets use the shared **Input expressions in actions** contract (§bind), including expression
|
|
2064
|
+
types, one pre-action snapshot, clamping and sequential action visibility; expression strings
|
|
2065
|
+
require `bind`, while literal machine groups require only `machine`. Order on entry: outgoing exitActions, transition actions,
|
|
2066
|
+
incoming enterActions. When `machine` is declared it owns transition `actions`, so that
|
|
2067
|
+
block queues exactly once instead of also executing as a bind-only immediate block.
|
|
2068
|
+
Bind's separate `entry`/`exit` blocks retain their immediate timing. Queue in that order using the ordinary input queue. Effects become
|
|
2069
|
+
visible at the next step's input stage; emits have `layer: null`, as other queued actions.
|
|
2070
|
+
The initial state's enterActions are queued during player construction and apply on the
|
|
2071
|
+
first step. No action mutates the current transition search's inputs.
|
|
2072
|
+
|
|
2073
|
+
A positive blend's endActions queue when the fade completes at the clock stage. Zero-duration
|
|
2074
|
+
or reduced-motion transitions queue them after entry actions at entry. Overshoot at or beyond
|
|
2075
|
+
blend duration also completes immediately. An interrupted fade discards its endActions.
|
|
2076
|
+
Pending actions prevent settlement through the existing queue; they never run twice.
|
|
2077
|
+
|
|
2078
|
+
### Syntax, diagnostics and tooling
|
|
2079
|
+
|
|
2080
|
+
Registry entries define JSON Schema, `anima schema`, both syntaxes and formatting. AniML keeps
|
|
2081
|
+
scalar fields as attributes. Object-valued fields (`mix`, `guard`, `change`, each action group)
|
|
2082
|
+
are JSON-valued attributes, with normal XML escaping; single quotes make authored JSON easier:
|
|
2083
|
+
`<on when="@after(0)" to="pop" change='{"input":"count","mode":"increased"}'/>`.
|
|
2084
|
+
`machineSeed` is an attribute of `<anima>`. Build/decompile/format use the same generic
|
|
2085
|
+
non-node registry extension documented in MODULES §3. Browser JSON requires only machine;
|
|
2086
|
+
browser AniML additionally loads markup, with no machine-specific compiler hook.
|
|
2087
|
+
|
|
2088
|
+
Shape errors retain `type`, `missing-key`, `bad-value`, `unknown-key` and declared-feature
|
|
2089
|
+
errors. Semantic errors provide pointer, message and hint:
|
|
2090
|
+
|
|
2091
|
+
| Code | Pointer | Meaning / correction |
|
|
2092
|
+
|---|---|---|
|
|
2093
|
+
| `machine-state` | state `/kind`, `/reset`, `/on` or edge `/to` | Special state needs empty pose; any cannot be initial/target; exit has no edges; reset must be true; random weights must have finite total. |
|
|
2094
|
+
| `machine-blend` | state `/mix`, `/mix/exit`, `/play` | Supply a numeric axis and sorted unique thresholds; choose an existing child exit clock matching play. |
|
|
2095
|
+
| `machine-clock` | edge or `/blendRatio` | Relative timing needs a play source with finite duration (zero ratios remain zero); choose blend or blendRatio. Blend/continued clocks must be autonomous and unsynchronized. |
|
|
2096
|
+
| `machine-guard` | edge `/guard` or operand | Choose one valid source and matching types; ordered comparisons require numbers. |
|
|
2097
|
+
| `unknown-input`, `input-type` | source, action or `/change/input` | Declare the correctly typed input, including scoped component inputs. |
|
|
2098
|
+
| `unknown-timeline` | `/mix/children/i/play` | Name an existing timeline. |
|
|
2099
|
+
| `unknown-target` | guard operand `/focus` | Name an existing document node. |
|
|
2100
|
+
| bind diagnostics | source `/bind` | Correct the numeric expression. |
|
|
2101
|
+
|
|
2102
|
+
The capability directories map rows to examples, validation, independently calculated
|
|
2103
|
+
conformance cases and native-oracle evidence. A documented oracle skip is not native parity.
|
|
2104
|
+
|
|
2105
|
+
<!-- capability: state-machine.any-state -->
|
|
2106
|
+
|
|
2107
|
+
<!-- capability: state-machine.blend-1d-data -->
|
|
2108
|
+
|
|
2109
|
+
<!-- capability: state-machine.blend-1d-input -->
|
|
2110
|
+
|
|
2111
|
+
<!-- capability: state-machine.blend-additive -->
|
|
2112
|
+
|
|
2113
|
+
<!-- capability: state-machine.blend-direct -->
|
|
2114
|
+
|
|
2115
|
+
<!-- capability: state-machine.blend-exit-clock -->
|
|
2116
|
+
|
|
2117
|
+
<!-- capability: state-machine.blend-weight-constant -->
|
|
2118
|
+
|
|
2119
|
+
<!-- capability: state-machine.blend-weight-data -->
|
|
2120
|
+
|
|
2121
|
+
<!-- capability: state-machine.blend-weight-input -->
|
|
2122
|
+
|
|
2123
|
+
<!-- capability: state-machine.condition-artboard -->
|
|
2124
|
+
|
|
2125
|
+
<!-- capability: state-machine.condition-component -->
|
|
2126
|
+
|
|
2127
|
+
<!-- capability: state-machine.condition-focus -->
|
|
2128
|
+
|
|
2129
|
+
<!-- capability: state-machine.continue-on-exit -->
|
|
2130
|
+
|
|
2131
|
+
<!-- capability: state-machine.entry-state -->
|
|
2132
|
+
|
|
2133
|
+
<!-- capability: state-machine.exit-state -->
|
|
2134
|
+
|
|
2135
|
+
<!-- capability: state-machine.exit-time-relative -->
|
|
2136
|
+
|
|
2137
|
+
<!-- capability: state-machine.state-entry-actions -->
|
|
2138
|
+
|
|
2139
|
+
<!-- capability: state-machine.state-exit-actions -->
|
|
2140
|
+
|
|
2141
|
+
<!-- capability: state-machine.state-random -->
|
|
2142
|
+
|
|
2143
|
+
<!-- capability: state-machine.state-reset -->
|
|
2144
|
+
|
|
2145
|
+
<!-- capability: state-machine.transition-actions -->
|
|
2146
|
+
|
|
2147
|
+
<!-- capability: state-machine.transition-disabled -->
|
|
2148
|
+
|
|
2149
|
+
<!-- capability: state-machine.transition-duration-relative -->
|
|
2150
|
+
|
|
2151
|
+
<!-- capability: state-machine.transition-end-actions -->
|
|
2152
|
+
|
|
2153
|
+
<!-- capability: state-machine.transition-random-weight -->
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
<!-- capability: state-machine.condition-data -->
|
|
2157
|
+
<!-- capability: state-machine.literal-asset -->
|
|
2158
|
+
<!-- capability: state-machine.literal-boolean -->
|
|
2159
|
+
<!-- capability: state-machine.literal-color -->
|
|
2160
|
+
<!-- capability: state-machine.literal-enum -->
|
|
2161
|
+
<!-- capability: state-machine.literal-string -->
|
|
2162
|
+
<!-- capability: state-machine.literal-trigger -->
|
|
2163
|
+
### Typed data guards
|
|
2164
|
+
|
|
2165
|
+
With `data`, a direct scalar path on the left accepts `==`/`!=` against JSON string/enum,
|
|
2166
|
+
boolean, hex colour or `asset("id")` literals. Enum members and typed assets must exist.
|
|
2167
|
+
Colors compare all four RGBA bytes; quoted `&&` stays inside a string. The existing JSON/AniML
|
|
2168
|
+
`when` field retains its spelling. Type/operator mismatches emit `data-guard` at the `/when`
|
|
2169
|
+
pointer with a correction hint. Bare `$trigger` guards consume signal occurrences; there is no
|
|
2170
|
+
integer trigger literal syntax. Details: [data contract](../modules/data/SPEC.md).
|
|
2171
|
+
|
|
2172
|
+
## input
|
|
2173
|
+
|
|
2174
|
+
Optional P4c input behavior is specified in [the input contract](../modules/input/SPEC.md).
|
|
2175
|
+
Capture listeners run from the outermost ancestor down to the target, with authored order at
|
|
2176
|
+
equal depth. A `consume: true` capture listener stops delivery before inner capture or normal
|
|
2177
|
+
listeners. Normal keyboard listeners stop delivery by default; `propagate: true` (AniML
|
|
2178
|
+
`propagate="true"`) passes the key on, while `consume: true` takes precedence. Capture listeners
|
|
2179
|
+
only stop delivery with explicit consume — an intentional difference from Rive, where a capture
|
|
2180
|
+
listener claims the event by default (measured in `tests/oracle/input-capture-phase`): Anima keeps
|
|
2181
|
+
the DOM's rule that observing an event in the capture phase does not take it away from the target. This policy includes nested component keys and all
|
|
2182
|
+
three keyboard phases; unmatched key/modifier filters never claim a key. Focus navigation and
|
|
2183
|
+
native Enter/Space activation retain the focus/host policies above. These input
|
|
2184
|
+
consumption rules apply only to authored Anima listeners; DOM propagation to the
|
|
2185
|
+
surrounding page remains intact as specified in §10.
|
|
2186
|
+
|
|
2187
|
+
Preserve-offset alignment intentionally differs from the measured native Rive probe. Anima
|
|
2188
|
+
stores target origin minus grab point at pointer-down, then adds that offset to each pointer
|
|
2189
|
+
position in parent coordinates. In `tests/oracle/input-align-preserve`, the origin is x=80,
|
|
2190
|
+
the grab is x=88 (offset -8), and the move is x=104: Anima yields x=96 and retains it after
|
|
2191
|
+
release; native Rive yields x=80 at both observations (absolute delta 16). This is an
|
|
2192
|
+
intentional difference recorded separately, never a passing oracle or a relaxed tolerance.
|
|
2193
|
+
Snap alignment agrees. See the retained native command/number dumps and
|
|
2194
|
+
`docs/research/rive-web-runtime.md` lessons 2, 5 and 6 for the core/host boundary rationale.
|
|
2195
|
+
|
|
2196
|
+
Generic registry attributes and the sample command dispatcher are integrated. See per-row reports
|
|
2197
|
+
for native comparison evidence and recorded differences.
|
|
2198
|
+
|
|
2199
|
+
<!-- capability: host-api.coordinate-mapping -->
|
|
2200
|
+
<!-- capability: host-api.dom-listener-control -->
|
|
2201
|
+
<!-- capability: host-api.gamepad -->
|
|
2202
|
+
<!-- capability: host-api.low-level-pointer -->
|
|
2203
|
+
<!-- capability: host-api.low-level-scroll -->
|
|
2204
|
+
<!-- capability: host-api.manual-gamepad-batch -->
|
|
2205
|
+
<!-- capability: host-api.multitouch -->
|
|
2206
|
+
<!-- capability: host-api.pointer-exit -->
|
|
2207
|
+
<!-- capability: host-api.scroll-hit-policy -->
|
|
2208
|
+
<!-- capability: host-api.scroll-latching -->
|
|
2209
|
+
<!-- capability: input.align-preserve -->
|
|
2210
|
+
<!-- capability: input.align-snap -->
|
|
2211
|
+
<!-- capability: input.bounded-hit-tests -->
|
|
2212
|
+
<!-- capability: input.button-middle -->
|
|
2213
|
+
<!-- capability: input.button-secondary -->
|
|
2214
|
+
<!-- capability: input.capture-phase -->
|
|
2215
|
+
<!-- capability: input.cursor-arrow -->
|
|
2216
|
+
<!-- capability: input.cursor-hidden -->
|
|
2217
|
+
<!-- capability: input.cursor-intent -->
|
|
2218
|
+
<!-- capability: input.cursor-pointer -->
|
|
2219
|
+
<!-- capability: input.cursor-resize-horizontal -->
|
|
2220
|
+
<!-- capability: input.cursor-resize-vertical -->
|
|
2221
|
+
<!-- capability: input.cursor-text -->
|
|
2222
|
+
<!-- capability: input.disable-listeners -->
|
|
2223
|
+
<!-- capability: input.gamepad-axis -->
|
|
2224
|
+
<!-- capability: input.gamepad-button -->
|
|
2225
|
+
<!-- capability: input.gamepad-button-phases -->
|
|
2226
|
+
<!-- capability: input.gamepad-connected -->
|
|
2227
|
+
<!-- capability: input.gamepad-disconnected -->
|
|
2228
|
+
<!-- capability: input.gamepad-host -->
|
|
2229
|
+
<!-- capability: input.gamepad-index-mapping -->
|
|
2230
|
+
<!-- capability: input.hit-occlusion-query -->
|
|
2231
|
+
<!-- capability: input.hit-parent-chain -->
|
|
2232
|
+
<!-- capability: input.hit-shape-geometry -->
|
|
2233
|
+
<!-- capability: input.hit-test-host -->
|
|
2234
|
+
<!-- capability: input.key-down -->
|
|
2235
|
+
<!-- capability: input.key-filter -->
|
|
2236
|
+
<!-- capability: input.key-modifiers -->
|
|
2237
|
+
<!-- capability: input.key-repeat -->
|
|
2238
|
+
<!-- capability: input.key-up -->
|
|
2239
|
+
<!-- capability: input.keyboard-bubbling -->
|
|
2240
|
+
<!-- capability: input.multi-touch -->
|
|
2241
|
+
<!-- capability: input.multi-trigger-listener -->
|
|
2242
|
+
<!-- capability: input.opaque-targets -->
|
|
2243
|
+
<!-- capability: input.pointer-cancel -->
|
|
2244
|
+
<!-- capability: input.pointer-drag -->
|
|
2245
|
+
<!-- capability: input.pointer-drag-end -->
|
|
2246
|
+
<!-- capability: input.pointer-drag-start -->
|
|
2247
|
+
<!-- capability: input.pointer-enable -->
|
|
2248
|
+
<!-- capability: input.touch-page-scroll -->
|
|
2249
|
+
<!-- capability: input.trackpad-scroll -->
|
|
2250
|
+
<!-- capability: input.wheel-scroll -->
|
|
2251
|
+
<!-- capability: transforms.collapse -->
|
|
2252
|
+
<!-- capability: transforms.solo -->
|
|
2253
|
+
|
|
2254
|
+
|
|
2255
|
+
|
|
2256
|
+
|
|
2257
|
+
|
|
2258
|
+
|
|
2259
|
+
## events
|
|
2260
|
+
|
|
2261
|
+
Declare `requires: ["events"]` and import the optional events runtime. The module adds
|
|
2262
|
+
`eventDefs`, a name-keyed map of event definitions, and `audioVolumes`, an audio-asset-name map
|
|
2263
|
+
of resource gains in [0,1]. Existing timeline `[time,name]`, state `emit` and listener `emit`
|
|
2264
|
+
spellings and ordering remain unchanged. Definitions enrich every occurrence of that name;
|
|
2265
|
+
undeclared names continue to report the original `{name,layer,time}` record.
|
|
2266
|
+
|
|
2267
|
+
A definition has optional `payload`, `url`, `target`, `audio` and `volume` fields. `payload`
|
|
2268
|
+
is a field-name map of `{type,value?,input?,address?}` objects. Type is `number`, `bool` or
|
|
2269
|
+
`string` or `color`; exactly one source is required. A literal must have the declared type. `input`
|
|
2270
|
+
references a bool/number input of the same type; `address` references an animatable property
|
|
2271
|
+
of the same type. Each reported event gets its own detached `payload` object. Values resolve
|
|
2272
|
+
at the end of the local player's step, after queued inputs, state resolution and bindings;
|
|
2273
|
+
all occurrences in that step observe that resolved snapshot, including time-zero entry events.
|
|
2274
|
+
Payloads survive component bubbling unchanged; parent definitions do not reinterpret child names.
|
|
2275
|
+
For number/bool/color fields, `value: "{expression}"` additionally requires bind and evaluates
|
|
2276
|
+
against the same final-step input snapshot as `input` sources; it does not capture the instant of
|
|
2277
|
+
an emit action. String fields always stay literal, including braces. Number fields accept bool
|
|
2278
|
+
expressions as 0/1; bool fields require bool; colour fields require a colour expression or hex
|
|
2279
|
+
literal and report a canonical hex string (including alpha when needed). Colours cannot reference
|
|
2280
|
+
inputs because inputs are bool/number/trigger only. `anima sample` snapshots include these fields
|
|
2281
|
+
verbatim. This is snapshot sampling, not scripting.
|
|
2282
|
+
`events.script-event-context` is deferred to P9 because no script VM/context exists.
|
|
2283
|
+
|
|
2284
|
+
`url` is an HTTP(S) URL or a relative reference. `target` is `_self` (default), `_blank`,
|
|
2285
|
+
`_parent` or `_top` and requires `url`. Core rejects non-HTTP schemes (`event-url`). Host URL
|
|
2286
|
+
parsing is authoritative: no navigation occurs without explicit `url-policy="allow"` or an
|
|
2287
|
+
`icon.urlPolicy(url,target,event)` callback returning exactly true. Relative references resolve
|
|
2288
|
+
against the host document and must stay on its origin; absolute HTTP(S) may cross origins.
|
|
2289
|
+
Protocol-relative or backslash references that resolve off-origin are rejected. Canonical
|
|
2290
|
+
protocol checks run even with a permissive policy. Delivery uses `window.open` with `noopener`;
|
|
2291
|
+
browser popup/target restrictions still apply. There is no navigation in core or CLI.
|
|
2292
|
+
|
|
2293
|
+
`audio` references an assets-module audio asset (`event-audio` otherwise); `volume` is an
|
|
2294
|
+
optional [0,1] event gain, default 1. The browser requires prior user activation; earlier events
|
|
2295
|
+
are dropped without fetching or decoding and are never replayed later. Decode is on demand and
|
|
2296
|
+
cached per element/load/asset. Each occurrence starts a separate source, honoring asset clip `start`/`end` (assets chapter). Effective gain is
|
|
2297
|
+
`event.volume * audioVolumes[asset] * icon.volume`, with each omitted gain equal to 1.
|
|
2298
|
+
For bubbled component events the owning child supplies the asset and resource gain; the host
|
|
2299
|
+
resolver receives its dot-scoped asset name (for example `child.tone`).
|
|
2300
|
+
The asset resolver is consulted as for image assets. Relative asset sources use the element's
|
|
2301
|
+
`src`, falling back to the host document. Reload/disconnect/destroy stops sources, closes the
|
|
2302
|
+
context and invalidates outstanding decodes. Browser resume/fetch/decode failures report an
|
|
2303
|
+
`anima:error` diagnostic `event-audio-load`; they do not change deterministic player output.
|
|
2304
|
+
|
|
2305
|
+
Local listeners may use `on: "event:<name>"` to queue their existing fire/set/toggle/emit
|
|
2306
|
+
actions for the next step. This is an **intentional difference** from Rive's same-frame
|
|
2307
|
+
listener response: a step cannot re-enter itself, so event-caused writes cannot affect
|
|
2308
|
+
its own transitions or recursively run an event cascade. All runtimes keep this queue
|
|
2309
|
+
boundary, including `step(0)` calls. Unknown local event names are `unknown-event`.
|
|
2310
|
+
Pending actions keep the player unsettled; callbacks never recursively re-enter a step.
|
|
2311
|
+
Components retain their existing scoped listener spelling and next-parent-step semantics.
|
|
2312
|
+
|
|
2313
|
+
Conditions and event fields that read an input changed by a listener action see the **new**
|
|
2314
|
+
value in the step that applies the action: a click that sets `count = count + 1` and a
|
|
2315
|
+
transition `when="mod(count, 5) == 0"` report the milestone at counts 5, 10, 15, … This is an
|
|
2316
|
+
**intentional difference** from the measured Rive behaviour (`tests/oracle/data-bind-listener-actions`),
|
|
2317
|
+
where the same scene reports at counts 6, 11, 16, … because its condition still sees the value
|
|
2318
|
+
from before the increment. Anima follows what the author wrote.
|
|
2319
|
+
|
|
2320
|
+
For documents requiring events, state exits additionally report `@state-end`, then
|
|
2321
|
+
`@transition-start`, each with `{payload:{state}}` (source state for end, target for start).
|
|
2322
|
+
A completed blend reports `@transition-end` once with the target state. A state may additionally name
|
|
2323
|
+
`endEmit`, and each transition may name `startEmit` and `endEmit`. Each named action reports an
|
|
2324
|
+
ordinary event immediately after its corresponding metadata signal, using the same definitions,
|
|
2325
|
+
payload resolution, URL/audio policy and next-step listeners as timeline and entry events.
|
|
2326
|
+
Interrupted transitions do not fire their end action. These optional fields require events and
|
|
2327
|
+
use the identical attribute names on AniML `<state>` and `<on>`. Component scopes expose the
|
|
2328
|
+
named action events to existing parent listeners. Each player's active action is independent.
|
|
2329
|
+
Zero-duration,
|
|
2330
|
+
reduced-motion and fully carried blends report end immediately before the target's entry emit.
|
|
2331
|
+
Interrupted blends do not report completion. Fade completion is checked before layer timeline
|
|
2332
|
+
crossings, after listener actions. These signals have timeline time 0 and the owning layer.
|
|
2333
|
+
|
|
2334
|
+
Repeating timeline states report `@loop` at each boundary: every duration for `loop`, every
|
|
2335
|
+
turn for `pingpong`. There is no loop signal at initial entry, held/seeked clocks, reduced-motion
|
|
2336
|
+
freezes or `once` completion. The existing motion crossing policy supplies work area, speed,
|
|
2337
|
+
reverse and carry behavior. Loop signals precede that state's ordinary timeline event block;
|
|
2338
|
+
ordinary event ordering is unchanged. Coincident authored endpoints remain distinct, while each
|
|
2339
|
+
loop boundary is reported once. Existing crossing caps remain unchanged.
|
|
2340
|
+
|
|
2341
|
+
`event-payload`, `event-input` and `event-address` reject ambiguous/mistyped sources, missing
|
|
2342
|
+
or mistyped input/property references. Diagnostics include a JSON pointer, message and hint.
|
|
2343
|
+
AniML uses root `<event-def name="…" payload='{"field":{"type":"number","input":"count"}}'
|
|
2344
|
+
url="…" target="_blank" audio="tone" volume="0.5"/>` and
|
|
2345
|
+
`<audio-volume asset="tone" value="0.4"/>`. Payload is JSON inside the XML attribute and obeys
|
|
2346
|
+
normal XML escaping. Neither form changes timeline `<event at="…" name="…"/>`.
|
|
2347
|
+
|
|
2348
|
+
The event delivery/subscription separation follows the host/runtime concerns recorded in
|
|
2349
|
+
`docs/research/rive-web-runtime.md` §§2–3; no Rive implementation or documentation text is used.
|
|
2350
|
+
|
|
2351
|
+
|
|
2352
|
+
Repeated event audio resolves its emitting player through both ordinary component placements and
|
|
2353
|
+
list indices (for example `rows.1.inside.play`). Decode caches use asset declaration identity, so
|
|
2354
|
+
reordering different component catalogs with the same clip name cannot replay another row's audio.
|
|
2355
|
+
The host still observes the scoped event and the ordinary Web Audio start call after activation.
|
|
2356
|
+
|
|
2357
|
+
## contour
|
|
2358
|
+
|
|
2359
|
+
The optional `contour` module (5120 B browser budget) contains authoring geometry and path
|
|
2360
|
+
operations. Declare `requires: ["contour"]`; gradients additionally require paint, ordered
|
|
2361
|
+
paints additionally require composite. Core hooks contribute resolved geometry, never backend
|
|
2362
|
+
commands. This follows MODULES §3.1 and the separation discussed in
|
|
2363
|
+
[web runtime research](../docs/research/rive-web-runtime.md#lessons-for-anima).
|
|
2364
|
+
The corner-control distance below follows Rive’s MIT natural-rounding construction; see NOTICE.
|
|
2365
|
+
The other algorithms are independent constructions.
|
|
2366
|
+
|
|
2367
|
+
### Editable vertex authoring forms and geometry origin
|
|
2368
|
+
|
|
2369
|
+
A `contour` node has `vertices`, at least two objects `{point:[x,y], kind, …}`, optional
|
|
2370
|
+
`closed` (default false), and optional `geometryOrigin:[x,y]` (default `[0,0]`). It accepts
|
|
2371
|
+
ordinary painted-node transforms/fills/strokes. Each vertex point is in grid units; origin is
|
|
2372
|
+
subtracted from every point before ordinary node transforms, independently of paint coordinates.
|
|
2373
|
+
AniML `<contour>` uses `vertices="[…JSON…]"`, `closed="true"`, and
|
|
2374
|
+
`geometry-origin="x y"`. The array is explicitly JSON-valued in the registry. Node/paint
|
|
2375
|
+
fields, schema and CLI names derive from the ordinary registry. Authoring fields are static;
|
|
2376
|
+
editing means reloading revised vertex data. Path morphing remains the paint module's `d` API.
|
|
2377
|
+
|
|
2378
|
+
Vertex kinds compile to an ordinary path shared by SVG, canvas and the outline hook:
|
|
2379
|
+
|
|
2380
|
+
| kind | Optional fields and construction |
|
|
2381
|
+
| --- | --- |
|
|
2382
|
+
| straight | No handles; adjacent edges are lines unless the neighbour supplies a cubic handle. |
|
|
2383
|
+
| mirrored | `handle:[dx,dy]`, default zero; outgoing is handle, incoming is its exact negative. |
|
|
2384
|
+
| asymmetric | `outgoing:[dx,dy]`, `incoming:[dx,dy]`, defaults zero. Outgoing supplies direction and length; incoming supplies length only, projected opposite outgoing. Nonzero incoming length requires a nonzero outgoing direction. |
|
|
2385
|
+
| detached | `incoming` and `outgoing` vectors, independently positioned, default zero. |
|
|
2386
|
+
| rounded | `radius ≥ 0`, default zero. Use one cutback min(radius, half previous length, half next length) and a cubic with the natural-rounding controls below. Open endpoints do not round. |
|
|
2387
|
+
|
|
2388
|
+
A cubic edge uses source outgoing and destination incoming controls (point plus vector).
|
|
2389
|
+
A closed contour joins the last vertex to the first, then closes. Fields belonging to another
|
|
2390
|
+
kind produce `bad-value` at that vertex field with a corrective hint; invalid array sizes,
|
|
2391
|
+
coordinates, kinds and negative radii retain structural diagnostics. This is an explicit,
|
|
2392
|
+
deterministic authoring contract. For rounded corners, let u/v be the vectors from the point to
|
|
2393
|
+
its previous/next neighbour (using the neighbour’s adjacent cubic control when present).
|
|
2394
|
+
Let θ = atan2(abs(cross(u,v)), dot(u,v)). With cutback r, set
|
|
2395
|
+
h = min(1, (4/3) tan(θ/4) × (θ < π/2 ? 1+cos θ : 2−sin θ)).
|
|
2396
|
+
The cubic’s controls lie fraction h from each cut toward the authored point. Zero-length
|
|
2397
|
+
neighbour vectors collapse the rounding. This mathematical construction closely follows
|
|
2398
|
+
[Rive’s MIT path implementation](https://github.com/rive-app/rive-runtime/blob/main/src/shapes/path.cpp),
|
|
2399
|
+
reimplemented here without source-text copying; attribution/license are in NOTICE. Native
|
|
2400
|
+
identity path-effect captures check acute, right, obtuse and degenerate corners independently.
|
|
2401
|
+
|
|
2402
|
+
### Sequential and synchronized trim; ordered/shared effects
|
|
2403
|
+
|
|
2404
|
+
`stroke.trim` is `{start?,end?,offset?,mode?}`: defaults 0,1,0,`sequential`. Start/end are
|
|
2405
|
+
fractions in 0..1. Offset is an arbitrary fraction of one turn. Sequential trim measures the
|
|
2406
|
+
concatenation of all subpath lengths in authored order; synchronized trim applies the same
|
|
2407
|
+
fractions independently to each subpath. A nonpositive end−start draws nothing; a full span
|
|
2408
|
+
keeps all geometry; other spans begin at `(start+offset) mod 1`, wrapping at one.
|
|
2409
|
+
|
|
2410
|
+
`stroke.effects` is an ordered JSON array of trim/dash objects or shared-stack names.
|
|
2411
|
+
A trim is `{type:"trim",start?,end?,offset?,mode?}`. A dash is
|
|
2412
|
+
`{type:"dash",dash:[nonnegative grid lengths…],offset?}`. Odd dash arrays repeat twice;
|
|
2413
|
+
at least one entry must be positive. Positive offsets advance into the pattern. Dashes restart
|
|
2414
|
+
at each input subpath. Each operation consumes the previous operation's output, so trim after
|
|
2415
|
+
dash measures the remaining dash fragments, while dash after trim restarts at each trimmed
|
|
2416
|
+
fragment. Effects alter stroke geometry only, preserving fill geometry and the hit outline.
|
|
2417
|
+
|
|
2418
|
+
Document `effects` maps names to arrays of effect objects, expanded by name at each use.
|
|
2419
|
+
Shared arrays cannot contain references, so cycles are impossible. Each use starts from its
|
|
2420
|
+
own node geometry. AniML uses JSON-valued root `effects` and stroke `effects` / `trim`
|
|
2421
|
+
attributes, with ordinary XML escaping. Compound document fields explicitly opt in via their
|
|
2422
|
+
registry markup name; existing document containers retain their structural forms.
|
|
2423
|
+
`unknown-source` identifies a missing stack name at its exact array index. A dash without
|
|
2424
|
+
positive length or fields of the wrong effect kind is `bad-value`, with field pointer and hint.
|
|
2425
|
+
An effect stack and `trim`, or either plus legacy `dash` / `trimStart` / `trimEnd` /
|
|
2426
|
+
`trimOffset`, is `paint-conflict`; move all operations into one stack. Legacy stroke behaviour
|
|
2427
|
+
is unchanged. Effects are static; changing the stack requires reloading the document.
|
|
2428
|
+
|
|
2429
|
+
Cubic path segments are subdivided at the midpoint until control-polygon length minus chord
|
|
2430
|
+
is at most 0.0001 local grid units (maximum depth 20). Operations slice the resulting segments
|
|
2431
|
+
by Euclidean length. Geometry is compiled into resolved per-paint paths; neither backend uses
|
|
2432
|
+
its own dash/trim approximation. Existing path-length/dash algorithms are unchanged.
|
|
2433
|
+
|
|
2434
|
+
### Clockwise fill
|
|
2435
|
+
|
|
2436
|
+
`fill.winding: "clockwise"` (AniML `fill-winding="clockwise"`) selects strictly positive
|
|
2437
|
+
winding, overriding the ordinary fill `rule` for rendering. In local SVG y-down coordinates,
|
|
2438
|
+
a clockwise contour contributes +1, counterclockwise −1. The filled region contains exactly
|
|
2439
|
+
points whose total winding is greater than zero; nonzero negative regions are empty. Open
|
|
2440
|
+
contours implicitly close. This applies to self-intersections, not merely signed whole-contour
|
|
2441
|
+
area. The operation runs in local geometry space before transforms (a reflected node remains
|
|
2442
|
+
filled). It compiles to nonoverlapping trapezoids split at endpoints and edge intersections;
|
|
2443
|
+
curves use the same subdivision rule as effects. Both backends fill that ordinary path nonzero.
|
|
2444
|
+
Stroke geometry is unchanged. Core pointer hit testing uses the same positive local winding,
|
|
2445
|
+
including implicit closure of open fill contours, holes and self-intersections. Reflections do
|
|
2446
|
+
not change the local winding test. Ordered fills contain a point if any fill contains it; stroke
|
|
2447
|
+
and listener padding continue to use the original outline distance.
|
|
2448
|
+
|
|
2449
|
+
## mix
|
|
2450
|
+
|
|
2451
|
+
The optional `mix` module (5120 B browser budget) refines backend compositing. Declare
|
|
2452
|
+
`requires: ["mix"]`; base node blend and ordered paints still require composite.
|
|
2453
|
+
|
|
2454
|
+
### Additive blend and strength
|
|
2455
|
+
|
|
2456
|
+
`node.mix: {mode, strength?}` selects a node compositor. Mode accepts the existing composite
|
|
2457
|
+
blend names plus `additive`; strength defaults one and is in 0..1. `mix` and node `blend`
|
|
2458
|
+
cannot coexist (`paint-conflict` at `/mix` with an instruction to choose one).
|
|
2459
|
+
`<group mix="{"mode":"additive","strength":0.5}">` illustrates the
|
|
2460
|
+
JSON-valued AniML spelling. `node.mix.strength` is an ordinary animatable number address.
|
|
2461
|
+
|
|
2462
|
+
Additive means Canvas2D `lighter`: sum premultiplied source and destination components,
|
|
2463
|
+
clamping to the output range. Strength interpolates between source-over (zero) and addition (one). For premultiplied
|
|
2464
|
+
source `S`, destination `D`, source alpha `Sa` and strength `a`, the result is
|
|
2465
|
+
`S + D * (1 - Sa * (1 - a))`, clamped per component (including alpha). Node opacity
|
|
2466
|
+
scales `S` and `Sa` before this operation. Canvas evaluates fractional strength once in premultiplied pixel space to avoid rounding
|
|
2467
|
+
the source contribution twice in 8-bit intermediate surfaces; strength zero/one use native
|
|
2468
|
+
source-over/lighter. Strength has no effect on other compositors. SVG refuses
|
|
2469
|
+
additive nodes or paints with `unsupported-render`, reason and a canvas hint because
|
|
2470
|
+
`plus-lighter` is not consistently supported by the supported SVG engines/static renderer.
|
|
2471
|
+
|
|
2472
|
+
### Per-paint blend override
|
|
2473
|
+
|
|
2474
|
+
Fill and stroke objects accept static `blend`, default `inherit`, selecting any node mix mode,
|
|
2475
|
+
and animatable `blendAmount` in 0..1 (AniML `fill-blend`, `stroke-blend`,
|
|
2476
|
+
`fill-blend-amount`, `stroke-blend-amount`). If any paint overrides blending, the node blend
|
|
2477
|
+
is moved to each paint: inherit chooses the node blend, explicit mode replaces it. Paints
|
|
2478
|
+
compose in authored order, then ordinary node opacity/masking/group isolation applies.
|
|
2479
|
+
`blendAmount` selects the same source-over/addition interpolation for an additive paint; it
|
|
2480
|
+
has no effect on other modes. Canvas uses each paint's compositor. SVG gives each paint a
|
|
2481
|
+
separate blend group, with the geometry transform on an inner group. To avoid Firefox isolating
|
|
2482
|
+
transformed ancestors, documents with paint overrides move node transforms into these inner
|
|
2483
|
+
world-space groups; authored opacity, masks and node compositors retain their isolation. Mask
|
|
2484
|
+
sources and clip outlines use the same world coordinate space. Gradients and fixed strokes
|
|
2485
|
+
remain local to the inner geometry transform. Additive overrides retain the explicit SVG refusal.
|
|
2486
|
+
A node without paint overrides retains the existing group compositor exactly.
|
|
2487
|
+
|
|
2488
|
+
### Host renderer and factory mapping
|
|
2489
|
+
|
|
2490
|
+
`AnimaIcon.renderers[name] = icon => renderer` is the host factory. It creates an `IconRenderer`
|
|
2491
|
+
whose `draw(DrawFrame,force)`, `layout()`, `point(x,y)`, `connect()`, `disconnect()` and
|
|
2492
|
+
`destroy()` implement the backend bridge. Factories own their paths, gradients, buffers, image
|
|
2493
|
+
decoding and resource caches; there is no mandatory global resource superclass or per-verb
|
|
2494
|
+
bridge. A custom backend may inject its own resource factory through that closure. The core
|
|
2495
|
+
contract is the draw frame, including transforms, geometry, paints, clipping and opacity, so
|
|
2496
|
+
playback is shared. Browser tests create and release host-owned paths and gradients in both
|
|
2497
|
+
engines. These host-only API rows have no observable Rive screenshot counterpart.
|
|
2498
|
+
## data
|
|
2499
|
+
|
|
2500
|
+
The optional `data` module generalizes typed inputs with view models, scalar and reference properties,
|
|
2501
|
+
named instances, `$` paths, binding direction modes, data-writing listeners and node custom properties.
|
|
2502
|
+
Its normative contract, step position, spellings, diagnostics and limits are in
|
|
2503
|
+
[modules/data/SPEC.md](../modules/data/SPEC.md). `data` bindings resolve in §7's existing binding
|
|
2504
|
+
position, after layers and before clamping/reporting/children. Change listeners queue for the next
|
|
2505
|
+
step; no callback re-enters the machine. Existing 0.1 input behavior is unchanged.
|
|
2506
|
+
|
|
2507
|
+
Capability evidence and incomplete rows are enumerated in
|
|
2508
|
+
[modules/data/ROWS.json](../modules/data/ROWS.json). No phase-completion claim is made.
|
|
2509
|
+
|
|
2510
|
+
<!-- capability: data.bind-conditions -->
|
|
2511
|
+
<!-- capability: data.bind-converter-options -->
|
|
2512
|
+
<!-- capability: data.bind-instance-values -->
|
|
2513
|
+
<!-- capability: data.bind-keyframes -->
|
|
2514
|
+
With `data motion`, a keyframe value may be a direct scalar data path. Sampling resolves needed
|
|
2515
|
+
endpoints against the current player before interpolation, including blends and TimelineInstances,
|
|
2516
|
+
without mutating shared tracks. JSON `[0.5, "$peak"]` and AniML `<key t="0.5" value="$peak"/>`
|
|
2517
|
+
are equivalent. Full typing, diagnostics and timing: [data](../modules/data/SPEC.md#data-keyframe-values).
|
|
2518
|
+
|
|
2519
|
+
<!-- seam(P7-2): data.bind-layout is specified with layout below. -->
|
|
2520
|
+
<!-- capability: data.bind-script-inputs -->
|
|
2521
|
+
<!-- capability: data.bind-text -->
|
|
2522
|
+
<!-- capability: data.binding-absolute -->
|
|
2523
|
+
<!-- capability: data.binding-once -->
|
|
2524
|
+
<!-- capability: data.binding-relative -->
|
|
2525
|
+
<!-- capability: data.binding-to-source -->
|
|
2526
|
+
<!-- capability: data.binding-two-way-prefer-source -->
|
|
2527
|
+
<!-- capability: data.binding-two-way-prefer-target -->
|
|
2528
|
+
<!-- capability: data.component-public-data -->
|
|
2529
|
+
<!-- capability: data.custom-property-boolean -->
|
|
2530
|
+
<!-- capability: data.custom-property-color -->
|
|
2531
|
+
<!-- capability: data.custom-property-enum -->
|
|
2532
|
+
<!-- capability: data.custom-property-number -->
|
|
2533
|
+
<!-- capability: data.custom-property-string -->
|
|
2534
|
+
<!-- capability: data.custom-property-trigger -->
|
|
2535
|
+
<!-- capability: data.default-instance -->
|
|
2536
|
+
<!-- capability: data.editor-preview-instance -->
|
|
2537
|
+
<!-- capability: data.enum-members -->
|
|
2538
|
+
<!-- capability: data.global-model -->
|
|
2539
|
+
<!-- capability: data.instance-export -->
|
|
2540
|
+
<!-- capability: data.list-items -->
|
|
2541
|
+
<!-- capability: data.model-definition -->
|
|
2542
|
+
<!-- capability: data.named-instances -->
|
|
2543
|
+
<!-- capability: data.operation-operand-view-model -->
|
|
2544
|
+
<!-- capability: data.property-artboard -->
|
|
2545
|
+
<!-- capability: data.property-asset-blob -->
|
|
2546
|
+
<!-- capability: data.property-asset-font -->
|
|
2547
|
+
<!-- capability: data.property-asset-image -->
|
|
2548
|
+
<!-- capability: data.property-boolean -->
|
|
2549
|
+
<!-- capability: data.property-color -->
|
|
2550
|
+
<!-- capability: data.property-enum-custom -->
|
|
2551
|
+
<!-- capability: data.property-enum-system -->
|
|
2552
|
+
<!-- capability: data.property-list -->
|
|
2553
|
+
<!-- capability: data.property-number -->
|
|
2554
|
+
<!-- capability: data.property-string -->
|
|
2555
|
+
<!-- capability: data.property-symbol-list-index -->
|
|
2556
|
+
<!-- capability: data.property-text-style -->
|
|
2557
|
+
<!-- capability: data.property-trigger -->
|
|
2558
|
+
<!-- capability: data.property-view-model -->
|
|
2559
|
+
<!-- capability: data.script-model-api -->
|
|
2560
|
+
<!-- capability: data.scripted-converter -->
|
|
2561
|
+
<!-- capability: data.state-action-trigger -->
|
|
2562
|
+
<!-- capability: data.system-enum-horizontal-text-align -->
|
|
2563
|
+
<!-- capability: data.system-enum-layout-alignment -->
|
|
2564
|
+
<!-- capability: data.system-enum-layout-direction -->
|
|
2565
|
+
<!-- capability: data.system-enum-layout-display -->
|
|
2566
|
+
<!-- capability: data.system-enum-layout-flex-direction -->
|
|
2567
|
+
<!-- capability: data.system-enum-layout-position -->
|
|
2568
|
+
<!-- capability: data.system-enum-layout-position-units -->
|
|
2569
|
+
<!-- capability: data.system-enum-layout-scale-type -->
|
|
2570
|
+
<!-- capability: data.system-enum-layout-wrap -->
|
|
2571
|
+
<!-- capability: data.system-enum-scroll-direction -->
|
|
2572
|
+
<!-- capability: data.system-enum-semantic-check-state -->
|
|
2573
|
+
<!-- capability: data.system-enum-text-box -->
|
|
2574
|
+
<!-- capability: data.system-enum-text-overflow -->
|
|
2575
|
+
<!-- capability: data.system-enum-text-trim-bottom -->
|
|
2576
|
+
<!-- capability: data.system-enum-text-trim-top -->
|
|
2577
|
+
<!-- capability: data.system-enum-vertical-text-align -->
|
|
2578
|
+
<!-- capability: data.system-enum-word-wrap -->
|
|
2579
|
+
<!-- capability: drawing-paint.gradient-channel-binding -->
|
|
2580
|
+
<!-- capability: input.data-listener -->
|
|
2581
|
+
<!-- capability: input.write-data-constant -->
|
|
2582
|
+
<!-- capability: input.write-data-copy -->
|
|
2583
|
+
<!-- capability: input.write-focused -->
|
|
2584
|
+
<!-- capability: input.write-gamepad-axis -->
|
|
2585
|
+
<!-- capability: input.write-gamepad-button-pressed -->
|
|
2586
|
+
<!-- capability: input.write-gamepad-button-value -->
|
|
2587
|
+
<!-- capability: input.write-gamepad-changed-value -->
|
|
2588
|
+
|
|
2589
|
+
## host
|
|
2590
|
+
|
|
2591
|
+
Optional host data scripting is specified in [modules/host/SPEC.md](../modules/host/SPEC.md).
|
|
2592
|
+
Import the host entry alongside `data`; it adds no document fields or AniML spellings. It provides
|
|
2593
|
+
typed handles, reflection, instances, binding, batched writes, change subscriptions delivered after
|
|
2594
|
+
the step that made the change, and the web `auto-bind` attribute.
|
|
2595
|
+
|
|
2596
|
+
|
|
2597
|
+
<!-- capability: assets.replacement-scope-image -->
|
|
2598
|
+
### Image replacement scope
|
|
2599
|
+
|
|
2600
|
+
Host `image(path).set(URL | Blob)` decodes a player-local replacement and queues only that
|
|
2601
|
+
property's resource ID; image `asset` bindings follow it at the next step. Shared compiled
|
|
2602
|
+
components, other data properties and authored resources retain their identities. Static nodes
|
|
2603
|
+
without asset bindings keep legacy per-player resource replacement. See
|
|
2604
|
+
[host editing](../modules/host-edit/SPEC.md) and [data-host](../modules/data-host/SPEC.md).
|
|
2605
|
+
|
|
2606
|
+
## host-edit
|
|
2607
|
+
|
|
2608
|
+
Optional page-data editing is specified in [modules/host-edit/SPEC.md](../modules/host-edit/SPEC.md).
|
|
2609
|
+
Import `host-edit` alongside `host` and `data`; it adds no document fields. It provides
|
|
2610
|
+
identity-preserving list mutation, default and named instance discovery, colour and enum helpers,
|
|
2611
|
+
image replacement on the web, nested component player access, component root selection and a
|
|
2612
|
+
constructor readiness promise. Its runtime registration key is `hostedit`.
|
|
2613
|
+
|
|
2614
|
+
## convert
|
|
2615
|
+
|
|
2616
|
+
Optional feature `convert`, after `bind` and `data`; no runtime dependencies. JSON declarations are a
|
|
2617
|
+
`converters` map at the document root. Each value is `{type, ...options}` or `{chain: [names...]}`.
|
|
2618
|
+
Names in a group execute left to right; forward references are permitted, cycles and missing
|
|
2619
|
+
references are errors. A group occurrence has its own state key, including repeated names.
|
|
2620
|
+
|
|
2621
|
+
Named converters/groups register through the active data module’s `registerConverter(name, fn)`;
|
|
2622
|
+
`fn(value, options, ctx)` receives `{player, dt, direction, get, state, key}`. Definitions are selected
|
|
2623
|
+
from that player’s document, so equal names in different documents remain independent. Bindings
|
|
2624
|
+
require `data` and `convert`. Binding `options` override declaration options; whole `$path` strings
|
|
2625
|
+
resolve from the player’s current data scope. Formula `$` operands use the same resolver and bind
|
|
2626
|
+
parser. Unknown named converters and invalid numeric operand paths fail at load with pointers.
|
|
2627
|
+
|
|
2628
|
+
`ctx.state` is a shared player-local Map. Convert namespaces a separate map per stable binding
|
|
2629
|
+
`key` inside it, preserving external converters’ access to the shared map. Interpolation state is stored only
|
|
2630
|
+
there, keyed by group occurrence and direction, including repeated converter names. No state is
|
|
2631
|
+
stored on a loaded model or shared between players. The direct API `convertValue` and
|
|
2632
|
+
`createConverterRunner` accept `{player,time,state,key?,get?,direction?}`; `time` is milliseconds
|
|
2633
|
+
and direct temporal calls retain their context map. The runtime supplies `player.time * 1000`.
|
|
2634
|
+
Converter-free documents install no scheduler wrappers. Active interpolation and `random="always"`
|
|
2635
|
+
keep the player unsettled; `once`, `on-change`, and completed interpolation allow sleep.
|
|
2636
|
+
Bindings evaluate once per data binding evaluation; `mode="once"` retains its converted output.
|
|
2637
|
+
|
|
2638
|
+
| Type | Options and semantics |
|
|
2639
|
+
| --- | --- |
|
|
2640
|
+
| formula | `expression`, `random`: always (default), once, on-change. Variable `value` is the input. |
|
|
2641
|
+
| calculate | `operation`, numeric `operand` (default 1); arithmetic and registered math operations. |
|
|
2642
|
+
| range | `from` and `to` number pairs (default 0 1); map linearly. `modulo`, then `clamp`, then `reverse` apply to normalized progress. Reversed endpoints are permitted; zero-width source is invalid. Optional `ease` uses the existing timeline easing grammar (default linear). |
|
|
2643
|
+
| round | `decimals` (0–100, default 0), `mode`: round/floor/ceil; ECMAScript rounding semantics. |
|
|
2644
|
+
| negate | Boolean inversion; numeric inputs are errors. |
|
|
2645
|
+
| to-number | Number, boolean or string conversion using ECMAScript Number; nonnumeric strings become zero. |
|
|
2646
|
+
| to-string | ECMAScript String conversion. |
|
|
2647
|
+
| number-format | `decimals` (0–100, default 0), `grouping` boolean; fixed precision, dot decimal, comma thousands; locale independent. |
|
|
2648
|
+
| color-format | Hex colour or RGBA tuple → `format` hex/hexa/rgb/rgba (default hex), or component `pattern` (see below). |
|
|
2649
|
+
| pad | String `length` (0–10000), `fill` (nonempty string, default space), `side` start/end. UTF-16 length. |
|
|
2650
|
+
| trim | `mode`: start/end/all/none; ECMAScript whitespace. |
|
|
2651
|
+
| template | `text`, `open`/`close` delimiters (default braces), `grouping`, optional `args` map/array. Without `args`, scalar input is argument 0 and a list supplies positional arguments. Named keys, numeric positions and sequential `{}` placeholders are supported. `args` values may be `$` paths. `{points:2:8}` fixes two decimals and left-pads to width 8 (maximum 10000). Missing arguments are errors. |
|
|
2652
|
+
| remove-zeros | Strip redundant fractional zeros and empty decimal point; integer zeros remain. |
|
|
2653
|
+
| enum-to-index | `values`: ordered strings. Unknown values are errors. |
|
|
2654
|
+
| list-length | Return list length. |
|
|
2655
|
+
| number-to-list | Integer count 0–10000; produce index values or repeated `item`. Model instance creation awaits data integration. |
|
|
2656
|
+
| trigger | Boolean passthrough or truncated integer counter; a trigger binding destination fires when the converted output changes (next step, no initial pulse). |
|
|
2657
|
+
| degrees-to-radians | Multiply by pi/180 forward; by 180/pi in reverse. |
|
|
2658
|
+
| normalize | Normalize using `from` pair, default 0 1; reverse denormalizes with the same pair. No implicit clamp. |
|
|
2659
|
+
| interpolate | `duration` in milliseconds, default 0. First evaluation returns input immediately; changes retarget from the current sample. Numeric or rounded RGBA channel interpolation; optional `ease` uses the existing timeline easing grammar (default linear). No extrapolation before start or after completion. |
|
|
2660
|
+
|
|
2661
|
+
Expressions reuse bind precedence, token/depth limits, numeric/bool coercion and non-finite-to-zero
|
|
2662
|
+
rules without eval or dynamic code generation. Added calls: sin/cos/tan/asin/acos/atan/atan2,
|
|
2663
|
+
log (natural), exp, pow, sqrt, round, ceil, floor, abs, random(). Long aliases sine/cosine/tangent,
|
|
2664
|
+
asine/acosine/atangent/atangent2/power/squareRoot are accepted. Radians for trig. Existing
|
|
2665
|
+
min/max/clamp/mix/mod remain available. All math functions, including random(), register through the active bind module’s
|
|
2666
|
+
`registerFunction(name,arity,fn)` and formulas compile with its `parse` hook. There is no second
|
|
2667
|
+
expression parser. Random is evaluated within the formula context; unscoped random calls outside
|
|
2668
|
+
a formula return zero. Math functions are available to ordinary bindings whenever convert is
|
|
2669
|
+
installed. Random uses independently implemented uint32 LCG
|
|
2670
|
+
`seed = (1664525 * seed + 1013904223) mod 2^32`, output seed/2^32; the stream is shared per player in authored evaluation order, while sites/bindings cache separately.
|
|
2671
|
+
An explicit player/script seed joins this stream with machine draws through the core source, in
|
|
2672
|
+
evaluation order. Reseeding resets the source, preserving existing once/on-change site caches.
|
|
2673
|
+
`always` advances on evaluation, `once` caches per site, `on-change` invalidates when the incoming
|
|
2674
|
+
value changes under Object.is. Repeated evaluation of unchanged input retains the cached value.
|
|
2675
|
+
|
|
2676
|
+
Diagnostics: `convert-unknown-type`, `convert-options`, `convert-type`, `convert-cycle`,
|
|
2677
|
+
`convert-reference`; formula errors retain bind codes. Load-time diagnostics point at
|
|
2678
|
+
`/converters/<escaped-name>/<option>` where possible and include message and hint.
|
|
2679
|
+
`ConverterError` carries code and option for the data integration to attach a binding pointer.
|
|
2680
|
+
|
|
2681
|
+
AniML adapter: `convertMarkup.rootChild` accepts `<converter name="pct" type="range" from="0 1"
|
|
2682
|
+
to="0 100"/>` and `<converter-group name="fmt" chain="pct round label"/>`.
|
|
2683
|
+
The generated `convertCompiler` hook supplies declaration compilation/decompilation independently
|
|
2684
|
+
of events/data authoring. Browser `markup-convert` registers the `convert` slot. JSON declarations,
|
|
2685
|
+
AniML, CLI schema/inspect and `anima docs convert` use the same metadata and module chapter.
|
|
2686
|
+
|
|
2687
|
+
Remaining parity limits: retained model-instance list creation; scripted conversion (P9). Their canonical
|
|
2688
|
+
probes retain precise `todo.json` reasons. Existing scalar/index-list/boolean conversion functions
|
|
2689
|
+
are useful independently but do not complete those broader rows. See ROWS.json and REPORT.md.
|
|
2690
|
+
|
|
2691
|
+
### Intentional differences and observation limits
|
|
2692
|
+
|
|
2693
|
+
These decisions retain the original oracle findings unchanged; none becomes a passing comparison.
|
|
2694
|
+
|
|
2695
|
+
- Boolean → string uses ECMAScript `"true"`/`"false"`, rather than the native numeric spelling
|
|
2696
|
+
(`"1"` for true). See [boolean text finding](../tests/oracle/data-converter-to-string/finding.json).
|
|
2697
|
+
- Interpolation initializes eagerly from the value at load. A later source change interpolates from
|
|
2698
|
+
that value rather than snapping on the first non-default change as native does. See
|
|
2699
|
+
[number interpolation finding](../tests/oracle/data-interpolate-number/finding.json).
|
|
2700
|
+
RGBA channels round half up, so a black/white midpoint is `#808080`, rather than native `#7f7f7f`;
|
|
2701
|
+
see [colour interpolation finding](../tests/oracle/data-interpolate-color/finding.json).
|
|
2702
|
+
- Decimal rounding and degrees → radians retain doubles. Their measured differences are solely
|
|
2703
|
+
native float32 storage precision, not different arithmetic contracts. See
|
|
2704
|
+
[decimal rounder finding](../tests/oracle/data-converter-rounder/finding.json) and
|
|
2705
|
+
[degrees → radians finding](../tests/oracle/data-converter-system-degs-to-rads/finding.json).
|
|
2706
|
+
- Enum → index returns the declared zero-based index. The native dump's unchanged numeric bridge
|
|
2707
|
+
is an observation limit, not evidence of a different enum conversion rule. See
|
|
2708
|
+
[enum observation finding](../tests/oracle/data-converter-enum-to-uint/finding.json).
|
|
2709
|
+
|
|
2710
|
+
Algorithms are independently implemented from standard arithmetic and ECMAScript functions.
|
|
2711
|
+
No Rive source or .riv files were read; no CLI documentation text was copied. The registry design follows
|
|
2712
|
+
the metadata rationale in `docs/research/rive-format-and-graph.md`; this is not a Rive algorithm port.
|
|
2713
|
+
|
|
2714
|
+
|
|
2715
|
+
<!-- capability: data.converter-color-formatting -->
|
|
2716
|
+
### Colour component patterns
|
|
2717
|
+
|
|
2718
|
+
JSON `pattern` / AniML `pattern` overrides `format` on a `color-format` converter.
|
|
2719
|
+
Markers `{r} {g} {b} {a}` are rounded bytes, `{h}` rounded degrees (achromatic 0), `{s}`/`{l}`
|
|
2720
|
+
rounded HSL percentages, `{hex}` lowercase `#rrggbb`. Unknown markers produce `convert-options`
|
|
2721
|
+
at `/converters/<name>/pattern` with a hint. See [convert](../modules/convert/SPEC.md).
|
|
2722
|
+
|
|
2723
|
+
<!-- capability: data.converter-trigger -->
|
|
2724
|
+
### Trigger destinations
|
|
2725
|
+
|
|
2726
|
+
A binding target naming a trigger queues one fire when its converted output changes after the
|
|
2727
|
+
initial baseline; transitions/listeners observe it on the following step. Magnitude/direction
|
|
2728
|
+
is irrelevant. `trigger` truncates numeric input and passes booleans unchanged. Forward-only
|
|
2729
|
+
modes `to-target`/`once` are valid; reverse modes produce `data-binding` at the `/mode` pointer.
|
|
2730
|
+
|
|
2731
|
+
### Export discovery and keyframe bindings
|
|
2732
|
+
|
|
2733
|
+
JSON instance `exported: true` is AniML `export="true"` (legacy `exported` accepted).
|
|
2734
|
+
`dataState(player).exports()` lists only explicitly exported authored `{name,model}` records.
|
|
2735
|
+
Internal private instances remain addressable. Existing host APIs retain their all-instance list.
|
|
2736
|
+
Data-keyframe values use the player-aware sampler; converter evaluation is isolated per player.
|
|
2737
|
+
|
|
2738
|
+
## derive
|
|
2739
|
+
|
|
2740
|
+
The optional `derive` module adds computed, read-only scalar model properties. The normative
|
|
2741
|
+
contract is [modules/derive/SPEC.md](../modules/derive/SPEC.md); its `value` and `convert` keys
|
|
2742
|
+
have identical JSON/AniML spellings. Dependencies resolve per instance and cycles are invalid.
|
|
2743
|
+
|
|
2744
|
+
## lists
|
|
2745
|
+
|
|
2746
|
+
The optional `lists` module adds retained repeats and nested data contexts. The normative
|
|
2747
|
+
JSON/AniML spellings, ordering, bounds, identity, limits and diagnostics are defined in
|
|
2748
|
+
[modules/lists/SPEC.md](../modules/lists/SPEC.md). Both `components` and `data` are required.
|
|
2749
|
+
|
|
2750
|
+
<!-- capability: components.list -->
|
|
2751
|
+
### List instances
|
|
2752
|
+
`type: "repeat"`, `list: "$items"`, `component: "row"` is
|
|
2753
|
+
`<repeat id="rows" list="$items" component="row"/>`. Surviving item identities retain players.
|
|
2754
|
+
|
|
2755
|
+
<!-- capability: components.list-map -->
|
|
2756
|
+
### Model component maps
|
|
2757
|
+
`component: {Player:"row"}` is `component="Player:row"`; missing known mappings are `list-map`.
|
|
2758
|
+
|
|
2759
|
+
<!-- capability: components.list-size-override -->
|
|
2760
|
+
### Item bounds
|
|
2761
|
+
`size: [w,h]` is `size="w h"`; both numbers are nonnegative. It changes instance hit bounds.
|
|
2762
|
+
|
|
2763
|
+
<!-- capability: components.explicit-context -->
|
|
2764
|
+
### Explicit data context
|
|
2765
|
+
`data: "$team"` is `data="$team"` on an instance; the referenced model must match its public model.
|
|
2766
|
+
|
|
2767
|
+
<!-- capability: components.inherited-context -->
|
|
2768
|
+
### Inherited data context
|
|
2769
|
+
A component with no `data` declaration shares the inherited document's live named data instances,
|
|
2770
|
+
recursively, including non-global models. This applies to host writes inside/outside `batch` and to
|
|
2771
|
+
document writes. An explicit child `data` model keeps local storage unless bound; global models
|
|
2772
|
+
retain their sharing contract. Each root player is independent. Descendant steps follow ancestor bindings; a shared data write
|
|
2773
|
+
during a descendant step keeps the ancestor unsettled so its bindings resample on the next step.
|
|
2774
|
+
With lists, a context-free
|
|
2775
|
+
component additionally sees its actual parent's default/item context.
|
|
2776
|
+
|
|
2777
|
+
<!-- capability: data.binding-relative -->
|
|
2778
|
+
### Relative bindings
|
|
2779
|
+
`$parent.name` walks one context; `$parent.parent.name` walks two. Parent reads are read-only.
|
|
2780
|
+
|
|
2781
|
+
<!-- capability: data.property-symbol-list-index -->
|
|
2782
|
+
### Item index
|
|
2783
|
+
`$index` is the current zero-based index and is read-only. Declaring `index` or `parent` is
|
|
2784
|
+
`reserved-name` at `/data/models/<model>/properties/<name>`, with a rename hint.
|
|
2785
|
+
|
|
2786
|
+
<!-- capability: data.converter-number-to-list -->
|
|
2787
|
+
### Count repetition
|
|
2788
|
+
`count: "$n"` is `count="$n"`. It floors nonnegative counts and retains players by index.
|
|
2789
|
+
Counts above 256 are rejected; shrinking releases the removed indices. Any readable numeric data
|
|
2790
|
+
path is accepted, including `$items.length`, named/relative numeric paths and (with `derive`)
|
|
2791
|
+
`$title.length`. Nonnumeric paths report `list-source` at `/nodes/<index>/count` with the accepted
|
|
2792
|
+
source forms. `anima inspect` lists only the repeated input slots in the current initial list/count,
|
|
2793
|
+
including triggers; reserved potential slots for absent rows are omitted.
|
|
2794
|
+
|
|
2795
|
+
## nested
|
|
2796
|
+
|
|
2797
|
+
The optional module's normative contract is [modules/nested/SPEC.md](../modules/nested/SPEC.md).
|
|
2798
|
+
|
|
2799
|
+
<!-- capability: components.nested-remap -->
|
|
2800
|
+
<!-- capability: components.nested-quantize -->
|
|
2801
|
+
<!-- capability: components.leaf-fit -->
|
|
2802
|
+
<!-- capability: components.stateful-output -->
|
|
2803
|
+
Instances gain absolute `time`, positive `fps`, `size`/`fit`/`align`, and read-only scalar `outputs`.
|
|
2804
|
+
The parent reads `badge.outputs.done`; the child declares `<output name="done" value="count > 1"/>`.
|
|
2805
|
+
|
|
2806
|
+
## markup-libraries
|
|
2807
|
+
|
|
2808
|
+
<!-- capability: components.library-export -->
|
|
2809
|
+
<!-- capability: components.library-models -->
|
|
2810
|
+
<!-- capability: components.library-update -->
|
|
2811
|
+
|
|
2812
|
+
Local libraries share model definitions, authored data instances and enums through lexical,
|
|
2813
|
+
collision-free import expansion in both AniML and the resulting JSON data declarations.
|
|
2814
|
+
Named-instance expressions and enum references are rewritten; member keys and authored values
|
|
2815
|
+
are retained. Export selection and private dependencies follow the
|
|
2816
|
+
[library contract](../modules/markup-libraries/SPEC.md). Native library-service comparison is
|
|
2817
|
+
skip-noted because the required account commands are banned; this does not waive local tests.
|
|
2818
|
+
|
|
2819
|
+
Libraries support public export selection, model definitions and local vendoring updates.
|
|
2820
|
+
See [modules/markup-libraries/SPEC.md](../modules/markup-libraries/SPEC.md).
|
|
2821
|
+
|
|
2822
|
+
<!-- capability: state-machine.literal-artboard -->
|
|
2823
|
+
With `data components nested`, direct component-property guards accept `==`/`!=` against
|
|
2824
|
+
`component("row")`, a typed definition-name literal. See [nested](../modules/nested/SPEC.md).
|
|
2825
|
+
Independent guard/keyframe conformance and module-owned native comparisons cover this guard.
|
|
2826
|
+
|
|
2827
|
+
## componentpaint
|
|
2828
|
+
|
|
2829
|
+
<!-- capability: drawing-paint.paint-artboard -->
|
|
2830
|
+
Declare `components paint componentpaint`. JSON `fill: {paint: "component:row"}` is AniML
|
|
2831
|
+
`paint="component:row"`: an independent live component Player supplies a repeating shape-local
|
|
2832
|
+
fill tile, clipped to its viewBox (including its origin). Its own default machine advances once
|
|
2833
|
+
per parent step and honors reduced motion. Flat color/type/ordered-paint conflicts, unknown
|
|
2834
|
+
components and recursive sources are errors. An ordinary stroke remains independent.
|
|
2835
|
+
The normative lifetime, sampling, diagnostic and backend contract is
|
|
2836
|
+
[modules/componentpaint/README.md](../modules/componentpaint/README.md).
|
|
2837
|
+
|
|
2838
|
+
## CLI projects and capture (tooling)
|
|
2839
|
+
|
|
2840
|
+
These operations are authoring tools, not runtime features: they do not alter `Player.step` or
|
|
2841
|
+
require a feature declaration, registry field or conformance vector.
|
|
2842
|
+
|
|
2843
|
+
The CLI discovers `anima.json` from its working directory upward, taking the nearest file.
|
|
2844
|
+
Supported keys are `entry` (default input path), `sources` (array of AniML library paths),
|
|
2845
|
+
`exclude` (source globs), `constants` (named string/number/bool build-time values),
|
|
2846
|
+
`presets` (map of render-option objects), and `out` (default local export directory). Paths are
|
|
2847
|
+
relative to that configuration file. Unknown keys and malformed values fail with
|
|
2848
|
+
`project-configuration`, a JSON pointer and a repair hint. Commands needing an input use `entry`
|
|
2849
|
+
when no positional file is supplied. Explicit files take precedence. Configuration discovery is
|
|
2850
|
+
based on the working directory, not the input file's directory.
|
|
2851
|
+
|
|
2852
|
+
Excluded source paths are matched relative to the project file using Node path glob semantics;
|
|
2853
|
+
excluded libraries are omitted before reading or importing. An explicitly excluded input fails
|
|
2854
|
+
with `project-configuration` at `/exclude`. This supports test-only libraries outside exports.
|
|
2855
|
+
Constants replace `{{name}}` only in AniML attribute values, including imported sources; replacements
|
|
2856
|
+
are XML escaped. Text nodes are untouched. Unknown names fail at `/constants/<name>`. Constants
|
|
2857
|
+
never appear in runtime JSON and are not evaluated as expressions.
|
|
2858
|
+
|
|
2859
|
+
`sources` requires an AniML entry. Each source is an ordinary named `<library>` imported before
|
|
2860
|
+
compilation, with its declared name as namespace. For example a library named `ui` exposes
|
|
2861
|
+
`ui:button`. Existing import resolution, export selection, duplicate-name and cycle diagnostics
|
|
2862
|
+
apply. Sources are not concatenated into a second format. Imported asset origins remain subject
|
|
2863
|
+
to the existing compiler/build asset rules. A project file has no representation in `.ani`;
|
|
2864
|
+
the generated `.ani` schema describes runtime documents only.
|
|
2865
|
+
|
|
2866
|
+
Render presets accept `component`, `fit`, `viewport`, `scale`, `size`, `at`, `dark`, `color`,
|
|
2867
|
+
`current`, `background`, `sheet`, `over`, `frames`, `fps`, and `reduced-motion`. Numeric options may be numbers or
|
|
2868
|
+
strings, boolean options must be booleans, and `color` is an array of `slot=value` strings.
|
|
2869
|
+
`--preset name` supplies defaults; explicit flags override them. Presets cannot redirect files
|
|
2870
|
+
or execute scripts. Repeated `--preset a --preset b` runs a sequential queue in that order,
|
|
2871
|
+
stopping on failure. With multiple presets the output stem gains `-<preset>` to avoid collisions.
|
|
2872
|
+
`frames` (1–10000) renders at `fps` (default 60, finite >0 and ≤240) after script/at setup,
|
|
2873
|
+
stepping 1/fps between frames. PNG output gains zero-based `-0000` numbering. WebM uses CLI-only
|
|
2874
|
+
headless Chromium canvas capture and MediaRecorder; timestamps follow real-time encoder scheduling,
|
|
2875
|
+
so encoded bytes/timing are not a deterministic conformance output. WebM defaults to 60 frames.
|
|
2876
|
+
Unavailable Chromium or WebM support emits `build-encoder-unavailable` with a PNG fallback hint.
|
|
2877
|
+
Sequences reject sheet/compare and require PNG or WebM output.
|
|
2878
|
+
|
|
2879
|
+
`anima schema` accepts conjunctive `--animatable`, `--bindable`, `--inherited` filters.
|
|
2880
|
+
Inherited fields are the shared node properties, bindable fields are animatable addresses;
|
|
2881
|
+
static schema fields remain available in unfiltered lookup. `--editor-only` reports not applicable:
|
|
2882
|
+
Anima has no editor-only entries. `docs authoring` includes a contents list;
|
|
2883
|
+
`docs authoring <section-slug>` extracts a section. `docs spec --list` lists chapter and subsection
|
|
2884
|
+
slugs, and `docs spec <slug>` extracts one. Doctor checks Node, executable browsers, writable cache,
|
|
2885
|
+
module wiring, nearest project validity, the default playground port 4519, and Chromium mobile/touch
|
|
2886
|
+
emulation. Authentication is explicitly not applicable because Anima has no accounts.
|
|
2887
|
+
|
|
2888
|
+
`anima render` selects a component with `--component name`, using the same inherited assets,
|
|
2889
|
+
data, converters and component declarations as the web component root selector. Scripts address
|
|
2890
|
+
the selected root's own inputs and nodes. The selector accepts the same original, qualified and
|
|
2891
|
+
legacy component names as the web host. Ambiguous names are usage errors listing qualified
|
|
2892
|
+
alternatives; unknown names are usage errors.
|
|
2893
|
+
`--viewport WxH` sets positive integer output dimensions (default `--size` square).
|
|
2894
|
+
`--fit contain` (default) centres a uniform fit, `cover` centres and crops, `fill` stretches,
|
|
2895
|
+
and `none` centres the authored view box with one output pixel per view-box unit.
|
|
2896
|
+
An explicit background covers the entire output viewport, including letterbox regions.
|
|
2897
|
+
`--scale k` accepts finite `0 < k ≤ 1`, rounds each output dimension to the nearest integer,
|
|
2898
|
+
with a minimum of one pixel. Fit is computed before downscaling. These options apply to single
|
|
2899
|
+
frames; contact sheets reject viewport, fit and scale overrides.
|
|
2900
|
+
|
|
2901
|
+
`--probe 'x,y;x,y'` reports `{x,y,rgba:[r,g,b,a]}` for each point as one JSON object containing
|
|
2902
|
+
`probes`. Coordinates are zero-based integer **output pixels after viewport and scale**, with
|
|
2903
|
+
origin at the top left; channels are 0–255 including alpha. Out-of-bounds or malformed points
|
|
2904
|
+
are usage errors. Probes can be requested without an output file.
|
|
2905
|
+
|
|
2906
|
+
`--audio out.wav` mixes script audio events at their exact occurrence within each step, with clip start/end and per-event
|
|
2907
|
+
and document volume into stereo, 48 kHz signed 16-bit PCM using Chromium's `OfflineAudioContext`.
|
|
2908
|
+
The output duration is the player's final time, at least one sample; clips are truncated there.
|
|
2909
|
+
Audio sources must be local or base64 data URIs. Assets and document gains resolve in the emitting
|
|
2910
|
+
player’s catalog, including inherited and shadowed component/repeat catalogs. Tooling captures source
|
|
2911
|
+
clock crossing fractions before loop folding; the script session’s `onEvents` callback receives
|
|
2912
|
+
absolute times and the emitting model via symbol metadata. Ordinary event JSON and shipped browser
|
|
2913
|
+
bundles are unchanged. Unresolved assets fail explicitly.
|
|
2914
|
+
The Chromium launch is lazy and always closed, including failures. Missing/unlaunchable Chromium
|
|
2915
|
+
reports `build-encoder-unavailable` with a browser installation hint. It is not silently replaced
|
|
2916
|
+
by a muted WAV.
|
|
2917
|
+
|
|
2918
|
+
`anima compare a.png b.png [--tolerance 0..255] [-o diff.png]` compares decoded RGBA without
|
|
2919
|
+
resizing. Different dimensions fail. JSON reports `maxChannelDifference`,
|
|
2920
|
+
`meanChannelDifference` (absolute differences averaged over all four channels of all pixels),
|
|
2921
|
+
`differingPixels` (any channel differs), `pixelsBeyondTolerance` (any channel exceeds tolerance)
|
|
2922
|
+
and `pass`. The diff is opaque grayscale with the largest channel delta per pixel. Exit 1 means
|
|
2923
|
+
comparison failure; invalid tolerance, argument count or output extension is exit 2.
|
|
2924
|
+
|
|
2925
|
+
`anima schema --search words` searches registered node types, structural topics and fields
|
|
2926
|
+
(including both spellings), and the diagnostic table in this spec. Each query term scores 100
|
|
2927
|
+
for an exact name, 10 for a name substring, or 1 for a description substring. Scores add; zero
|
|
2928
|
+
scores are omitted. Results sort by descending score then ascending name (code-point order).
|
|
2929
|
+
`anima schema type.field` returns the matching registry field by JSON or markup spelling.
|
|
2930
|
+
|
|
2931
|
+
`anima inspect --summary` returns a wiring table (JSON: `{wiring:[…]}`) of unbound inputs,
|
|
2932
|
+
unused component/model/property declarations, listener targets and unreachable states. Each
|
|
2933
|
+
finding has code, pointer, message and hint. This is a conservative static aid: dynamic host writes
|
|
2934
|
+
and imported/dynamically selected components may require human interpretation. Invalid documents
|
|
2935
|
+
still report normal loader diagnostics rather than a partially linked graph.
|
|
2936
|
+
|
|
2937
|
+
`anima doctor` checks Node ≥24, cached Chromium/Firefox executables, cache write permission and,
|
|
2938
|
+
inside a repository containing `tools/modules.ts` and `spec/MODULES.md`, generated module
|
|
2939
|
+
wiring with `node tools/modules.ts --check`. It exits 1 if any check fails and supplies repair hints.
|
|
2940
|
+
It does not install browsers or rewrite wiring.
|
|
2941
|
+
|
|
2942
|
+
`anima new directory --sample bell|moon|progress` creates a fresh project directory containing
|
|
2943
|
+
`main.animl`, supporting local libraries, `anima.json` and agent instructions. An existing directory
|
|
2944
|
+
is never overwritten. Existing `new --glyph`/`--glyphy` single-document scaffolding is unchanged.
|
|
2945
|
+
`anima build [file] --out-dir directory` (or project `out`) exports canonical `.ani`, embedded local
|
|
2946
|
+
assets, `anima.js` and an `index.html` with inline document JSON. It performs no upload or package
|
|
2947
|
+
publication. HTML script text escapes `<` to preserve the raw-text boundary.
|
|
2948
|
+
|
|
2949
|
+
The renderer uses SVG viewports and the existing rasterizer; browser services supply audio.
|
|
2950
|
+
This retains the browser-host split discussed in `docs/research/rive-web-runtime.md` without
|
|
2951
|
+
copying runtime code or adopting a binary/native publishing path.
|
|
2952
|
+
|
|
2953
|
+
### Browser host stop and diagnostics
|
|
2954
|
+
|
|
2955
|
+
<!-- capability: host-api.stop -->
|
|
2956
|
+
`icon.stop()` pauses playback and the shared clock, preserving the current frame.
|
|
2957
|
+
`icon.play()` resumes from that frame without accounting for stopped wall time.
|
|
2958
|
+
This host-only operation adds no JSON/AniML grammar, loader diagnostics or CLI operation.
|
|
2959
|
+
|
|
2960
|
+
<!-- capability: host-api.fps-counter -->
|
|
2961
|
+
<!-- capability: host-api.performance-marks -->
|
|
2962
|
+
<!-- capability: host-api.deprecation-policy -->
|
|
2963
|
+
The optional browser-only `devtools` contract is specified in
|
|
2964
|
+
[modules/devtools/SPEC.md](../modules/devtools/SPEC.md). It adds no document grammar.
|
|
2965
|
+
|
|
2966
|
+
## a11y
|
|
2967
|
+
|
|
2968
|
+
Node `a11y` supports `progressbar`, `meter`, `status`, and a local semantic-node
|
|
2969
|
+
`controls` relation (Anima extensions without inventory rows). Progressbar/meter use
|
|
2970
|
+
numeric bound `value`, min/max (defaults 0/100); omit progressbar value for indeterminate
|
|
2971
|
+
progress. Status defaults to polite live announcements. `controls` maps to `aria-controls`.
|
|
2972
|
+
Dialog `modal` and button/link/group/list-item `expanded` already accept bool literals,
|
|
2973
|
+
inputs or data paths. `anima docs a11y` includes recipes; `anima schema a11y`
|
|
2974
|
+
shows the complete registry vocabulary. These are node fields, separate from root §10 a11y.
|
|
2975
|
+
|
|
2976
|
+
Node semantics are specified in [modules/a11y/SPEC.md](../modules/a11y/SPEC.md). Interaction,
|
|
2977
|
+
diffs and focus policy live in the companion `a11y-actions` (runtime key `a11yactions`); in-browser
|
|
2978
|
+
AniML forms in the `markup-a11y` web entry. The optional `a11y-dom` web entry supplies the platform overlay,
|
|
2979
|
+
including canvas positioning through the optional canvas backend's node-box adapter. Synthesized
|
|
2980
|
+
semantic focus targets use world geometry-box centers; directional candidates exclude ancestors
|
|
2981
|
+
of the current target. Unchanged semantic reads reuse retained dependencies before collecting the
|
|
2982
|
+
scene. Geometry/visibility, binding and owner dependencies govern recollection; paint-only changes and
|
|
2983
|
+
unchanged list steps preserve it. See the module contracts and `spec/conformance/a11y-spatial-focus`. Root §10 is unchanged.
|
|
2984
|
+
|
|
2985
|
+
### Native composite roles and synchronous actions
|
|
2986
|
+
|
|
2987
|
+
Node roles include `toolbar`, `grid`, `row` and `gridcell`. With a11y-actions and
|
|
2988
|
+
focus, gridcells are implicit focus targets; the three structural roles are not.
|
|
2989
|
+
The optional `@anima-format/web/a11y-navigation` entry includes the DOM overlay and
|
|
2990
|
+
adds one tab stop per toolbar/grid. Toolbar arrows on both axes and Home/End use
|
|
2991
|
+
the tab-list roving policy. A grid contains semantic rows of cells: Left/Right stay
|
|
2992
|
+
within a row; Up/Down select the same authored column in the adjacent row (missing
|
|
2993
|
+
or unavailable cells retain focus). Home/End select row endpoints; Ctrl+Home/End
|
|
2994
|
+
select grid endpoints. Row `focus.edge` controls horizontal boundaries: stop,
|
|
2995
|
+
closed-loop within the row, or parent-scope using existing focus traversal.
|
|
2996
|
+
Vertical boundaries stop. Page Up/Down bubble unprevented for the page to handle.
|
|
2997
|
+
See [the native navigation contract](../modules/a11y-dom/SPEC.md) for availability,
|
|
2998
|
+
modifier and ordering rules. Module-owned conformance assertions are in
|
|
2999
|
+
`modules/a11y/test/core/issues.test.ts`, with the paired `navigation` example.
|
|
3000
|
+
|
|
3001
|
+
Accepted host semantic actions synchronously step(0) through the scheduled-step
|
|
3002
|
+
path: all resulting events (including nested component events) and state changes
|
|
3003
|
+
are dispatched as `anima:event` and `anima:state`, just as for `icon.fire()`.
|
|
3004
|
+
A focus-disabled scope retains its semantic role and subtree; enabled descendants
|
|
3005
|
+
remain eligible for native focus and roving.
|
|
3006
|
+
|
|
3007
|
+
<!-- capability: a11y.tree-bounds -->
|
|
3008
|
+
### Semantic bounds
|
|
3009
|
+
|
|
3010
|
+
`nodes[].a11y.bounds` (AniML `bounds`) is a local `[x,y,w,h]` box with nonnegative width and height,
|
|
3011
|
+
transformed to world-grid coordinates. Without it, visible child boxes are unioned
|
|
3012
|
+
for unpainted groups; an empty node reports its transformed origin as a point.
|
|
3013
|
+
The optional painted calculator shared by tooling and the browser overlay bounds trimmed pieces
|
|
3014
|
+
with their caps, viewport-sized non-scaling strokes and feather falloff; it intersects clips with
|
|
3015
|
+
source geometry boxes and masks with source painted boxes. Explicit boxes win. Dash bounds
|
|
3016
|
+
intentionally use the undashed stroke (conservative), and text uses measured logical line boxes
|
|
3017
|
+
rather than ink, matching DOM text rectangles and screen-reader highlights. The full contract,
|
|
3018
|
+
including inverted-mask complements and finite three-sigma feather support, is specified in
|
|
3019
|
+
[modules/a11y/SPEC.md](../modules/a11y/SPEC.md#painted-geometric-bounds-p7-8).
|
|
3020
|
+
Native semantic rectangles omit stroke; the existing P7-8 capture records this intentional native
|
|
3021
|
+
difference in `tests/oracle/p7-8-semantic-bounds/`.
|
|
3022
|
+
|
|
3023
|
+
|
|
3024
|
+
<!-- capability: a11y.property-heading-level -->
|
|
3025
|
+
### Heading hierarchy
|
|
3026
|
+
|
|
3027
|
+
A node with `a11y.role: "heading"` accepts `level` as an integer 1–6 or a
|
|
3028
|
+
number input/data path. AniML uses `<a11y role="heading" level="2"/>`; a bound
|
|
3029
|
+
level uses a JSON string attribute, for example `level=""depth""`.
|
|
3030
|
+
Bound values must remain integers 1–6. Unknown names report `unknown-input`,
|
|
3031
|
+
wrong types `input-type`, and invalid literal levels `bad-value`, at that field's
|
|
3032
|
+
JSON pointer. See [the node semantics contract](../modules/a11y/SPEC.md), its
|
|
3033
|
+
`a11y-property-heading-level` conformance case and paired capability example.
|
|
3034
|
+
|
|
3035
|
+
|
|
3036
|
+
<!-- capability: a11y.focus-condition -->
|
|
3037
|
+
### Focus transition condition
|
|
3038
|
+
|
|
3039
|
+
With `a11y-actions` and `focus`, a machine guard operand `{ "focus": "id" }`
|
|
3040
|
+
is true for the primary target and focusable semantic ancestors. Documents without
|
|
3041
|
+
node semantics retain exact-target focus conditions. The existing machine guard
|
|
3042
|
+
registry and AniML JSON `guard` attribute own both spellings and unknown-target
|
|
3043
|
+
validation. See `spec/conformance/a11y-focus-condition` and
|
|
3044
|
+
[the node semantics contract](../modules/a11y/SPEC.md). Repeated targets are covered by the module tests; six native PNG stages independently verify
|
|
3045
|
+
ancestor conditions, as recorded in the closure report.
|
|
3046
|
+
|
|
3047
|
+
## data-host
|
|
3048
|
+
|
|
3049
|
+
<!-- capability: transforms.computed-transform-values -->
|
|
3050
|
+
<!-- capability: data.host-nested-paths -->
|
|
3051
|
+
<!-- capability: host-api.nested-models -->
|
|
3052
|
+
The optional `data-host` entry adds read-only computed geometry; the host API also resolves
|
|
3053
|
+
nested paths through `at(path)` and discovers authored exports with `exportedInstances()`.
|
|
3054
|
+
Host components, colors and batching use the existing data vocabulary in both syntaxes.
|
|
3055
|
+
Bindings can use validated `asset("name")` literals with the `datahost` feature.
|
|
3056
|
+
The normative contract is [modules/data-host/SPEC.md](../modules/data-host/SPEC.md).
|
|
3057
|
+
|
|
3058
|
+
<!-- capability: input.write-key-pressed -->
|
|
3059
|
+
<!-- capability: input.write-pointer-x -->
|
|
3060
|
+
<!-- capability: input.write-pointer-y -->
|
|
3061
|
+
<!-- capability: input.write-pointer-delta-x -->
|
|
3062
|
+
<!-- capability: input.write-pointer-delta-y -->
|
|
3063
|
+
With `input data`, JSON `write: "$x", value: "pointer-x"` is AniML
|
|
3064
|
+
`write="$x" value="pointer-x"`. A write map also accepts these source names as keys.
|
|
3065
|
+
Coordinates are grid units; deltas are from the preceding event for the same pointer, initially
|
|
3066
|
+
zero. `key-pressed` stores the triggering `KeyboardEvent.key` name (a string), on any selected
|
|
3067
|
+
key phase. Missing event fields leave data unchanged. Wrong/read-only destination types or
|
|
3068
|
+
unknown sources report `input-write` at `/listeners/<i>/write/<source>` with a type hint.
|
|
3069
|
+
Both data properties and ordinary inputs of the matching type are writable. See
|
|
3070
|
+
[input](../modules/input/SPEC.md) and its independent conformance/oracle probes.
|
|
3071
|
+
|
|
3072
|
+
## Tool font processing (P6-2)
|
|
3073
|
+
|
|
3074
|
+
The CLI's optional font tooling adds no browser runtime code. Text measurements use the contract
|
|
3075
|
+
in `design/p6/DESIGN.md` §4: cumulative advances after each `Intl.Segmenter('und')` grapheme,
|
|
3076
|
+
kerning included, in grid units. Tracking is inserted between graphemes. Metrics are in grid units;
|
|
3077
|
+
descent is positive below the baseline. The tool uses OS/2 typographic metrics when USE_TYPO_METRICS
|
|
3078
|
+
is set, otherwise hhea metrics; absent cap/x heights fall back to ascent / half ascent. It never
|
|
3079
|
+
selects a system font for deterministic measurement. Missing registered families are `font-missing`.
|
|
3080
|
+
A family list selects the first registered face; style and nearest weight select among local faces.
|
|
3081
|
+
The weight supplies `wght` unless an explicit axis overrides it. Axis coordinates clamp to fvar bounds;
|
|
3082
|
+
avar maps normalized coordinates and gvar tuples vary outlines and phantom-point advances.
|
|
3083
|
+
|
|
3084
|
+
Tool sources are local TTF/OTF/WOFF/WOFF2 or base64 data URIs. Font collections, CFF2, HVAR metric
|
|
3085
|
+
variations and Type2 arithmetic/seac operators are currently unsupported. CFF1 Type2 contours,
|
|
3086
|
+
subroutines and CID FDSelect 0/3 are supported, along with TrueType simple and composite glyphs.
|
|
3087
|
+
WOFF uses bounded zlib decompression; WOFF2 uses bounded Brotli and inverse glyf/loca/hmtx transforms.
|
|
3088
|
+
The reader maps cmap 4/12, reads hhea/hmtx, OS/2, head, maxp, name, kern and GPOS kern pair adjustments
|
|
3089
|
+
1/2 (including extension lookups). Pair placement and advances are supported; a lookup whose
|
|
3090
|
+
IgnoreBaseGlyphs/IgnoreLigatures/IgnoreMarks flag matches either glyph's GDEF class does not
|
|
3091
|
+
adjust that pair. Every cmap mapping, across all subtables, counts toward the table operation limit
|
|
3092
|
+
(`font-limit`). GPOS device/variation adjustments, mark placement and GSUB
|
|
3093
|
+
shaping are not. Arabic, Hebrew, Indic, combining marks and joining controls issue
|
|
3094
|
+
`text-shaping-approximate`; the tool does not claim shaped glyph equivalence.
|
|
3095
|
+
|
|
3096
|
+
`anima sample`, `validate`, `render` and the conformance runner install the tool measurer when text
|
|
3097
|
+
is installed. `--font family=path` registers or overrides local faces. Font asset names, explicit
|
|
3098
|
+
family names and name-table family names are aliases. The optional text module supplies
|
|
3099
|
+
`setTextMeasurer(TextMeasurer)` (or `setMeasurer`) from `modules/text/core/measure.ts`.
|
|
3100
|
+
|
|
3101
|
+
`render` converts SVG text/tspan output into glyph outline paths before the existing rasterizer.
|
|
3102
|
+
It preserves transforms, clipping and paints; scalar x/y/dx/dy and numeric grid-unit sizes are
|
|
3103
|
+
supported. SVG position lists, nonzero glyph rotation and RTL outline conversion currently give
|
|
3104
|
+
`font-unsupported`. `render --browser-text` explicitly selects cached Chromium for PNG shaping and
|
|
3105
|
+
rasterization; SVG output embeds local faces for browser display. It uses the image encoder's lazy
|
|
3106
|
+
browser and closes it after use. System-font fallback is browser-dependent in this explicit mode.
|
|
3107
|
+
|
|
3108
|
+
Font assets may request `glyphs: "all" | "used" | "custom"` (AniML `glyphs`), plus `chars` for custom
|
|
3109
|
+
coverage; these keys are registered by the fonts module. All preserves every glyph. Used includes
|
|
3110
|
+
literal authored text and string `default`/`value`/`placeholder` fields, including bound defaults.
|
|
3111
|
+
Custom includes each Unicode scalar in `chars`. Subsetting embeds an sfnt TTF/OTF asset even without `--embed`.
|
|
3112
|
+
TrueType subsetting retains glyph IDs, .notdef, composite dependencies and kerning/variation tables,
|
|
3113
|
+
removes unused glyf payloads and replaces cmap coverage. With GSUB present it retains every outline
|
|
3114
|
+
conservatively. CFF1 subsetting retains glyph IDs and subroutines, replaces unused charstrings with
|
|
3115
|
+
empty glyphs, and relocates offset-bearing top/FD/private dictionaries and INDEXes. Unsupported
|
|
3116
|
+
retained Type2 glyph operators diagnose instead of discarding implicit glyph dependencies.
|
|
3117
|
+
|
|
3118
|
+
`anima import-font <source> -o local.ttf|otf [--chars text]` normalizes a container to sfnt and emits
|
|
3119
|
+
font asset metadata. `--google "Family:wght@400;600"` instead explicitly fetches Google Fonts CSS
|
|
3120
|
+
and its font files at build time. Multiple sources use numbered output files. `--chars` requests
|
|
3121
|
+
custom coverage and local subsetting. Network, invalid CSS/font bytes and unsupported forms are
|
|
3122
|
+
errors; imports never require a network request at runtime. Downloads and font decompression are
|
|
3123
|
+
limited to 32 MiB; malformed offsets and bounded recursion produce structured diagnostics rather
|
|
3124
|
+
than uncaught CLI exceptions.
|
|
3125
|
+
|
|
3126
|
+
`anima flatten <file> [--node id] [-o out.ani|animl]` freezes authored/default text into editable path
|
|
3127
|
+
groups, retaining surrounding nodes and common transforms. Remaining references to text-only
|
|
3128
|
+
properties are rejected instead of silently dropped. Unsupported SVG features are rejected by the
|
|
3129
|
+
conversion. The text module must be installed for text documents; layout semantics remain owned by
|
|
3130
|
+
that module. The committed `fixtures/fonts/anima-test.ttf` is original generated test data;
|
|
3131
|
+
`node tools/test-font.ts` reproduces it deterministically. Third-party fonts are not fixtures.
|
|
3132
|
+
|
|
3133
|
+
Tool font diagnostics carry severity, code, JSON pointer, message and hint: `font-invalid`,
|
|
3134
|
+
`font-format`, `font-limit`, `font-unsupported`, `font-source`, `font-missing`, `font-option`,
|
|
3135
|
+
`font-glyphs`, `font-download`, `font-flatten`, `font-contract`, `text-shaping-approximate`.
|
|
3136
|
+
|
|
3137
|
+
The independent implementation follows the [OpenType specification](https://learn.microsoft.com/en-us/typography/opentype/spec/)
|
|
3138
|
+
and [WOFF2 specification §5](https://www.w3.org/TR/WOFF2/#compressed-data-format), not another reader's
|
|
3139
|
+
source code. `docs/research/rive-format-and-graph.md` §1's bounded-reader pitfall informs the
|
|
3140
|
+
allocation limits; `docs/research/glyphy-compat.md` §1's grid-unit convention informs the tool boundary.
|
|
3141
|
+
|
|
3142
|
+
## fonts
|
|
3143
|
+
|
|
3144
|
+
<!-- capability: assets.font -->
|
|
3145
|
+
<!-- capability: assets.host-decode-font -->
|
|
3146
|
+
<!-- capability: assets.replacement-scope-font -->
|
|
3147
|
+
<!-- capability: data.host-font -->
|
|
3148
|
+
<!-- capability: host-api.data-font -->
|
|
3149
|
+
<!-- capability: host-api.font-fallback -->
|
|
3150
|
+
<!-- capability: text.font-fallback -->
|
|
3151
|
+
<!-- capability: text.font-assets -->
|
|
3152
|
+
<!-- capability: text.font-decoding -->
|
|
3153
|
+
<!-- capability: text.variable-axes -->
|
|
3154
|
+
<!-- capability: text.opentype-features -->
|
|
3155
|
+
<!-- capability: text.font-metrics -->
|
|
3156
|
+
|
|
3157
|
+
Optional runtime feature `fonts` uses the assets feature's existing `type: "font"` records.
|
|
3158
|
+
JSON `{assets: {body: {type: "font", source: "body.woff2"}}}` is AniML
|
|
3159
|
+
`<asset name="body" type="font" src="body.woff2"/>`; declare `requires="assets fonts"`.
|
|
3160
|
+
No new asset fields or deterministic player channels are added. Core accepts font resources without
|
|
3161
|
+
a browser; decoding is a web concern. This keeps the optional asset boundary described in
|
|
3162
|
+
`docs/research/rive-web-runtime.md` without adopting its shaping engine or resource format.
|
|
3163
|
+
|
|
3164
|
+
<!-- // seam(P7-8): optional font introspection normative link. -->
|
|
3165
|
+
Decoded metrics, coverage, effective options and derived glyph paths are specified in the
|
|
3166
|
+
[optional fonts-info contract](../modules/fonts/INFO.md).
|
|
3167
|
+
|
|
3168
|
+
### Resources, decoding and readiness
|
|
3169
|
+
|
|
3170
|
+
The `@anima-format/web/fonts` entry loads font assets before `anima:load` and the first render.
|
|
3171
|
+
Fonts and asset I/O preserve each other's preload hooks in either entry order.
|
|
3172
|
+
Sources pass through `icon.resolveAsset`, including its local/any policy and host resolver.
|
|
3173
|
+
URLs and resolver-returned Blobs are read as bytes, decoded using `FontFace.load`, added to
|
|
3174
|
+
`document.fonts`, then awaited with `document.fonts.load`. Unsupported or corrupt fonts and
|
|
3175
|
+
policy failures emit `anima:warning` with a diagnostic array: code `font-load`, severity `warning`,
|
|
3176
|
+
JSON pointer `/assets/<escaped-name>/source`, message and recovery hint. The scene continues with
|
|
3177
|
+
CSS fallback. A failed replacement preserves the previous face. Browser decoding does not change
|
|
3178
|
+
`Player.step` values, timing or events; headless tools do not decode fonts through this module.
|
|
3179
|
+
|
|
3180
|
+
`await icon.fonts.decode(bytesOrURL, descriptors?)` returns a loaded FontFace. URLs obey the asset
|
|
3181
|
+
policy; direct bytes are explicit host input. `await icon.fonts.set(name, faceOrBytesOrURL,
|
|
3182
|
+
descriptors?)` replaces the named family for every consumer in that icon scope. Byte and URL
|
|
3183
|
+
sources receive private family names, preventing cross-icon collisions. The latest replacement
|
|
3184
|
+
wins; cleanup invalidates pending work. Faces stay retained until scope cleanup, except that a
|
|
3185
|
+
completed replacement releases the superseded face once no name in that scope uses its family. Faces already in
|
|
3186
|
+
`document.fonts` before adoption are never deleted by cleanup. Shared adopted faces use reference
|
|
3187
|
+
counts. Child component catalogs use `icon.fonts.forModel(model)` to distinguish same-named assets.
|
|
3188
|
+
Reload/dispose clears owned resources; imported pages without `requires: ["fonts"]` do not decode
|
|
3189
|
+
font assets.
|
|
3190
|
+
|
|
3191
|
+
### CSS, fallback and metrics
|
|
3192
|
+
|
|
3193
|
+
`cssFont(spec)` accepts the structural FontSpec contract and returns CSS properties `font`,
|
|
3194
|
+
`fontFamily`, `fontSize`, `fontWeight`, `fontStyle`, `letterSpacing`, `fontVariationSettings`, and
|
|
3195
|
+
`fontFeatureSettings`. The `font` property is the canvas/FontFaceSet shorthand. `axes` and `features`
|
|
3196
|
+
become quoted OpenType tags followed by numeric values; absent maps become `normal`.
|
|
3197
|
+
For live asset aliases and host fallback, browser consumers MUST use
|
|
3198
|
+
`icon.fonts.cssFont(spec)` (or `icon.fonts.forModel(model).cssFont(spec)` for children). Apply the
|
|
3199
|
+
individual CSS properties to SVG text; canvas consumers apply `font` and the supported individual
|
|
3200
|
+
properties. The text module owns field validation, layout, animation, measurement and backend
|
|
3201
|
+
application. A controller does not synthesize axis or feature introspection from font bytes.
|
|
3202
|
+
|
|
3203
|
+
`await icon.fonts.ready(spec, text?)` awaits the used CSS font selection before measuring the actual
|
|
3204
|
+
run text. `await icon.fonts.add(face, {weight}?)` adds a host fallback face and optional weight;
|
|
3205
|
+
faces sharing a family remain eligible for browser weight selection. Families are appended in
|
|
3206
|
+
insertion order after the authored CSS family list. Host fallback configuration survives source
|
|
3207
|
+
reloads (including configuration before the first load); `icon.fonts.clear()` forgets it. Cleanup
|
|
3208
|
+
releases owned document font registrations; the next CSS/readiness use restores configured faces.
|
|
3209
|
+
With the `fonts-text` consumer, a disconnect likewise parks named asset faces and aliases (including
|
|
3210
|
+
child scopes); the same player's next `forModel`/CSS use restores them, a reload discards them.
|
|
3211
|
+
`icon.fonts.revision` increases on font changes and cleanup. Text measurement/layout caches must
|
|
3212
|
+
invalidate on that revision even when a replacement preserves its CSS family name. Glyph coverage and fallback selection are
|
|
3213
|
+
browser behavior. Metrics come from the text module's browser measurer after readiness; no guessed
|
|
3214
|
+
metrics, coverage table, cap-height/x-height parser or outline extractor is supplied by `fonts`.
|
|
3215
|
+
|
|
3216
|
+
### Data assignment and replacement scope
|
|
3217
|
+
|
|
3218
|
+
With the optional data and host modules, `await icon.data.font(path).set(face)` accepts a decoded
|
|
3219
|
+
FontFace. It allocates a player-local font resource id and queues the typed asset-reference write
|
|
3220
|
+
through the existing host API. Other data properties and other icons keep their previous resources.
|
|
3221
|
+
String assignment retains the existing typed font-asset validation. The public handle value remains
|
|
3222
|
+
the selected resource id. Text styles must resolve that id through the owning controller. Named
|
|
3223
|
+
resource replacement changes all uses in its resource scope; data-property assignment changes only
|
|
3224
|
+
consumers of the assigned property. Bound text rendering and complete font-metric parity require
|
|
3225
|
+
the text module; the fonts module alone does not add text nodes.
|
|
3226
|
+
|
|
3227
|
+
## text
|
|
3228
|
+
|
|
3229
|
+
The optional `text` module adds styled, data-bound text nodes, measured layout, SVG/canvas
|
|
3230
|
+
drawing and named host run access. Its normative fields, defaults, validation, measurement
|
|
3231
|
+
contract, fit/overflow algorithms and verification limits are specified in
|
|
3232
|
+
[modules/text/SPEC.md](../modules/text/SPEC.md). Registry entries derive JSON Schema, CLI schema
|
|
3233
|
+
and AniML spellings from the same vocabulary. P6 parity remains planned pending native evidence.
|
|
3234
|
+
|
|
3235
|
+
<!-- capability: text.flatten -->
|
|
3236
|
+
### Text outline conversion
|
|
3237
|
+
|
|
3238
|
+
<!-- // seam(P7-6): normative CLI behavior belongs to the owned text specification. -->
|
|
3239
|
+
`anima flatten` traverses root and component-definition text, preserves clips and common group
|
|
3240
|
+
properties, and rejects remaining text-only references. See [the normative text contract](../modules/text/SPEC.md#text-outline-conversion-p7-6).
|
|
3241
|
+
|
|
3242
|
+
## text-effects
|
|
3243
|
+
|
|
3244
|
+
<!-- capability: text.background -->
|
|
3245
|
+
<!-- capability: text.modifier-follow-path -->
|
|
3246
|
+
<!-- capability: text.modifier-opacity -->
|
|
3247
|
+
<!-- capability: text.modifier-origin -->
|
|
3248
|
+
<!-- capability: text.modifier-rotation -->
|
|
3249
|
+
<!-- capability: text.modifier-scale -->
|
|
3250
|
+
<!-- capability: text.modifier-translation -->
|
|
3251
|
+
<!-- capability: text.modifier-variation -->
|
|
3252
|
+
<!-- capability: text.range-combine -->
|
|
3253
|
+
<!-- capability: text.range-easing -->
|
|
3254
|
+
<!-- capability: text.range-falloff -->
|
|
3255
|
+
<!-- capability: text.range-run -->
|
|
3256
|
+
<!-- capability: text.range-units -->
|
|
3257
|
+
<!-- capability: text.script-direction -->
|
|
3258
|
+
|
|
3259
|
+
The optional `texteffects` feature (`web/text-effects`) adds logical per-cluster translation,
|
|
3260
|
+
rotation, scaling, opacity, origins and font-axis effects; scoped glyph/word/line ranges with
|
|
3261
|
+
falloff, easing and combination; path positions/tangents; and run backgrounds. The text node's
|
|
3262
|
+
JSON `effects` array is AniML `text-effects` or structural `<text-effect>` children, with nested
|
|
3263
|
+
`<text-range>` children. `direction` maps whole runs to native SVG/canvas bidi.
|
|
3264
|
+
|
|
3265
|
+
[modules/text-effects/SPEC.md](../modules/text-effects/SPEC.md) defines defaults, precise range
|
|
3266
|
+
weights and matrix composition, animation addresses, validation, backend limits and evidence.
|
|
3267
|
+
The registry drives schema, CLI and both syntax spellings. Thirteen raw native captures are retained; accepted
|
|
3268
|
+
comparisons remain unresolved and these rows remain planned. Bidi's measured addition is 150 B min+gzip (owner guard: <=150 B).
|
|
3269
|
+
|
|
3270
|
+
<!-- capability: text.script-hit-testing -->
|
|
3271
|
+
<!-- capability: a11y.inferred-text -->
|
|
3272
|
+
### Text hit testing and inferred semantics (P6)
|
|
3273
|
+
|
|
3274
|
+
The optional `text-hit` host adapter exposes grapheme hit positions and caret/selection boxes
|
|
3275
|
+
from the shared text layout. `input` listeners may write `text-index` on a text target. The
|
|
3276
|
+
optional `a11y-text` browser adapter supplies visible text names and layout bounds to the semantic
|
|
3277
|
+
tree; headless tools install this adapter with a11y. Existing JSON/AniML text, label, bounds and
|
|
3278
|
+
listener fields are unchanged. See [the normative text semantics contract](../modules/a11y/TEXT.md).
|
|
3279
|
+
|
|
3280
|
+
## textinput
|
|
3281
|
+
|
|
3282
|
+
The heavy DOM-backed field feature is specified in [text input](../modules/text-input/SPEC.md).
|
|
3283
|
+
Its browser entry is `text-input`; its registry feature is `textinput` (module feature names cannot contain hyphens).
|
|
3284
|
+
Fields extend fixed text nodes. Registry descriptors define both spellings; no base compiler branch is added.
|
|
3285
|
+
|
|
3286
|
+
|
|
3287
|
+
<!-- capability: text.input-text -->
|
|
3288
|
+
### Independent field text drawable
|
|
3289
|
+
|
|
3290
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3291
|
+
|
|
3292
|
+
<!-- capability: text.input-editable -->
|
|
3293
|
+
### Editable text fields
|
|
3294
|
+
|
|
3295
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3296
|
+
|
|
3297
|
+
<!-- capability: text.input-cursor -->
|
|
3298
|
+
### Styled input caret
|
|
3299
|
+
|
|
3300
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3301
|
+
|
|
3302
|
+
<!-- capability: text.input-selection -->
|
|
3303
|
+
### Styled input selection
|
|
3304
|
+
|
|
3305
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3306
|
+
|
|
3307
|
+
<!-- capability: text.input-selection-rounding -->
|
|
3308
|
+
### Rounded selection geometry
|
|
3309
|
+
|
|
3310
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3311
|
+
|
|
3312
|
+
<!-- capability: text.input-focus-selection -->
|
|
3313
|
+
### Select all on focus
|
|
3314
|
+
|
|
3315
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3316
|
+
|
|
3317
|
+
<!-- capability: text.input-ime -->
|
|
3318
|
+
### IME and dead-key text entry
|
|
3319
|
+
|
|
3320
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3321
|
+
|
|
3322
|
+
<!-- capability: text.input-clipboard -->
|
|
3323
|
+
### Text clipboard integration
|
|
3324
|
+
|
|
3325
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3326
|
+
|
|
3327
|
+
<!-- capability: text.input-limits -->
|
|
3328
|
+
### Text input limits
|
|
3329
|
+
|
|
3330
|
+
Text fields accept the bindable bool address `<text>.textInput.obscured`; its rest
|
|
3331
|
+
value is authored `obscured` or false. Resolved masking, semantic redaction and the
|
|
3332
|
+
native input type change together, preserving selection and caret. Static
|
|
3333
|
+
multiline+obscured remains a configuration error; bound obscuring is ineffective
|
|
3334
|
+
on multiline fields.
|
|
3335
|
+
|
|
3336
|
+
`caretBlink` (`caret-blink`, default true) alternates 530 ms visible / 530 ms
|
|
3337
|
+
hidden only for a focused browser field, restarting visible on edits and caret
|
|
3338
|
+
moves. Reduced motion, paused/static and CLI rendering keep it steady. Blink
|
|
3339
|
+
repaints do not advance Player time or keep an unfocused scene awake.
|
|
3340
|
+
|
|
3341
|
+
`selectionTextColor` (`selection-text-color`) colors selected glyphs over the
|
|
3342
|
+
`selectionColor` highlight; omission preserves foreground. The older
|
|
3343
|
+
`selectedTextColor` remains an alias; the new spelling wins if both are present.
|
|
3344
|
+
|
|
3345
|
+
The optional `text-input-extra` browser entry supplies blinking and scroll to
|
|
3346
|
+
caret: horizontal for single-line fields (which never wrap), vertical for
|
|
3347
|
+
multiline fields. Glyphs, highlight and caret clip to the fixed field box; the
|
|
3348
|
+
viewport translation does not reflow layout. Tool/CLI rendering includes the
|
|
3349
|
+
pure viewport projection and a steady caret.
|
|
3350
|
+
|
|
3351
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3352
|
+
|
|
3353
|
+
<!-- capability: text.input-multiline -->
|
|
3354
|
+
### Multiline text input
|
|
3355
|
+
|
|
3356
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3357
|
+
|
|
3358
|
+
<!-- capability: text.input-obscured -->
|
|
3359
|
+
### Obscured text input
|
|
3360
|
+
|
|
3361
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3362
|
+
|
|
3363
|
+
<!-- capability: text.input-placeholder -->
|
|
3364
|
+
### Text input placeholder
|
|
3365
|
+
|
|
3366
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3367
|
+
|
|
3368
|
+
<!-- capability: host-api.text-input -->
|
|
3369
|
+
### Text and IME bridge
|
|
3370
|
+
|
|
3371
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3372
|
+
|
|
3373
|
+
<!-- capability: host-api.clipboard-text -->
|
|
3374
|
+
### Text selection for clipboard
|
|
3375
|
+
|
|
3376
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3377
|
+
|
|
3378
|
+
<!-- capability: input.text-listener -->
|
|
3379
|
+
### Text input listeners
|
|
3380
|
+
|
|
3381
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3382
|
+
|
|
3383
|
+
<!-- capability: input.write-text -->
|
|
3384
|
+
### Store text
|
|
3385
|
+
|
|
3386
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3387
|
+
|
|
3388
|
+
<!-- capability: a11y.role-text-field -->
|
|
3389
|
+
### Semantic role: textField
|
|
3390
|
+
|
|
3391
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3392
|
+
|
|
3393
|
+
<!-- capability: a11y.state-multiline -->
|
|
3394
|
+
### Semantic state: Multiline
|
|
3395
|
+
|
|
3396
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3397
|
+
|
|
3398
|
+
<!-- capability: a11y.state-obscured -->
|
|
3399
|
+
### Semantic state: Obscured
|
|
3400
|
+
|
|
3401
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3402
|
+
|
|
3403
|
+
<!-- capability: a11y.text-proxy -->
|
|
3404
|
+
### Accessible text proxy
|
|
3405
|
+
|
|
3406
|
+
See [the normative field contract](../modules/text-input/SPEC.md) for both spellings, selection, commits, native semantics, diagnostics and tooling. Tests live in `modules/text-input/test/`; native-reference closure remains planned.
|
|
3407
|
+
|
|
3408
|
+
|
|
3409
|
+
## Host fit and artboard size (`host-fit`)
|
|
3410
|
+
|
|
3411
|
+
`@anima-format/web/host-fit` is an optional **host-only** entry. It adds no document
|
|
3412
|
+
keys, `requires` name, registry entries, AniML attributes or CLI render options. An
|
|
3413
|
+
AniML scene and its compiled `.ani` have identical host-fit behaviour; put host
|
|
3414
|
+
attributes on `<anima-icon>`, not on `<anima>`. Existing static CLI fit options are
|
|
3415
|
+
independent. This separation follows the host/scene distinction in
|
|
3416
|
+
`docs/research/rive-web-runtime.md` (input mapping and accessibility overlay).
|
|
3417
|
+
|
|
3418
|
+
The HTML `fit` and `alignment` attributes have reflecting string properties with the
|
|
3419
|
+
same names. `fit` accepts `contain`, `cover`, `fill`, `fit-width`, `fit-height`,
|
|
3420
|
+
`none`, `scale-down`, `layout`; absent/unknown means contain. `alignment` accepts
|
|
3421
|
+
`top-left`, `top-center`, `top-right`, `center-left`, `center`, `center-right`,
|
|
3422
|
+
`bottom-left`, `bottom-center`, `bottom-right`. Legacy `align` remains an alias;
|
|
3423
|
+
`alignment` takes precedence when both exist. `top`, `left`, `right`, `bottom`
|
|
3424
|
+
are accepted aliases for the middle edge positions. Unknown alignment means center.
|
|
3425
|
+
The properties return the attribute spelling, or `contain`/`center` when absent.
|
|
3426
|
+
|
|
3427
|
+
With no fit, alignment or size request, loading the entry preserves the existing SVG
|
|
3428
|
+
grid/bleed viewport and default host aspect ratio (B8). An explicit host-fit request
|
|
3429
|
+
fits the whole artboard rectangle into the CSS content box, including any nonzero
|
|
3430
|
+
viewBox origin. It clips drawing to that viewport in both backends. For a source
|
|
3431
|
+
rectangle `(x,y,w,h)` and host `W×H` CSS pixels, scale is:
|
|
3432
|
+
|
|
3433
|
+
| fit | scale |
|
|
3434
|
+
| --- | --- |
|
|
3435
|
+
| contain (including unknown) | `s = min(W/w,H/h)` |
|
|
3436
|
+
| cover | `s = max(W/w,H/h)` |
|
|
3437
|
+
| fill | `sx = W/w`, `sy = H/h` |
|
|
3438
|
+
| fit-width | `s = W/w` |
|
|
3439
|
+
| fit-height | `s = H/h` |
|
|
3440
|
+
| none | `s = 1` CSS pixel per grid unit |
|
|
3441
|
+
| scale-down | `s = min(1,W/w,H/h)` |
|
|
3442
|
+
| layout | `s = 1`; artboard size becomes `W×H` grid units |
|
|
3443
|
+
|
|
3444
|
+
Uniform fits use `sx=sy=s`. For alignment fractions `ax,ay ∈ {0,0.5,1}`, the source
|
|
3445
|
+
to host matrix is `[sx,0,0,sy,(W-w*sx)*ax-x*sx,(H-h*sy)*ay-y*sy]`. Negative free
|
|
3446
|
+
space aligns cropping by the same rule. SVG expresses this matrix with an equivalent
|
|
3447
|
+
virtual viewBox and `preserveAspectRatio="none"`; canvas uses the identical virtual
|
|
3448
|
+
viewBox before DPR scaling. Neither requires a transformed scene group. Pointer
|
|
3449
|
+
coordinates invert that matrix; semantic bounds use it forwards. Resize, fit,
|
|
3450
|
+
alignment, size overrides and renderer switches update these consumers together.
|
|
3451
|
+
Zero-sized hosts defer fitting until they become positive-sized. Disconnect and
|
|
3452
|
+
destroy stop resize observation; reconnect resumes it.
|
|
3453
|
+
|
|
3454
|
+
`icon.size` reads a detached `[width,height]` array of grid units (without a loaded
|
|
3455
|
+
document or explicit override, `[0,0]`). Assign a pair of finite positive numbers to override the
|
|
3456
|
+
artboard dimensions for this host, retaining its authored origin. The scene and shared compiled model
|
|
3457
|
+
are never mutated; another host is unaffected. Invalid writes preserve the previous
|
|
3458
|
+
size and dispatch `anima:error` with diagnostic `host-size`, pointer `""` (host API),
|
|
3459
|
+
message and hint. `resetSize()` removes the override. The override persists across
|
|
3460
|
+
loads on the same host. In layout fit, the host dimensions take precedence; resetting
|
|
3461
|
+
size clears the explicit override but keeps responsive sizing while layout is active.
|
|
3462
|
+
|
|
3463
|
+
<!-- seam(P7-4): real per-player root layout integration. -->
|
|
3464
|
+
|
|
3465
|
+
**Layout integration:** fitting writes `player.layoutSize?: [w,h]` per
|
|
3466
|
+
player, where undefined means authored root width/height. A layout provider consumes
|
|
3467
|
+
this override for root containers without mutating model defaults and exposes
|
|
3468
|
+
`player.layoutBox(id)`. On a changed override, the host calls `player.step(0)` when
|
|
3469
|
+
that reader exists, then redraws. The scale for layout fit is one CSS pixel per grid
|
|
3470
|
+
unit; DPR changes only the canvas backing store. Leaving layout restores the explicit
|
|
3471
|
+
size if set, otherwise authored root dimensions. The layout module now consumes the override for
|
|
3472
|
+
root containers through their normal bounds. Both engines compare real responsive child drawing
|
|
3473
|
+
against native layout-fit captures at two host sizes, in SVG and canvas.
|
|
3474
|
+
|
|
3475
|
+
## dataartboard — bindable component data
|
|
3476
|
+
|
|
3477
|
+
<!-- capability: data.host-artboard -->
|
|
3478
|
+
<!-- capability: host-api.bindable-artboards -->
|
|
3479
|
+
<!-- capability: host-api.data-artboard -->
|
|
3480
|
+
<!-- capability: components.host-artboard-reference -->
|
|
3481
|
+
|
|
3482
|
+
The normative contract is [modules/data-artboard/SPEC.md](../modules/data-artboard/SPEC.md):
|
|
3483
|
+
`artboard` model properties, `instance component="$content"`, queued host handles and
|
|
3484
|
+
values referencing components from another loaded document. Re-instantiation follows
|
|
3485
|
+
the existing nested-player and data-context rules; a changed selection restarts the
|
|
3486
|
+
child clock, and the same selection retains it. The four deterministic probes are
|
|
3487
|
+
`data-host-artboard`, `host-api-bindable-artboards`, `host-api-data-artboard`, and
|
|
3488
|
+
`components-host-artboard-reference`. Host comparison limits are evidenced in
|
|
3489
|
+
[the native comparison note](../modules/data-artboard/NATIVE.md).
|
|
3490
|
+
|
|
3491
|
+
## layout
|
|
3492
|
+
|
|
3493
|
+
`requires: ["layout"]` adds single-line row/column containers, child sizing and a deterministic
|
|
3494
|
+
layout stage. [The normative layout contract](../modules/layout/SPEC.md) defines both spellings,
|
|
3495
|
+
the CSS-based algorithm, units, caching, geometry, guard timing, diagnostics and intentional native
|
|
3496
|
+
differences. Animated reflow belongs to the separate `layout-reflow` entry.
|
|
3497
|
+
|
|
3498
|
+
<!-- capability: layout.containers -->
|
|
3499
|
+
|
|
3500
|
+
<!-- capability: layout.flex-direction -->
|
|
3501
|
+
|
|
3502
|
+
<!-- capability: layout.flex-justify -->
|
|
3503
|
+
|
|
3504
|
+
<!-- capability: layout.flex-align-items -->
|
|
3505
|
+
|
|
3506
|
+
<!-- capability: layout.self-alignment -->
|
|
3507
|
+
|
|
3508
|
+
<!-- capability: layout.alignment -->
|
|
3509
|
+
|
|
3510
|
+
<!-- capability: layout.spacing -->
|
|
3511
|
+
|
|
3512
|
+
<!-- capability: layout.fixed-sizing -->
|
|
3513
|
+
|
|
3514
|
+
<!-- capability: layout.hug-sizing -->
|
|
3515
|
+
|
|
3516
|
+
<!-- capability: layout.fill-sizing -->
|
|
3517
|
+
|
|
3518
|
+
<!-- capability: layout.size-bounds -->
|
|
3519
|
+
|
|
3520
|
+
<!-- capability: layout.visibility -->
|
|
3521
|
+
|
|
3522
|
+
<!-- capability: layout.participants -->
|
|
3523
|
+
|
|
3524
|
+
<!-- capability: layout.intrinsic -->
|
|
3525
|
+
|
|
3526
|
+
<!-- capability: layout.paint -->
|
|
3527
|
+
|
|
3528
|
+
<!-- capability: data.bind-layout -->
|
|
3529
|
+
|
|
3530
|
+
|
|
3531
|
+
<!-- seam(P7-4): optional animated-reflow contract and host-fit closure. -->
|
|
3532
|
+
<!-- capability: layout.animated-reflow -->
|
|
3533
|
+
### Animated layout reflow
|
|
3534
|
+
|
|
3535
|
+
The normative [layout reflow contract](../modules/layout/SPEC.md#animated-reflow-layout-reflow)
|
|
3536
|
+
defines container/participant `reflow`, §5 easing, inherited settings, uninterrupted retargets,
|
|
3537
|
+
reduced motion and the target/drawn box readers. The optional `layout-reflow` web entry owns
|
|
3538
|
+
interpolation and diagnostics; CLI tooling includes it. Deterministic target/drawn observations
|
|
3539
|
+
are in `layout-animated-reflow`, with allocated text measurement in `layout-text-wrap`.
|
|
3540
|
+
Native timing and retarget mapping remain pending; raw captures are retained without a new tolerance.
|
|
3541
|
+
|
|
3542
|
+
<!-- capability: host-api.fit-layout -->
|
|
3543
|
+
### Responsive host layout
|
|
3544
|
+
|
|
3545
|
+
`<anima-icon fit="layout">` / `icon.fit = "layout"` uses the host's CSS content size as root layout
|
|
3546
|
+
dimensions, at one CSS pixel per grid unit (DPR affects backing pixels only). Root layout reflows;
|
|
3547
|
+
it is not merely scaled. `player.layoutSize` is the per-player grid-unit override; clearing it
|
|
3548
|
+
restores authored sizing. The host adapter applies normal explicit-size/reset precedence as above.
|
|
3549
|
+
A host whose computed content size is not a resolved pixel length (for example `display:none` with a
|
|
3550
|
+
percentage size) is not measured; its layout size and viewport are kept until it has a layout box.
|
|
3551
|
+
Browser conformance and byte-exact native references at two host sizes live in the host-fit tests.
|
|
3552
|
+
This host-only control adds no scene registry keys, AniML attributes or static CLI rendering flag.
|
|
3553
|
+
|
|
3554
|
+
## mesh
|
|
3555
|
+
|
|
3556
|
+
<!-- capability: rigging.mesh -->
|
|
3557
|
+
|
|
3558
|
+
`requires: ["mesh", "assets", "paint"]` adds `mesh` to `image` nodes only. Other hosts
|
|
3559
|
+
are `rig-mesh-host`, at the node's `/mesh` pointer. Meshes are opt-in heavy modules.
|
|
3560
|
+
|
|
3561
|
+
JSON `mesh` is either `{ "grid": [cols, rows] }` or
|
|
3562
|
+
`{ "vertices": [[x, y, u, v], ...], "triangles": [[a, b, c], ...] }`.
|
|
3563
|
+
The AniML spelling is the registry-generic JSON-valued `mesh` attribute, for example
|
|
3564
|
+
`<image ... mesh='{"grid":[4,2]}'/>`; no additional compiler entry is required.
|
|
3565
|
+
Choose exactly one form (`bad-value`). Grid counts are integer cell counts, each 1..128.
|
|
3566
|
+
Vertices are row-major, from top left, including both boundary rows/columns.
|
|
3567
|
+
Cell `(x,y)` has indices `a=y*(cols+1)+x`, `b=a+cols+1`, and triangles
|
|
3568
|
+
`[a,a+1,b+1]`, `[a,b+1,b]`. A 4×2 grid has 15 vertices and 16 triangles.
|
|
3569
|
+
|
|
3570
|
+
Grid vertices span the image box `x0,y0,width,height`. Their UVs invert the image's
|
|
3571
|
+
fit/align rectangle (the Image nodes contract), so a rest mesh reproduces a plain
|
|
3572
|
+
image, including transparent letterboxing. Automatically generated UVs can extend
|
|
3573
|
+
outside 0..1; the finite texture rectangle supplies transparent space there.
|
|
3574
|
+
Untracked grid vertices follow animated width/height; a vertex track or binding
|
|
3575
|
+
replaces its position in local grid units. Explicit meshes ignore fit and align.
|
|
3576
|
+
They need at least three `[x,y,u,v]` vertices; explicit u/v must lie in 0..1
|
|
3577
|
+
(`bad-value`). The mesh needs at least one triangle. Triangle indices must be
|
|
3578
|
+
integers in `[0,vertexCount)` (`rig-mesh-index`). Degenerate UV triangles draw
|
|
3579
|
+
nothing; collapsed destination triangles have zero area.
|
|
3580
|
+
|
|
3581
|
+
`drawFrame` publishes `DrawNode.mesh = {points, uv, triangles}`: points and UVs
|
|
3582
|
+
are flat coordinate arrays, triangles are index triples. Geometry also retains
|
|
3583
|
+
the ordinary image box and asset. If installed, `player.skinPoints(node, points)`
|
|
3584
|
+
deforms the resolved animated positions in place before publication (P8-4 seam).
|
|
3585
|
+
|
|
3586
|
+
The optional `@anima-format/web/canvas-mesh` entry draws triangles by affine
|
|
3587
|
+
`drawImage`, with their clip edges expanded outwards by 0.5 backing-store pixel.
|
|
3588
|
+
It owns a per-renderer image cache and extends the existing renderer readiness promise;
|
|
3589
|
+
image decode requests a redraw without stepping time. Canvas itself adds no mesh code.
|
|
3590
|
+
The retained browser SVG keeps the undeformed image and emits one warning per
|
|
3591
|
+
node per player: `unsupported-render`, feature `mesh`, pointer `/nodes/.../mesh`,
|
|
3592
|
+
with a hint to use `renderer="canvas"` and import `canvas-mesh`.
|
|
3593
|
+
|
|
3594
|
+
The tooling-only `toSVG` export from `@anima-format/core/mesh-static` accepts
|
|
3595
|
+
`meshTriangles: true`: each triangle becomes a clipPath plus an affine image.
|
|
3596
|
+
CLI `render` always enables this option. The default tooling serializer rejects mesh output with `unsupported-render`;
|
|
3597
|
+
the retained browser SVG keeps its undeformed fallback. No triangle serializer
|
|
3598
|
+
enters web bundles.
|
|
3599
|
+
|
|
3600
|
+
<!-- capability: rigging.mesh-animation -->
|
|
3601
|
+
|
|
3602
|
+
Each vertex has a tuple address `<image>.mesh.vertex.<k>` = `[x,y]`, with canonical
|
|
3603
|
+
zero-based indices. Tracks accept finite two-number arrays in JSON. AniML track
|
|
3604
|
+
text uses quoted JSON point literals, e.g. `0 "[10,5]"; 0.5 "[9.5,3.25]"`;
|
|
3605
|
+
the mesh value compiler decodes these to tuples. Invalid points are `value-type`.
|
|
3606
|
+
Sampling and state crossfades interpolate both coordinates linearly with the
|
|
3607
|
+
track/blend ease, preserving signed and fractional coordinates (no colour clamp
|
|
3608
|
+
or rounding). Public values and script snapshots expose arrays.
|
|
3609
|
+
|
|
3610
|
+
With `bind`, `[x-expression, y-expression]` binds a vertex from two scalar numeric
|
|
3611
|
+
expressions; nested scalar calls are supported, e.g. `[x, max(5,y)]`. Invalid tuple
|
|
3612
|
+
syntax is `bind-type`, with the binding's JSON pointer and a correction hint.
|
|
3613
|
+
Binding is post-state, following the existing bind stage; it overrides track output.
|
|
3614
|
+
Without mesh in requires, no mesh per-frame work is installed.
|
|
3615
|
+
|
|
3616
|
+
Pointer hit testing and semantic bounds of a meshed image use its image box, not the deformed
|
|
3617
|
+
triangles, whether the vertices are animated, bound or skinned.
|
|
3618
|
+
|
|
3619
|
+
Independent draw expectations cover grid and explicit meshes, fit/align UVs and
|
|
3620
|
+
fractional animated/bound vertices. Native captures use the original generated
|
|
3621
|
+
flag texture at 12 px/unit; browser pixels compare to the CLI triangle render.
|
|
3622
|
+
Probe locations: `spec/conformance/rigging-mesh{,-animation}` and
|
|
3623
|
+
`tests/oracle/rigging-mesh{,-animation}`. Oracle budgets remain pending.
|
|
3624
|
+
|
|
3625
|
+
Decisions follow `design/p8/DECISIONS.md` D21–D24 and D35, the Canvas2D mesh gap
|
|
3626
|
+
in `docs/research/rive-format-and-graph.md` §4.3, and backend separation/lifecycle
|
|
3627
|
+
notes in `docs/research/rive-web-runtime.md`. No native source was used.
|
|
3628
|
+
|
|
3629
|
+
## rig
|
|
3630
|
+
|
|
3631
|
+
`requires: ["rig"]` adds bones and ordered transform constraints. The browser imports
|
|
3632
|
+
`@anima-format/web/rig`; distance constraints also import the optional
|
|
3633
|
+
`@anima-format/web/rig-distance`. Both entries are heavy and dependency-free.
|
|
3634
|
+
AniML uses `<bone>` and constraint child elements; JSON uses `type: "bone"` and an
|
|
3635
|
+
ordered `constraints: [{kind, ...}]` array. Generic JSON-valued constraint attributes
|
|
3636
|
+
are deliberately absent; structural children preserve authored order.
|
|
3637
|
+
|
|
3638
|
+
### Bone frames and rigid art
|
|
3639
|
+
|
|
3640
|
+
A bone draws nothing and accepts `children`, `length` (nonnegative, default 0),
|
|
3641
|
+
`root` (static boolean, default false), and the common transform/visibility fields.
|
|
3642
|
+
Length, rotation, uniform/axis scales, opacity and visibility animate normally.
|
|
3643
|
+
Its pivot is always its start: authoring `pivot` is `rig-bone-pivot`.
|
|
3644
|
+
|
|
3645
|
+
Outside another bone a bone starts at `(x,y)` and is a root. Inside a bone it
|
|
3646
|
+
starts at its parent's current tip, with rotation relative to that parent's
|
|
3647
|
+
frame; `x` or `y`, even 0, is `rig-bone-offset`. A nested `root: true` instead
|
|
3648
|
+
starts at `(x,y)` in its parent's moving grid coordinates, anchoring a new chain.
|
|
3649
|
+
Per D38, a bone may be at document/component root level, or have a `group` or
|
|
3650
|
+
`bone` parent. Every other parent, including `layout`, `repeat`, `instance` and
|
|
3651
|
+
`text`, is `rig-bone-parent` at the bone's pointer, with hint
|
|
3652
|
+
"wrap the bones in a <group>". A group within layout can host a skeleton. A node that takes no
|
|
3653
|
+
children at all (a shape such as `circle` or `rect`) fails structurally first, with
|
|
3654
|
+
`unknown-key` at its `children`; the rig module adds nothing to core validation (D47).
|
|
3655
|
+
|
|
3656
|
+
Per D39, zero rest `scale`, `scaleX` or `scaleY` on a bone or any ancestor is
|
|
3657
|
+
`bad-value` at the affected bone, with a hint to use nonzero rest scales. Animation may
|
|
3658
|
+
collapse a bone from a nonzero rest pose.
|
|
3659
|
+
|
|
3660
|
+
Rest frames are computed once from document values, never the first sampled
|
|
3661
|
+
pose. A chained bone's frame is its parent's frame multiplied by
|
|
3662
|
+
`T(parent.length,0) R(rotate) S(scale*scaleX,scale*scaleY)`.
|
|
3663
|
+
A root uses its parent's grid transform multiplied by
|
|
3664
|
+
`T(x,y) R(rotate) S(scale*scaleX,scale*scaleY)`.
|
|
3665
|
+
Rigid child geometry remains in rest grid coordinates: the bone contributes
|
|
3666
|
+
`F(now) F(rest)^-1`, keeping every bone's drawing transform identity at rest
|
|
3667
|
+
(relative to its non-bone ancestors). A nested root uses this same rule.
|
|
3668
|
+
This preserves Glyphy geometry without re-origining (DECISIONS D3–D6;
|
|
3669
|
+
`docs/research/glyphy-compat.md`). A bone's world frame is its physical start
|
|
3670
|
+
frame, while the published drawing matrix is its rest-relative deformation.
|
|
3671
|
+
|
|
3672
|
+
### Constraint fields and evaluation
|
|
3673
|
+
|
|
3674
|
+
All five kinds accept numeric `strength` in 0..1, default 1, animatable and
|
|
3675
|
+
bindable. Fields are addressed `<id>.constraints.<index>.<field>`; arrays are
|
|
3676
|
+
zero-based, ordered, and later full-strength constraints override earlier ones.
|
|
3677
|
+
All fields shown as numbers below are animatable/bindable. Static controls are
|
|
3678
|
+
booleans, references, enumerations and space choices.
|
|
3679
|
+
|
|
3680
|
+
| JSON / AniML | Fields and defaults |
|
|
3681
|
+
| --- | --- |
|
|
3682
|
+
| `copyPosition` / `<copy-position>` | optional `from`; `factorX`, `factorY` = 1; `copyX`, `copyY` = true; optional `minX`, `maxX`, `minY`, `maxY`; `offset` = false; `fromSpace`, `toSpace` = world; `limitSpace` defaults to `toSpace` |
|
|
3683
|
+
| `copyRotation` / `<copy-rotation>` | optional `from`; `factor` = 1; optional `min`, `max` in degrees; `offset` = false; spaces as above |
|
|
3684
|
+
| `copyScale` / `<copy-scale>` | position fields, operating on scale axes |
|
|
3685
|
+
| `copyTransform` / `<copy-transform>` | required `from`; optional numeric `origin: [u,v]`; `fromSpace`, `toSpace` = world |
|
|
3686
|
+
| `keepDistance` / `<keep-distance>` | required `to` and nonnegative `distance`; `mode`: within (default), beyond, exact |
|
|
3687
|
+
|
|
3688
|
+
Camel-case field names use hyphenated AniML attributes (`factor-x`, `from-space`,
|
|
3689
|
+
`limit-space`, etc.). The CLI accepts JSON kind names as schema topics as well
|
|
3690
|
+
as canonical AniML element names. Every kind validates only its declared fields.
|
|
3691
|
+
Unknown references are `rig-target` at the constraint, with a repair hint.
|
|
3692
|
+
|
|
3693
|
+
Position is the transformed pivot (the start for a bone). Rotation and scales
|
|
3694
|
+
come from QR decomposition: nonnegative X scale, signed Y scale and shear.
|
|
3695
|
+
Derived rotations are normalized to [-180,180). A zero X axis uses the Y axis
|
|
3696
|
+
for its angle. Blend decomposes around the same pivot, interpolates position,
|
|
3697
|
+
scale and shear, and rotates along the shortest arc; the positive half-turn
|
|
3698
|
+
wins an exact tie. Strength 0 skips, 1 replaces. Each partial constraint reads
|
|
3699
|
+
the preceding constraint's result, never the previous frame's result.
|
|
3700
|
+
|
|
3701
|
+
For copy constraints, source-space decomposition reads the target world frame
|
|
3702
|
+
(`world`) or the target relative to its own parent (`local`). With no `from`,
|
|
3703
|
+
world reads the parent's frame and pivot; local reads identity (D17).
|
|
3704
|
+
The copied components replace the host's corresponding components in destination
|
|
3705
|
+
space; disabled axes keep their current values. Factors multiply source values.
|
|
3706
|
+
`offset` adds the host's document-rest position/rotation, or multiplies its
|
|
3707
|
+
rest scale. Limits clamp the result in `limitSpace`, without changing uncopied
|
|
3708
|
+
components, then convert back to world. When limit and destination space match,
|
|
3709
|
+
limits act on the signed copied axes before canonical decomposition.
|
|
3710
|
+
|
|
3711
|
+
`copyTransform` copies position, rotation, scales and shear together. By default
|
|
3712
|
+
the target point is its pivot. `origin` selects a normalized point over the
|
|
3713
|
+
target's rest geometric bounds (no stroke); its transformed point replaces the
|
|
3714
|
+
host's pivot position. The point tuple uses the shared `point` value type and is
|
|
3715
|
+
animatable and bindable as one value. JSON track keys use `[u,v]`; AniML authors
|
|
3716
|
+
the rest tuple as `origin="u v"` and tuple track keys as `[u,v]`.
|
|
3717
|
+
|
|
3718
|
+
Rest bounds are evaluated from authored geometry before animation. Paths, circles,
|
|
3719
|
+
arcs, rects, ellipses, polygons, stars, triangles, contours, images, text and
|
|
3720
|
+
layout boxes contribute their geometric bounds. A container target recursively
|
|
3721
|
+
unions descendant geometry after each descendant's authored rest transform is
|
|
3722
|
+
mapped into the target's local space; structural nodes with no drawing geometry
|
|
3723
|
+
contribute no box. Stroke width is never included. Browser builds that use a
|
|
3724
|
+
non-default `copyTransform.origin` must install the optional heavy `rig-origin`
|
|
3725
|
+
entry; otherwise loading reports `rig-origin` at the origin field with a repair
|
|
3726
|
+
hint.
|
|
3727
|
+
|
|
3728
|
+
Distance constraints measure between world pivots and preserve the host's
|
|
3729
|
+
rotation, scale and shear. Within enforces a maximum radius, beyond a minimum,
|
|
3730
|
+
and exact the ring. A point already allowed is unchanged; a coincident point
|
|
3731
|
+
needing a nonzero radius moves along world +X. Strength uses the same transform
|
|
3732
|
+
blend as every other constraint. Distance is in grid units.
|
|
3733
|
+
|
|
3734
|
+
### Rig stage and extension contract
|
|
3735
|
+
|
|
3736
|
+
After bindings, clamp and layout, solve nodes in a dependency order computed
|
|
3737
|
+
once at load: parents and targets before hosts; a solver's declared additional
|
|
3738
|
+
writes before their other descendants. Add constraints in authored order; a
|
|
3739
|
+
cycle is `rig-cycle` at the constraint closing it, with a hint to move its target
|
|
3740
|
+
outside the constrained subtree. No dirty-restart loop runs (D2, D15;
|
|
3741
|
+
`docs/research/rive-format-and-graph.md` §3 and
|
|
3742
|
+
`docs/research/rive-animation-and-state.md` §2.3).
|
|
3743
|
+
|
|
3744
|
+
Each pass starts from the layout-adjusted authored local values. It publishes
|
|
3745
|
+
per-player local matrices for rendering, hits, accessibility and pointer mapping.
|
|
3746
|
+
A pose pad reads that first solved result via `Model.rigPose(player)`; returning
|
|
3747
|
+
true performs exactly one more pass, with freshly posed values. Unchanged values
|
|
3748
|
+
and layout matrices skip the first solve, retaining its previous solved result;
|
|
3749
|
+
the pose callback may still request its second pass. Layout/guards never read
|
|
3750
|
+
same-step pose outputs. Rigging does not keep a settled player awake.
|
|
3751
|
+
|
|
3752
|
+
`modules/rig/core/api.ts` exposes `registerConstraint({kind, shape, targets,
|
|
3753
|
+
writes?, solve})` and `RigContext`. Independently bundled modules register through
|
|
3754
|
+
the active rig module's `registerConstraint` during `init`. `world`/`rest` use
|
|
3755
|
+
bone start frames for bones and grid matrices for ordinary nodes; `position`
|
|
3756
|
+
returns the world pivot. `setWorld` publishes a solver's declared writes;
|
|
3757
|
+
`num(node,k,key,fallback)` reads current constraint slots. Solvers return a desired
|
|
3758
|
+
host matrix for central strength blending, or explicitly blend multiple writes
|
|
3759
|
+
through `ctx.blend`. Each player owns its solved frames and local overrides.
|
|
3760
|
+
|
|
3761
|
+
Snapshots and `expect` select derived, read-only
|
|
3762
|
+
`<id>.world.x|y|rotate|scaleX|scaleY` and `<bone>.tip.x|y`.
|
|
3763
|
+
They are not animation destinations, binding destinations, or guard operands.
|
|
3764
|
+
|
|
3765
|
+
### Evidence by capability
|
|
3766
|
+
|
|
3767
|
+
<!-- capability: rigging.bone-chains -->
|
|
3768
|
+
Independent numeric evidence: `spec/conformance/rigging-bone-chains/` (0, 0.5, 1 s).
|
|
3769
|
+
Oracle capture/integration: `tests/oracle/rigging-bone-chains/`; a pending marker is not a parity pass.
|
|
3770
|
+
|
|
3771
|
+
<!-- capability: rigging.root-bones -->
|
|
3772
|
+
Independent numeric evidence: `spec/conformance/rigging-root-bones/` (0, 0.5, 1 s).
|
|
3773
|
+
Oracle capture/integration: `tests/oracle/rigging-root-bones/`; a pending marker is not a parity pass.
|
|
3774
|
+
|
|
3775
|
+
<!-- capability: rigging.constraint-order -->
|
|
3776
|
+
Independent numeric evidence: `spec/conformance/rigging-constraint-order/` (0, 0.5, 1 s).
|
|
3777
|
+
Oracle capture/integration: `tests/oracle/rigging-constraint-order/`; a pending marker is not a parity pass.
|
|
3778
|
+
|
|
3779
|
+
<!-- capability: rigging.constraint-strength -->
|
|
3780
|
+
Independent numeric evidence: `spec/conformance/rigging-constraint-strength/` (0, 0.5, 1 s).
|
|
3781
|
+
Oracle capture/integration: `tests/oracle/rigging-constraint-strength/`; a pending marker is not a parity pass.
|
|
3782
|
+
|
|
3783
|
+
<!-- capability: rigging.translation -->
|
|
3784
|
+
Independent numeric evidence: `spec/conformance/rigging-translation/` (0, 0.5, 1 s).
|
|
3785
|
+
Oracle capture/integration: `tests/oracle/rigging-translation/`; a pending marker is not a parity pass.
|
|
3786
|
+
|
|
3787
|
+
<!-- capability: rigging.rotation -->
|
|
3788
|
+
Independent numeric evidence: `spec/conformance/rigging-rotation/` (0, 0.5, 1 s).
|
|
3789
|
+
Oracle capture/integration: `tests/oracle/rigging-rotation/`; a pending marker is not a parity pass.
|
|
3790
|
+
|
|
3791
|
+
<!-- capability: rigging.scale -->
|
|
3792
|
+
Independent numeric evidence: `spec/conformance/rigging-scale/` (0, 0.5, 1 s).
|
|
3793
|
+
Oracle capture/integration: `tests/oracle/rigging-scale/`; a pending marker is not a parity pass.
|
|
3794
|
+
|
|
3795
|
+
<!-- capability: rigging.transform -->
|
|
3796
|
+
Independent numeric evidence: `spec/conformance/rigging-transform/` (0, 0.5, 1 s).
|
|
3797
|
+
Oracle capture/integration: `tests/oracle/rigging-transform/`; a pending marker is not a parity pass.
|
|
3798
|
+
|
|
3799
|
+
<!-- capability: rigging.distance-closer -->
|
|
3800
|
+
Independent numeric evidence: `spec/conformance/rigging-distance-closer/` (0, 0.5, 1 s).
|
|
3801
|
+
Oracle capture/integration: `tests/oracle/rigging-distance-closer/`; a pending marker is not a parity pass.
|
|
3802
|
+
|
|
3803
|
+
<!-- capability: rigging.distance-further -->
|
|
3804
|
+
Independent numeric evidence: `spec/conformance/rigging-distance-further/` (0, 0.5, 1 s).
|
|
3805
|
+
Oracle capture/integration: `tests/oracle/rigging-distance-further/`; a pending marker is not a parity pass.
|
|
3806
|
+
|
|
3807
|
+
<!-- capability: rigging.distance-exact -->
|
|
3808
|
+
Independent numeric evidence: `spec/conformance/rigging-distance-exact/` (0, 0.5, 1 s).
|
|
3809
|
+
Oracle capture/integration: `tests/oracle/rigging-distance-exact/`; a pending marker is not a parity pass.
|
|
3810
|
+
|
|
3811
|
+
## posepad
|
|
3812
|
+
|
|
3813
|
+
<!-- capability: rigging.joystick -->
|
|
3814
|
+
<!-- capability: animation.joystick-two-axis -->
|
|
3815
|
+
<!-- capability: animation.joystick-invert -->
|
|
3816
|
+
<!-- capability: animation.joystick-handle -->
|
|
3817
|
+
|
|
3818
|
+
`requires: ["posepad"]` enables `type: "posepad"`, AniML `<posepad>`. It draws nothing
|
|
3819
|
+
and has no hit area. The identical lowercase node name follows the existing node registry;
|
|
3820
|
+
no node-name alias or markup compiler extension is needed (P8 orchestrator decision).
|
|
3821
|
+
|
|
3822
|
+
| JSON | AniML | Meaning |
|
|
3823
|
+
|---|---|---|
|
|
3824
|
+
| `timelineX`, `timelineY` | `timeline-x`, `timeline-y` | Optional declared timeline names |
|
|
3825
|
+
| `valueX`, `valueY` | `value-x`, `value-y` | Numbers, default 0, clamped to −1..1; animatable/bindable |
|
|
3826
|
+
| `invertX`, `invertY` | `invert-x`, `invert-y` | Static booleans, default false |
|
|
3827
|
+
| `handle` | `handle` | Optional node id; declares the optional handle entry requirement |
|
|
3828
|
+
| `box` | `box` | `[x0, y0, width, height]` in pad coordinates, positive width/height |
|
|
3829
|
+
|
|
3830
|
+
After layout, pads run in authored node order, X before Y. Clamp control v to −1..1,
|
|
3831
|
+
negate when its axis is inverted, then sample at `(v + 1) / 2 * timeline.duration`.
|
|
3832
|
+
Sampling uses the active registry's track sampler (§5), including typed tracks, custom
|
|
3833
|
+
eases and per-player data key values. Loop/work-area/playback controls do not change this
|
|
3834
|
+
whole-duration mapping. Pad sampling never plays clocks or emits timeline events.
|
|
3835
|
+
|
|
3836
|
+
Sampled tracks form a per-player overlay above layers and bindings. Clamp numeric results
|
|
3837
|
+
to each target property's registry range; later axes/pads win collisions. `prop`, `num`,
|
|
3838
|
+
`value`, `values()` and snapshots expose the overlay. It does not mutate model rest values,
|
|
3839
|
+
tracks or another player's state. Layout sees the previous step's overlay; guards never
|
|
3840
|
+
read same-step posed output. Drawing revisions are invalidated for sampled target nodes.
|
|
3841
|
+
A pad introduces no ongoing clock and does not prevent settling.
|
|
3842
|
+
|
|
3843
|
+
A handle requires the optional heavy `@anima-format/web/posepad-handle` entry in addition
|
|
3844
|
+
to `@anima-format/web/posepad`. Declaring `handle` without that entry reports `posepad-handle`
|
|
3845
|
+
at the handle field, with an import hint (verbose builds); it never silently poses freely.
|
|
3846
|
+
Tooling includes handle support. A handle also requires `box`. Read the handle's current
|
|
3847
|
+
world pivot (including motion's animated pivot), map it through the inverse pad world matrix,
|
|
3848
|
+
then derive `valueX = clamp(2*(hx-x0)/width-1, -1, 1)` and similarly for Y. These outputs
|
|
3849
|
+
are uninverted; inversion affects sampling only. A singular pad matrix yields centered outputs.
|
|
3850
|
+
Both outputs are included in snapshots even when no track references them. Animate or bind
|
|
3851
|
+
the handle, not these outputs. Pointer drags use the input/bind modules' existing queued writes.
|
|
3852
|
+
|
|
3853
|
+
With `rig`, chain `model.rigPose` between its solve passes, returning true when the pad wrote;
|
|
3854
|
+
rig solves again exactly once. Without rig, chain a standalone `afterStep` stage after layout
|
|
3855
|
+
and read `p.world`. The distance-gated handle conformance probe remains pending until rig lands.
|
|
3856
|
+
|
|
3857
|
+
Diagnostics: `posepad-timeline` at `/nodes/…/timelineX|timelineY` names an unknown timeline;
|
|
3858
|
+
`posepad-handle` at `/nodes/…/handle` means an unknown handle or missing handle entry;
|
|
3859
|
+
`posepad-box` at `/nodes/…/box` means the required box is absent. Structural box errors use
|
|
3860
|
+
`bad-type` or `bad-value` at the offending field/coordinate. `posepad-output` points
|
|
3861
|
+
at a bind, track or state-pose write to a handle-driven value. `posepad-cycle` at the pad's
|
|
3862
|
+
axis timeline rejects a write to its handle, any handle ancestor, any transitive constraint
|
|
3863
|
+
target (and its ancestors), or its own coordinate frame/ancestors. These restrictions keep
|
|
3864
|
+
the bounded solve/pose/solve order independent of a dirt-restart loop.
|
|
3865
|
+
|
|
3866
|
+
Design rationale: [animation/state research](../docs/research/rive-animation-and-state.md) §2.3
|
|
3867
|
+
and [format/graph research](../docs/research/rive-format-and-graph.md) §3.2–3.4 describe update
|
|
3868
|
+
restarts; this explicit stage retains deterministic step order. No Rive source or binary format
|
|
3869
|
+
is used. Native oracle captures await orchestrator-owned budgets and remain pending evidence.
|
|
3870
|
+
|
|
3871
|
+
## ik
|
|
3872
|
+
|
|
3873
|
+
<!-- capability: rigging.ik -->
|
|
3874
|
+
### Inverse kinematics
|
|
3875
|
+
|
|
3876
|
+
The optional `ik` module registers the `ik` constraint kind with `rig`. In JSON an IK constraint is
|
|
3877
|
+
an item in a bone's `constraints` array:
|
|
3878
|
+
|
|
3879
|
+
```json
|
|
3880
|
+
{"kind":"ik","target":"aim","bones":2,"bend":"cw","strength":1}
|
|
3881
|
+
```
|
|
3882
|
+
|
|
3883
|
+
AniML writes the same object as a child element:
|
|
3884
|
+
|
|
3885
|
+
```xml
|
|
3886
|
+
<ik target="aim" bones="2" bend="cw" strength="1"/>
|
|
3887
|
+
```
|
|
3888
|
+
|
|
3889
|
+
`target` is a required node id and the target point is that node's world pivot. `bones` is a static
|
|
3890
|
+
integer of at least 1, defaults to 1, and counts the entire solved chain including the bone carrying
|
|
3891
|
+
the constraint. Thus `bones=1` aims only the host bone and `bones=2` solves the host plus its parent.
|
|
3892
|
+
This intentionally differs from Rive's parent-count field. `strength` is the common rig constraint
|
|
3893
|
+
strength: it is animatable and bindable at `<bone>.constraints.<k>.strength`, defaults to 1, and is
|
|
3894
|
+
clamped to 0..1. `target`, `bones`, and `bend` are structural fields.
|
|
3895
|
+
|
|
3896
|
+
The solve uses scaled world-space bone lengths and never changes a length. For one bone, the bone's
|
|
3897
|
+
direction aims from its start toward the target. For two bones, an analytic law-of-cosines solve
|
|
3898
|
+
places the joint on the selected bend side. When the target is farther away than the sum of the
|
|
3899
|
+
chain lengths, every bone straightens toward the target. A target coincident with the chain start
|
|
3900
|
+
leaves the current pose unchanged.
|
|
3901
|
+
|
|
3902
|
+
For chains longer than two bones, Anima independently reimplements the multi-link strategy in
|
|
3903
|
+
Rive's MIT runtime `src/constraints/ik_constraint.cpp`: in FK order, each of the first `n-1` links
|
|
3904
|
+
performs one analytic two-link solve against the final tip, with the downstream subchain rotated as
|
|
3905
|
+
a rigid remainder. The iteration count is therefore fixed by the chain length (`n-1`) and has no
|
|
3906
|
+
convergence test or early exit. No Rive source text or binary format is used. This choice fixes the
|
|
3907
|
+
otherwise non-unique multi-link solution while keeping results deterministic across runtimes.
|
|
3908
|
+
|
|
3909
|
+
The IK kind declares every solved ancestor bone through `writes()`, so rig dependency ordering runs
|
|
3910
|
+
the target first and the chain's descendants after the rewritten bones. The solver executes in the
|
|
3911
|
+
normal rig stage described in `## rig`. IK strength mixes only each chain bone's local rotation from
|
|
3912
|
+
its unconstrained value toward the fully solved value, using the shortest angular arc. The chain is
|
|
3913
|
+
then rebuilt in parent-to-child order from those mixed local rotations, so every child start remains
|
|
3914
|
+
at its parent's tip for every strength from 0 through 1. Bone lengths, local scale and local
|
|
3915
|
+
translation are unchanged. Strength 0 is the unconstrained pose and strength 1 is the full IK solve.
|
|
3916
|
+
|
|
3917
|
+
Validation adds two IK-specific errors in addition to rig's shared `rig-target` for an unknown
|
|
3918
|
+
target. `rig-ik-host` points at `/nodes/.../constraints/<k>` when `ik` is attached to a non-bone and
|
|
3919
|
+
hints to move it to the chain tip. `rig-ik-chain` points at the constraint's `/bones` field when the
|
|
3920
|
+
requested count is longer than the available parent-bone chain and hints to reduce the count or add
|
|
3921
|
+
parent bones. Using an IK constraint without declaring `ik` in `requires` is `undeclared-feature`.
|
|
3922
|
+
`anima schema ik` exposes `target`, `bones`, `bend`, and `strength` through the registered AniML
|
|
3923
|
+
element descriptor.
|
|
3924
|
+
|
|
3925
|
+
|
|
3926
|
+
<!-- capability: rigging.ik-bend -->
|
|
3927
|
+
### IK bend direction
|
|
3928
|
+
|
|
3929
|
+
`bend` is `cw` (the default) or `ccw`. For an unmirrored chain, `cw` places the two-link joint on
|
|
3930
|
+
the screen-clockwise side of the directed line from the chain start to the target; `ccw` selects the
|
|
3931
|
+
opposite analytic solution. A negative-determinant ancestor mirrors that side so authored bend
|
|
3932
|
+
direction follows the mirrored chain. The same side choice is used for each deterministic pair solve
|
|
3933
|
+
in a chain longer than two bones.
|
|
3934
|
+
|
|
3935
|
+
## pathfollow
|
|
3936
|
+
|
|
3937
|
+
The optional `pathfollow` module extends rig constraints with normalized outline following. Documents
|
|
3938
|
+
using these constraints declare both `rig` and `pathfollow` in `requires`. Path measurement is an
|
|
3939
|
+
internal runtime detail of this module; it does not add public APIs.
|
|
3940
|
+
|
|
3941
|
+
<!-- capability: rigging.follow-path -->
|
|
3942
|
+
### Follow path constraint
|
|
3943
|
+
|
|
3944
|
+
JSON `{"kind":"alongPath","path":"track",...}` and AniML `<along-path path="track" .../>` place
|
|
3945
|
+
the constrained node's pivot on the target outline. `path` is required and names a measurable
|
|
3946
|
+
`path`, `contour`, `circle`, `rect`, or `arc`. `at` is an animatable normalized distance and defaults
|
|
3947
|
+
to 0. Open outlines clamp `at` to 0..1. Closed outlines wrap it modulo 1, so 1 and 0 name the same
|
|
3948
|
+
point and values outside the interval continue around the outline. `orient` defaults to false; when
|
|
3949
|
+
true, the constrained node's world rotation becomes the sampled tangent angle, with 0 degrees along
|
|
3950
|
+
+x. `offset` defaults to false; when true, the node's animated translation away from its authored
|
|
3951
|
+
rest translation is added after path placement. The inherited rig `strength` field defaults to 1 and
|
|
3952
|
+
blends the old and desired world transforms.
|
|
3953
|
+
|
|
3954
|
+
The target is a rig dependency and is measured in its current resolved local geometry, then its world
|
|
3955
|
+
transform is applied to the sampled point and tangent. Path data includes current morph values.
|
|
3956
|
+
Measurement uses the same cubic subdivision as `Shared outline lengths`: absolute segments, arc
|
|
3957
|
+
conversion into at-most-90-degree cubic pieces, recursive de Casteljau subdivision at t=0.5 until
|
|
3958
|
+
control-polygon length minus chord is at most 0.0001 grid units or depth 20, and the mean of polygon
|
|
3959
|
+
and chord for each accepted piece. The cumulative table supplies both arc-length position and tangent.
|
|
3960
|
+
Circles, arcs and rounded rectangles follow their equivalent outline geometry. A skinned target is a
|
|
3961
|
+
known limitation: following uses its undeformed outline rather than the deformed skin result.
|
|
3962
|
+
|
|
3963
|
+
If `path` resolves to a node that is not measurable, loading reports `rig-path-target` at the exact
|
|
3964
|
+
constraint `/path` field, with a message that the target is not a measurable shape and a hint to use
|
|
3965
|
+
a path, contour, circle, rect or arc. Unknown ids continue to use rig's normal target diagnostic.
|
|
3966
|
+
Constraint fields are addressable as `<node>.constraints.<k>.<field>` and participate in the normal
|
|
3967
|
+
rig solve order.
|
|
3968
|
+
|
|
3969
|
+
<!-- capability: rigging.list-follow-path -->
|
|
3970
|
+
### List follow-path constraint
|
|
3971
|
+
|
|
3972
|
+
JSON `{"kind":"spreadAlongPath","path":"track",...}` and AniML `<spread-along-path
|
|
3973
|
+
path="track" .../>` distribute the constrained node's direct children along an outline. On a
|
|
3974
|
+
`repeat`, the same rule applies to its live reconciled items after list reconciliation. `start`,
|
|
3975
|
+
`end`, and `shift` are animatable normalized distances with defaults 0, 1, and 0. For child index
|
|
3976
|
+
`k` among `n` children, the sampled fraction is
|
|
3977
|
+
`start + (end - start) * k / max(n - 1, 1) + shift`. Therefore a single child uses `start + shift`.
|
|
3978
|
+
Open outlines clamp each resulting fraction and closed outlines wrap it by the same rule as
|
|
3979
|
+
`alongPath`. `orient` defaults to false and applies the sampled tangent rotation independently to
|
|
3980
|
+
each child. The inherited `strength` field blends each child's current and desired world transform.
|
|
3981
|
+
|
|
3982
|
+
A changing repeat count immediately recomputes all item fractions without rebuilding rig solve order.
|
|
3983
|
+
`spreadAlongPath` uses the same measurable-target set, `rig-path-target` diagnostic, private
|
|
3984
|
+
measurement table, transform handling, and undeformed-skinned-target limitation as `alongPath`.
|
|
3985
|
+
|
|
3986
|
+
## skin
|
|
3987
|
+
|
|
3988
|
+
The optional `skin` module deforms authored geometry from bone rest frames. Documents using it declare
|
|
3989
|
+
`skin` and `rig` in `requires`, plus the ordinary feature modules needed by the host geometry. A
|
|
3990
|
+
`skin` is valid on a `path` backed by inline `d` or a glyph, a `contour`, or an `image` that also has
|
|
3991
|
+
a `mesh`. Any other host reports `rig-skin-host` at the node's `/skin` pointer with a hint to move the
|
|
3992
|
+
skin to supported geometry. A path whose `d` is animated or posed while skinned reports
|
|
3993
|
+
`rig-skin-morph`; bone animation is the supported way to deform a skinned path.
|
|
3994
|
+
|
|
3995
|
+
<!-- capability: rigging.skin -->
|
|
3996
|
+
### Bind-pose skinning
|
|
3997
|
+
|
|
3998
|
+
JSON authors `skin: {"bones":[ids], "weights"?: [...]}`. AniML authors the same object structurally:
|
|
3999
|
+
`<skin bones="upper fore" weights='[...]'/>` as a child of the host node. `bones` is a
|
|
4000
|
+
whitespace-separated list; `weights`, when present, is one JSON array. The compiler and decompiler
|
|
4001
|
+
round-trip this child form through the registry. Bone ids must name bone nodes. An unknown id, a
|
|
4002
|
+
non-bone id, or an explicit influence that is not listed in `skin.bones` reports `rig-weight-bone`
|
|
4003
|
+
with the precise bone or influence pointer and a repair hint.
|
|
4004
|
+
|
|
4005
|
+
Binding uses the bones' document rest frames. No bind matrices are authored. For a node-local rest
|
|
4006
|
+
point `p`, let `W0` be the node's rest-world transform and let `Bi(rest)` and `Bi(now)` be a bone's
|
|
4007
|
+
rest and solved world transforms. Each bone contributes `Di = Bi(now) · Bi(rest)^-1`; `@rest`
|
|
4008
|
+
contributes identity. The deformed local point is
|
|
4009
|
+
`p' = W0^-1 · Σ_i wi · Di · W0 · p`. A non-invertible rest transform leaves the source point
|
|
4010
|
+
unchanged. The host draw transform remains its rest-world transform, so animated transforms on the
|
|
4011
|
+
skinned node and its ancestors do not add a second motion source and stroke width follows rest scale.
|
|
4012
|
+
|
|
4013
|
+
Path points are every M/L/C endpoint in order; Z adds no point. For a cubic, the first control is the
|
|
4014
|
+
previous anchor's `out` handle and the second control is the current anchor's `in` handle. Contour
|
|
4015
|
+
points are vertices; their handles deform before contour rounding is reconstructed. Mesh points are
|
|
4016
|
+
every mesh vertex. `draw.frame` publishes the deformed path/contour geometry, while meshed images use
|
|
4017
|
+
the `Player.skinPoints(node, xy)` seam before mesh points are published. Paint outline queries use the
|
|
4018
|
+
deformed path or contour, so pointer hit testing, semantic bounds, trims, and relative dashes observe
|
|
4019
|
+
the deformed outline rather than the bind outline.
|
|
4020
|
+
|
|
4021
|
+
The read-only derived address `<node>.skin.point.<k>` returns the deformed anchor/vertex in world
|
|
4022
|
+
coordinates. It is a snapshot/conformance output, not an authored animation target.
|
|
4023
|
+
|
|
4024
|
+
<!-- capability: rigging.weights -->
|
|
4025
|
+
### Vertex bone weights
|
|
4026
|
+
|
|
4027
|
+
With explicit weights, `weights` has exactly one entry per path anchor, contour vertex, or mesh
|
|
4028
|
+
vertex. Each entry has `point`, an influence map from a bone id or `@rest` to a number in 0..1.
|
|
4029
|
+
Influence maps may contain at most four entries; more report `rig-weight-count` with a hint to keep at
|
|
4030
|
+
most four. A sum differing from 1 by more than 1e-6 reports `rig-weight-sum` as a warning and the
|
|
4031
|
+
runtime normalizes the map. A non-positive sum resolves to a full `@rest` influence after the warning.
|
|
4032
|
+
|
|
4033
|
+
When `weights` is absent, the loader computes deterministic automatic weights independently for each
|
|
4034
|
+
anchor and handle. It measures world-space distance from the rest point to every listed bone segment
|
|
4035
|
+
from bone start to its authored `length`, chooses the two nearest segments, assigns each
|
|
4036
|
+
`1 / (d + 0.001)^2`, and normalizes the two values. With one listed bone, that bone receives weight 1.
|
|
4037
|
+
The result is cached per player/rest geometry and does not change as the bones animate.
|
|
4038
|
+
|
|
4039
|
+
<!-- capability: rigging.cubic-weights -->
|
|
4040
|
+
### Independent handle weights
|
|
4041
|
+
|
|
4042
|
+
An explicit weight entry may use `{ "point": {...}, "in": {...}, "out": {...} }`. `point` is
|
|
4043
|
+
required. `in` and `out` are independently validated and normalized by the same count, sum, and bone
|
|
4044
|
+
rules. If either handle map is omitted, that handle inherits its anchor's `point` map. This lets cubic
|
|
4045
|
+
handles bend independently while preserving the simpler anchor-only spelling for ordinary paths.
|
|
4046
|
+
|
|
4047
|
+
## scroll
|
|
4048
|
+
|
|
4049
|
+
<!-- capability: rigging.scroll -->
|
|
4050
|
+
### Scroll rig constraint
|
|
4051
|
+
|
|
4052
|
+
The optional `scroll` module extends rig with `scroll` and `scrollThumb` constraints. A `scroll`
|
|
4053
|
+
constraint is authored on the content node as `{kind:"scroll", viewport, axis, offsetX?, offsetY?,
|
|
4054
|
+
interactive?}`; AniML uses `<scroll viewport="…" axis="x|y|both" offset-x="…" offset-y="…"
|
|
4055
|
+
interactive="…"/>`. `viewport` is required, `axis` is `x`, `y` or `both`, offsets default to zero in
|
|
4056
|
+
grid units, and `interactive` defaults to true. The viewport extent is its layout box when it is a
|
|
4057
|
+
layout container, otherwise its rest local bounds. Content extent is the content node's layout box
|
|
4058
|
+
when present, otherwise the rest bounds of its subtree. Each enabled offset clamps to
|
|
4059
|
+
`[0, max(0, content - viewport)]` and translates the content by the negative clamped offset. A
|
|
4060
|
+
disabled axis resolves to zero.
|
|
4061
|
+
|
|
4062
|
+
Offsets are ordinary bindable/animatable constraint addresses. Interactive offsets are per-player
|
|
4063
|
+
state. Pointer and scroll input only queue a proposed state; phase 1 of the next `Player.step` applies
|
|
4064
|
+
the queue before rig solving. A primary-button drag that starts inside the viewport adds the pointer
|
|
4065
|
+
delta on enabled axes. Wheel or trackpad input over the viewport adds its delta and is handled only
|
|
4066
|
+
when clamping permits an actual offset change, so the host may scroll normally at an end stop. If an
|
|
4067
|
+
`offsetX` or `offsetY` address is bound or animated in the current step, that authored value wins over
|
|
4068
|
+
interactive state for that axis. The derived read-only snapshot addresses `percentX` and `percentY`
|
|
4069
|
+
are the clamped offset divided by its content range, or zero when the range is zero. They are output
|
|
4070
|
+
addresses only and are not guard operands.
|
|
4071
|
+
|
|
4072
|
+
`scrollThumb` is authored on a thumb node as `{kind:"scrollThumb", scroll, track, axis, resize?}` or
|
|
4073
|
+
`<scroll-thumb scroll="…" track="…" axis="x|y" resize="…"/>`. `scroll` names a content node carrying
|
|
4074
|
+
a `scroll` constraint and `track` names the track node. The thumb position follows the linked
|
|
4075
|
+
`percentX` or `percentY` across the available track travel. With `resize=true`, thumb length is
|
|
4076
|
+
`trackLength * viewportLength / contentLength`, clamped to the track and never smaller than the
|
|
4077
|
+
authored thumb length. Dragging the thumb maps pointer travel back to the linked offset.
|
|
4078
|
+
|
|
4079
|
+
A missing or unmeasurable viewport is `scroll-viewport` at that constraint's `/viewport` pointer,
|
|
4080
|
+
with a hint to target a layout container or a node/subtree with rest geometry. An unknown
|
|
4081
|
+
`scrollThumb.scroll` target uses rig's `rig-target` diagnostic. Authors clip visible scrolling content
|
|
4082
|
+
explicitly with composite's `clip`; scrolling does not add an implicit clip. This capability has no
|
|
4083
|
+
physics, inertia, elastic overscroll, snapping, virtualization or infinite-scroll semantics.
|