@unrulysystems/native-motion-core 0.1.0-alpha.0
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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/clock.cjs +71 -0
- package/dist/clock.d.cts +23 -0
- package/dist/clock.d.ts +23 -0
- package/dist/clock.js +66 -0
- package/dist/collect-reads.cjs +68 -0
- package/dist/collect-reads.d.cts +23 -0
- package/dist/collect-reads.d.ts +23 -0
- package/dist/collect-reads.js +62 -0
- package/dist/component/boundedArray.cjs +74 -0
- package/dist/component/boundedArray.d.cts +25 -0
- package/dist/component/boundedArray.d.ts +25 -0
- package/dist/component/boundedArray.js +68 -0
- package/dist/component/index.cjs +57 -0
- package/dist/component/index.d.cts +12 -0
- package/dist/component/index.d.ts +12 -0
- package/dist/component/index.js +22 -0
- package/dist/component/orchestration.cjs +129 -0
- package/dist/component/orchestration.d.cts +66 -0
- package/dist/component/orchestration.d.ts +66 -0
- package/dist/component/orchestration.js +124 -0
- package/dist/component/resolve.cjs +104 -0
- package/dist/component/resolve.d.cts +11 -0
- package/dist/component/resolve.d.ts +11 -0
- package/dist/component/resolve.js +97 -0
- package/dist/component/transition.cjs +352 -0
- package/dist/component/transition.d.cts +33 -0
- package/dist/component/transition.d.ts +33 -0
- package/dist/component/transition.js +339 -0
- package/dist/component/types.cjs +114 -0
- package/dist/component/types.d.cts +60 -0
- package/dist/component/types.d.ts +60 -0
- package/dist/component/types.js +111 -0
- package/dist/component/validate.cjs +1015 -0
- package/dist/component/validate.d.cts +37 -0
- package/dist/component/validate.d.ts +37 -0
- package/dist/component/validate.js +1002 -0
- package/dist/component/variants.cjs +333 -0
- package/dist/component/variants.d.cts +106 -0
- package/dist/component/variants.d.ts +106 -0
- package/dist/component/variants.js +321 -0
- package/dist/config/constants.cjs +41 -0
- package/dist/config/constants.d.cts +29 -0
- package/dist/config/constants.d.ts +29 -0
- package/dist/config/constants.js +38 -0
- package/dist/delay.cjs +72 -0
- package/dist/delay.d.cts +26 -0
- package/dist/delay.d.ts +26 -0
- package/dist/delay.js +70 -0
- package/dist/derived.cjs +143 -0
- package/dist/derived.d.cts +41 -0
- package/dist/derived.d.ts +41 -0
- package/dist/derived.js +139 -0
- package/dist/driver/index.cjs +22 -0
- package/dist/driver/index.d.cts +7 -0
- package/dist/driver/index.d.ts +7 -0
- package/dist/driver/index.js +14 -0
- package/dist/driver/keyframeTiming.cjs +130 -0
- package/dist/driver/keyframeTiming.d.cts +18 -0
- package/dist/driver/keyframeTiming.d.ts +18 -0
- package/dist/driver/keyframeTiming.js +124 -0
- package/dist/driver/keyframeTimingConfig.cjs +24 -0
- package/dist/driver/keyframeTimingConfig.d.cts +5 -0
- package/dist/driver/keyframeTimingConfig.d.ts +5 -0
- package/dist/driver/keyframeTimingConfig.js +22 -0
- package/dist/driver/prepare.cjs +459 -0
- package/dist/driver/prepare.d.cts +4 -0
- package/dist/driver/prepare.d.ts +4 -0
- package/dist/driver/prepare.js +454 -0
- package/dist/driver/reference.cjs +762 -0
- package/dist/driver/reference.d.cts +53 -0
- package/dist/driver/reference.d.ts +53 -0
- package/dist/driver/reference.js +757 -0
- package/dist/driver/step.cjs +55 -0
- package/dist/driver/step.d.cts +9 -0
- package/dist/driver/step.d.ts +9 -0
- package/dist/driver/step.js +52 -0
- package/dist/driver/tiers.cjs +40 -0
- package/dist/driver/tiers.d.cts +2 -0
- package/dist/driver/tiers.d.ts +2 -0
- package/dist/driver/tiers.js +37 -0
- package/dist/driver/types.cjs +9 -0
- package/dist/driver/types.d.cts +68 -0
- package/dist/driver/types.d.ts +68 -0
- package/dist/driver/types.js +8 -0
- package/dist/external-animation-ledger.cjs +1250 -0
- package/dist/external-animation-ledger.d.cts +416 -0
- package/dist/external-animation-ledger.d.ts +416 -0
- package/dist/external-animation-ledger.js +1243 -0
- package/dist/gesture/directionLock.cjs +25 -0
- package/dist/gesture/directionLock.d.cts +8 -0
- package/dist/gesture/directionLock.d.ts +8 -0
- package/dist/gesture/directionLock.js +21 -0
- package/dist/gesture/dragConfig.cjs +634 -0
- package/dist/gesture/dragConfig.d.cts +298 -0
- package/dist/gesture/dragConfig.d.ts +298 -0
- package/dist/gesture/dragConfig.js +624 -0
- package/dist/gesture/elastic.cjs +44 -0
- package/dist/gesture/elastic.d.cts +4 -0
- package/dist/gesture/elastic.d.ts +4 -0
- package/dist/gesture/elastic.js +39 -0
- package/dist/gesture/handoffSession.cjs +161 -0
- package/dist/gesture/handoffSession.d.cts +35 -0
- package/dist/gesture/handoffSession.d.ts +35 -0
- package/dist/gesture/handoffSession.js +158 -0
- package/dist/gesture/index.cjs +37 -0
- package/dist/gesture/index.d.cts +12 -0
- package/dist/gesture/index.d.ts +12 -0
- package/dist/gesture/index.js +11 -0
- package/dist/gesture/projection.cjs +95 -0
- package/dist/gesture/projection.d.cts +7 -0
- package/dist/gesture/projection.d.ts +7 -0
- package/dist/gesture/projection.js +87 -0
- package/dist/gesture/session.cjs +162 -0
- package/dist/gesture/session.d.cts +29 -0
- package/dist/gesture/session.d.ts +29 -0
- package/dist/gesture/session.js +158 -0
- package/dist/gesture/types.cjs +5 -0
- package/dist/gesture/types.d.cts +13 -0
- package/dist/gesture/types.d.ts +13 -0
- package/dist/gesture/types.js +4 -0
- package/dist/gesture/viewportConstraints.cjs +38 -0
- package/dist/gesture/viewportConstraints.d.cts +6 -0
- package/dist/gesture/viewportConstraints.d.ts +6 -0
- package/dist/gesture/viewportConstraints.js +34 -0
- package/dist/graph.cjs +226 -0
- package/dist/graph.d.cts +2 -0
- package/dist/graph.d.ts +2 -0
- package/dist/graph.js +223 -0
- package/dist/index.cjs +140 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +70 -0
- package/dist/inertia.cjs +214 -0
- package/dist/inertia.d.cts +53 -0
- package/dist/inertia.d.ts +53 -0
- package/dist/inertia.js +212 -0
- package/dist/instant.cjs +66 -0
- package/dist/instant.d.cts +16 -0
- package/dist/instant.d.ts +16 -0
- package/dist/instant.js +63 -0
- package/dist/internal-driver.cjs +81 -0
- package/dist/internal-driver.d.cts +19 -0
- package/dist/internal-driver.d.ts +19 -0
- package/dist/internal-driver.js +35 -0
- package/dist/keyframes.cjs +191 -0
- package/dist/keyframes.d.cts +13 -0
- package/dist/keyframes.d.ts +13 -0
- package/dist/keyframes.js +188 -0
- package/dist/layout/commitDetector.cjs +67 -0
- package/dist/layout/commitDetector.d.cts +21 -0
- package/dist/layout/commitDetector.d.ts +21 -0
- package/dist/layout/commitDetector.js +64 -0
- package/dist/layout/compose.cjs +67 -0
- package/dist/layout/compose.d.cts +30 -0
- package/dist/layout/compose.d.ts +30 -0
- package/dist/layout/compose.js +65 -0
- package/dist/layout/constants.cjs +13 -0
- package/dist/layout/constants.d.cts +6 -0
- package/dist/layout/constants.d.ts +6 -0
- package/dist/layout/constants.js +10 -0
- package/dist/layout/identity.cjs +700 -0
- package/dist/layout/identity.d.cts +67 -0
- package/dist/layout/identity.d.ts +67 -0
- package/dist/layout/identity.js +698 -0
- package/dist/layout/index.cjs +36 -0
- package/dist/layout/index.d.cts +18 -0
- package/dist/layout/index.d.ts +18 -0
- package/dist/layout/index.js +13 -0
- package/dist/layout/measure.cjs +81 -0
- package/dist/layout/measure.d.cts +43 -0
- package/dist/layout/measure.d.ts +43 -0
- package/dist/layout/measure.js +78 -0
- package/dist/layout/projection.cjs +99 -0
- package/dist/layout/projection.d.cts +23 -0
- package/dist/layout/projection.d.ts +23 -0
- package/dist/layout/projection.js +97 -0
- package/dist/layout/scroll.cjs +27 -0
- package/dist/layout/scroll.d.cts +13 -0
- package/dist/layout/scroll.d.ts +13 -0
- package/dist/layout/scroll.js +24 -0
- package/dist/layout/session.cjs +207 -0
- package/dist/layout/session.d.cts +73 -0
- package/dist/layout/session.d.ts +73 -0
- package/dist/layout/session.js +205 -0
- package/dist/layout/tree.cjs +826 -0
- package/dist/layout/tree.d.cts +70 -0
- package/dist/layout/tree.d.ts +70 -0
- package/dist/layout/tree.js +823 -0
- package/dist/layout/types.cjs +36 -0
- package/dist/layout/types.d.cts +31 -0
- package/dist/layout/types.d.ts +31 -0
- package/dist/layout/types.js +35 -0
- package/dist/motion-arc.cjs +184 -0
- package/dist/motion-arc.d.cts +78 -0
- package/dist/motion-arc.d.ts +78 -0
- package/dist/motion-arc.js +183 -0
- package/dist/motion-mix.cjs +205 -0
- package/dist/motion-mix.d.cts +3 -0
- package/dist/motion-mix.d.ts +3 -0
- package/dist/motion-mix.js +202 -0
- package/dist/motion-value-driver-port.cjs +636 -0
- package/dist/motion-value-driver-port.d.cts +406 -0
- package/dist/motion-value-driver-port.d.ts +406 -0
- package/dist/motion-value-driver-port.js +627 -0
- package/dist/motion-value.cjs +189 -0
- package/dist/motion-value.d.cts +51 -0
- package/dist/motion-value.d.ts +51 -0
- package/dist/motion-value.js +185 -0
- package/dist/presence/controller.cjs +657 -0
- package/dist/presence/controller.d.cts +6 -0
- package/dist/presence/controller.d.ts +6 -0
- package/dist/presence/controller.js +652 -0
- package/dist/presence/index.cjs +19 -0
- package/dist/presence/index.d.cts +4 -0
- package/dist/presence/index.d.ts +4 -0
- package/dist/presence/index.js +12 -0
- package/dist/presence/machine.cjs +50 -0
- package/dist/presence/machine.d.cts +10 -0
- package/dist/presence/machine.d.ts +10 -0
- package/dist/presence/machine.js +46 -0
- package/dist/presence/types.cjs +6 -0
- package/dist/presence/types.d.cts +32 -0
- package/dist/presence/types.d.ts +32 -0
- package/dist/presence/types.js +5 -0
- package/dist/repeat.cjs +311 -0
- package/dist/repeat.d.cts +174 -0
- package/dist/repeat.d.ts +174 -0
- package/dist/repeat.js +300 -0
- package/dist/spring.cjs +128 -0
- package/dist/spring.d.cts +18 -0
- package/dist/spring.d.ts +18 -0
- package/dist/spring.js +125 -0
- package/dist/subscriptions.cjs +74 -0
- package/dist/subscriptions.d.cts +19 -0
- package/dist/subscriptions.d.ts +19 -0
- package/dist/subscriptions.js +69 -0
- package/dist/subset/index.cjs +23 -0
- package/dist/subset/index.d.cts +6 -0
- package/dist/subset/index.d.ts +6 -0
- package/dist/subset/index.js +13 -0
- package/dist/subset/normalize.cjs +90 -0
- package/dist/subset/normalize.d.cts +10 -0
- package/dist/subset/normalize.d.ts +10 -0
- package/dist/subset/normalize.js +85 -0
- package/dist/subset/registry.cjs +259 -0
- package/dist/subset/registry.d.cts +25 -0
- package/dist/subset/registry.d.ts +25 -0
- package/dist/subset/registry.js +256 -0
- package/dist/subset/resolve.cjs +98 -0
- package/dist/subset/resolve.d.cts +24 -0
- package/dist/subset/resolve.d.ts +24 -0
- package/dist/subset/resolve.js +90 -0
- package/dist/timing.cjs +206 -0
- package/dist/timing.d.cts +17 -0
- package/dist/timing.d.ts +17 -0
- package/dist/timing.js +201 -0
- package/dist/transformTemplate.cjs +219 -0
- package/dist/transformTemplate.d.cts +29 -0
- package/dist/transformTemplate.d.ts +29 -0
- package/dist/transformTemplate.js +215 -0
- package/dist/transition.cjs +231 -0
- package/dist/transition.d.cts +89 -0
- package/dist/transition.d.ts +89 -0
- package/dist/transition.js +223 -0
- package/dist/types.cjs +4 -0
- package/dist/types.d.cts +175 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.js +3 -0
- package/dist/value-types/color.cjs +220 -0
- package/dist/value-types/color.d.cts +19 -0
- package/dist/value-types/color.d.ts +19 -0
- package/dist/value-types/color.js +218 -0
- package/dist/value-types/complex.cjs +160 -0
- package/dist/value-types/complex.d.cts +19 -0
- package/dist/value-types/complex.d.ts +19 -0
- package/dist/value-types/complex.js +155 -0
- package/dist/value-types/constants.cjs +15 -0
- package/dist/value-types/constants.d.cts +6 -0
- package/dist/value-types/constants.d.ts +6 -0
- package/dist/value-types/constants.js +12 -0
- package/dist/value-types/discrete.cjs +62 -0
- package/dist/value-types/discrete.d.cts +4 -0
- package/dist/value-types/discrete.d.ts +4 -0
- package/dist/value-types/discrete.js +56 -0
- package/dist/value-types/index.cjs +59 -0
- package/dist/value-types/index.d.cts +14 -0
- package/dist/value-types/index.d.ts +14 -0
- package/dist/value-types/index.js +24 -0
- package/dist/value-types/measure-resolve.cjs +325 -0
- package/dist/value-types/measure-resolve.d.cts +91 -0
- package/dist/value-types/measure-resolve.d.ts +91 -0
- package/dist/value-types/measure-resolve.js +313 -0
- package/dist/value-types/mix.cjs +90 -0
- package/dist/value-types/mix.d.cts +27 -0
- package/dist/value-types/mix.d.ts +27 -0
- package/dist/value-types/mix.js +85 -0
- package/dist/value-types/named-colors.cjs +61 -0
- package/dist/value-types/named-colors.d.cts +2 -0
- package/dist/value-types/named-colors.d.ts +2 -0
- package/dist/value-types/named-colors.js +58 -0
- package/dist/value-types/numeric.cjs +86 -0
- package/dist/value-types/numeric.d.cts +16 -0
- package/dist/value-types/numeric.d.ts +16 -0
- package/dist/value-types/numeric.js +79 -0
- package/dist/worklet-layout/config/constants.js +39 -0
- package/dist/worklet-layout/layout/constants.js +11 -0
- package/dist/worklet-layout/layout/identity.js +699 -0
- package/dist/worklet-layout/layout/projection.js +98 -0
- package/dist/worklet-layout/layout/session.js +206 -0
- package/dist/worklet-layout/layout/tree.js +824 -0
- package/dist/worklet-layout/layout/types.js +36 -0
- package/dist/worklet-layout/spring.js +125 -0
- package/dist/worklet-layout/timing.js +201 -0
- package/dist/worklet-layout/transition.js +223 -0
- package/dist/worklet-layout.cjs +47 -0
- package/dist/worklet-layout.d.cts +15 -0
- package/dist/worklet-layout.d.ts +15 -0
- package/dist/worklet-layout.js +28 -0
- package/dist/wrap.cjs +7 -0
- package/dist/wrap.d.cts +1 -0
- package/dist/wrap.d.ts +1 -0
- package/dist/wrap.js +4 -0
- package/package.json +45 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'worklet';
|
|
2
|
+
// @frame-path — executes inside the UI-runtime frame step; allocation-gated (REQ-DRIVER-021).
|
|
3
|
+
// SPEC-LAYOUT §2/§4a — the pure FLIP projection math (REQ-LAYOUT-002/011). Given a `before` rect (where the
|
|
4
|
+
// element was) and an `after` rect (where the host layout engine just placed it), `invert` yields the
|
|
5
|
+
// transform-only correction that makes the element — already laid out at `after` — appear at `before`.
|
|
6
|
+
// Driving progress 0→1 lerps that correction to identity, so at settle the element is coincident with the
|
|
7
|
+
// host layout and the applied transform is identity (Motion keeps layout authority; we animate the
|
|
8
|
+
// transition only). Host-agnostic, no graph/clock — the progress itself is driven by the reused SPRING in
|
|
9
|
+
// the session (M4). All transforms are about the rect's TOP-LEFT origin (transformOrigin 0,0), which keeps
|
|
10
|
+
// the invert/apply algebra exact and composable; a center/other origin is an M2 parity refinement (§7).
|
|
11
|
+
import { EPSILON_POS, EPSILON_SCALE } from "./constants.js";
|
|
12
|
+
/** The no-op correction: element sits exactly on its host layout. */
|
|
13
|
+
export const IDENTITY_TRANSFORM = {
|
|
14
|
+
translateX: 0,
|
|
15
|
+
translateY: 0,
|
|
16
|
+
scaleX: 1,
|
|
17
|
+
scaleY: 1,
|
|
18
|
+
};
|
|
19
|
+
// Linear interpolation of a single transform scalar by the (spring-driven) progress. Deliberately a scalar
|
|
20
|
+
// lerp, NOT value-types `mixNumeric` — that wraps a NumericValue for typed style interpolation, which is
|
|
21
|
+
// overkill for a raw geometric component. The reused SPRING supplies `progress`; this only shapes it.
|
|
22
|
+
function lerp(from, to, progress) {
|
|
23
|
+
'worklet';
|
|
24
|
+
return from + (to - from) * progress;
|
|
25
|
+
}
|
|
26
|
+
function assertMeasurable(rect) {
|
|
27
|
+
'worklet';
|
|
28
|
+
// A zero/negative/non-finite dimension would divide-by-zero (or NaN) the scale — fail loud, never fabricate
|
|
29
|
+
// a garbage delta (REQ-LAYOUT-011). Position may be any finite number; only the dimensions must be > 0.
|
|
30
|
+
if (!Number.isFinite(rect.width) ||
|
|
31
|
+
!Number.isFinite(rect.height) ||
|
|
32
|
+
rect.width <= 0 ||
|
|
33
|
+
rect.height <= 0) {
|
|
34
|
+
// ui-throw-ok: unreachable from the UI path — tree.setProjection validates BOTH rects
|
|
35
|
+
// (finite fields; positive target dimensions) before calling projectAtProgress (seal r11,
|
|
36
|
+
// F2a), so this assert cannot fire from the UI-reachable set. invert's remaining callers are
|
|
37
|
+
// JS-thread (session transform, web engine, verification), where the REQ-LAYOUT-011
|
|
38
|
+
// fail-loud contract stands.
|
|
39
|
+
throw new Error(`layout: cannot project onto a rect with non-positive/non-finite dimensions (measure returned width=${rect.width}, height=${rect.height})`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The FLIP invert (REQ-LAYOUT-002): the transform that makes an element currently laid out at `after` appear
|
|
44
|
+
* at `before`. Scaling is about the top-left origin, so the top-left stays put under scale and the translate
|
|
45
|
+
* is the raw origin delta. `applyTransformToRect(invert(b, a), a)` reproduces `b` exactly.
|
|
46
|
+
*/
|
|
47
|
+
// alloc-ok: js-thread — record-building API for JS-side callers (session.transform, web engine,
|
|
48
|
+
// verification); the UI frame path inverts field-wise inside the registry's readInto.
|
|
49
|
+
export function invert(before, after) {
|
|
50
|
+
'worklet';
|
|
51
|
+
assertMeasurable(after);
|
|
52
|
+
return {
|
|
53
|
+
translateX: before.x - after.x,
|
|
54
|
+
translateY: before.y - after.y,
|
|
55
|
+
scaleX: before.width / after.width,
|
|
56
|
+
scaleY: before.height / after.height,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The projection correction at animation progress `p` (REQ-LAYOUT-002/017): lerp each component from the
|
|
61
|
+
* invert at `p=0` (element at `before`) to identity at `p=1` (element on the host layout). Recomputed from
|
|
62
|
+
* the cached before/after and the current progress — never by re-measuring.
|
|
63
|
+
*/
|
|
64
|
+
// alloc-ok: js-thread — record-building API for JS-side callers and verification; not on the UI
|
|
65
|
+
// frame path (readInto owns the per-frame projection math).
|
|
66
|
+
export function projectAtProgress(before, after, progress) {
|
|
67
|
+
'worklet';
|
|
68
|
+
const from = invert(before, after);
|
|
69
|
+
return {
|
|
70
|
+
translateX: lerp(from.translateX, IDENTITY_TRANSFORM.translateX, progress),
|
|
71
|
+
translateY: lerp(from.translateY, IDENTITY_TRANSFORM.translateY, progress),
|
|
72
|
+
scaleX: lerp(from.scaleX, IDENTITY_TRANSFORM.scaleX, progress),
|
|
73
|
+
scaleY: lerp(from.scaleY, IDENTITY_TRANSFORM.scaleY, progress),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Apply a transform (translate + scale about the rect's top-left) to a rect, yielding the visual rect. Used
|
|
78
|
+
* for assertions and for the retarget seam's `currentVisual` (M4). Translate is in the unscaled frame; size
|
|
79
|
+
* scales in place.
|
|
80
|
+
*/
|
|
81
|
+
// alloc-ok: js-thread — record-building API for assertions and the retarget seam's JS-side
|
|
82
|
+
// currentVisual; not on the UI frame path.
|
|
83
|
+
export function applyTransformToRect(t, rect) {
|
|
84
|
+
'worklet';
|
|
85
|
+
return {
|
|
86
|
+
x: rect.x + t.translateX,
|
|
87
|
+
y: rect.y + t.translateY,
|
|
88
|
+
width: rect.width * t.scaleX,
|
|
89
|
+
height: rect.height * t.scaleY,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** Whether a transform is within the settle epsilons of identity (translate ≤ pos eps, scale ≤ scale eps). */
|
|
93
|
+
export function isIdentity(t, epsPos = EPSILON_POS, epsScale = EPSILON_SCALE) {
|
|
94
|
+
return (Math.abs(t.translateX) < epsPos &&
|
|
95
|
+
Math.abs(t.translateY) < epsPos &&
|
|
96
|
+
Math.abs(t.scaleX - 1) < epsScale &&
|
|
97
|
+
Math.abs(t.scaleY - 1) < epsScale);
|
|
98
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
'worklet';
|
|
2
|
+
// @frame-path — executes inside the UI-runtime frame step; allocation-gated (REQ-DRIVER-021).
|
|
3
|
+
// SPEC-LAYOUT §2/§4a — the projection SESSION (REQ-LAYOUT-002/011/018/019). Drives an element's visual rect
|
|
4
|
+
// to its host-computed layout through the reused SPRING, exposing the transform-only correction the substrate
|
|
5
|
+
// executes. A mid-flight host layout change retargets from the current visual position AND velocity (C0/C1);
|
|
6
|
+
// a null read fails closed to identity + unsettled; settle is measurement-availability. Host-agnostic — no
|
|
7
|
+
// driver/React/Fabric import; drives the injected MotionGraph + built SPRING generators, no forked math.
|
|
8
|
+
//
|
|
9
|
+
// Modeling decision (deviates from the build packet's single-progress sketch): the visual rect is animated
|
|
10
|
+
// PER COMPONENT — four motion values (x, y, width, height) each springing toward the after-rect. A single
|
|
11
|
+
// scalar progress cannot preserve velocity continuity across ALL axes on a retarget (the progress→visual-
|
|
12
|
+
// velocity ratio differs per component), whereas per-component springs get C0/C1 for free from the graph's
|
|
13
|
+
// seed-from-live (REQ-API-003) — exactly as GESTURE's owned retarget does. transform() is then the FLIP
|
|
14
|
+
// invert of the live visual rect against the after rect, which is identity at settle (REQ-LAYOUT-002).
|
|
15
|
+
import { resolveSpringGenerator } from "../transition.js";
|
|
16
|
+
import { timingGenerator } from "../timing.js";
|
|
17
|
+
import { IDENTITY_TRANSFORM, invert } from "./projection.js";
|
|
18
|
+
// Near-critical projection spring (physics ⇒ carries the seeded velocity, REQ-SPRING-007) — minimal overshoot
|
|
19
|
+
// for a layout settle. Config constant, not an inline literal; refinable — the exact feel binds cross-engine
|
|
20
|
+
// parity at M2, not the M1 continuity/identity/settle invariants (§7 vetoable).
|
|
21
|
+
const LAYOUT_PROJECTION_SPRING = { stiffness: 550, damping: 45 };
|
|
22
|
+
// alloc-ok: construction — one session per layout-identity pairing; the per-frame surface is
|
|
23
|
+
// sync()/settledAgainst()/writeVisualInto(), which allocate nothing.
|
|
24
|
+
export function createProjectionSession(options) {
|
|
25
|
+
'worklet';
|
|
26
|
+
const { graph } = options;
|
|
27
|
+
const spring = options.spring ?? LAYOUT_PROJECTION_SPRING;
|
|
28
|
+
const timing = options.timing;
|
|
29
|
+
// The visual rect, tracked as four independently-animated motion values (see the modeling note above).
|
|
30
|
+
const vx = graph.motionValue(0);
|
|
31
|
+
const vy = graph.motionValue(0);
|
|
32
|
+
const vw = graph.motionValue(0);
|
|
33
|
+
const vh = graph.motionValue(0);
|
|
34
|
+
let state = 'idle';
|
|
35
|
+
let measured = false;
|
|
36
|
+
let currentAfter = null;
|
|
37
|
+
let anims = [];
|
|
38
|
+
// Held while unmeasured (a null read): there is no in-flight animation to keep the graph unsettled, yet the
|
|
39
|
+
// element is not settled — it is waiting for a valid measurement (REQ-LAYOUT-019 / G-INV-7 analog).
|
|
40
|
+
let measureHandle;
|
|
41
|
+
function acquireMeasureHandle() {
|
|
42
|
+
if (measureHandle === undefined)
|
|
43
|
+
measureHandle = graph.registerActivity();
|
|
44
|
+
}
|
|
45
|
+
function releaseMeasureHandle() {
|
|
46
|
+
measureHandle?.release();
|
|
47
|
+
measureHandle = undefined;
|
|
48
|
+
}
|
|
49
|
+
function stopAnims() {
|
|
50
|
+
// Indexed + length=0: sync() reaches this on the settle frame, so a for..of iterator and a
|
|
51
|
+
// fresh array are frame-executed garbage (round-7 major 3; REQ-DRIVER-021).
|
|
52
|
+
for (let i = 0; i < anims.length; i++)
|
|
53
|
+
anims[i].stop();
|
|
54
|
+
anims.length = 0;
|
|
55
|
+
}
|
|
56
|
+
// Drive each visual channel toward the after-rect. Spring path: graph seeds {from, velocity} for
|
|
57
|
+
// C0/C1 retarget (REQ-CORE-002 / REQ-API-003). Timing path (family-3): duration/ease tween — seed
|
|
58
|
+
// velocity is ignored by timingGenerator (REQ-TIMING-001); retarget reseeds from live value.
|
|
59
|
+
// Do NOT stop the prior animation first — stop() clears animated velocity and breaks spring C1.
|
|
60
|
+
// alloc-ok: lifecycle-edge — flight start/retarget only (begin/retarget), never per frame.
|
|
61
|
+
function driveTo(after) {
|
|
62
|
+
if (timing !== undefined) {
|
|
63
|
+
anims = [
|
|
64
|
+
graph.animate(vx, timingGenerator(after.x, timing)),
|
|
65
|
+
graph.animate(vy, timingGenerator(after.y, timing)),
|
|
66
|
+
graph.animate(vw, timingGenerator(after.width, timing)),
|
|
67
|
+
graph.animate(vh, timingGenerator(after.height, timing)),
|
|
68
|
+
];
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
anims = [
|
|
72
|
+
graph.animate(vx, resolveSpringGenerator(after.x, spring)),
|
|
73
|
+
graph.animate(vy, resolveSpringGenerator(after.y, spring)),
|
|
74
|
+
graph.animate(vw, resolveSpringGenerator(after.width, spring)),
|
|
75
|
+
graph.animate(vh, resolveSpringGenerator(after.height, spring)),
|
|
76
|
+
];
|
|
77
|
+
}
|
|
78
|
+
// alloc-ok: js-thread — record-building read for the JS-side API (transform/visualRect); the
|
|
79
|
+
// frame path uses writeVisualInto/settledAgainst.
|
|
80
|
+
function visual() {
|
|
81
|
+
return { x: vx.get(), y: vy.get(), width: vw.get(), height: vh.get() };
|
|
82
|
+
}
|
|
83
|
+
// Reconcile the internal state to the graph: once the projection springs settle (graph settled) while
|
|
84
|
+
// measured, the session returns to idle. Every state-dependent entry point reconciles first — the getter
|
|
85
|
+
// AND the mutators — so post-settle reads and a subsequent retarget agree on the state (the GESTURE review
|
|
86
|
+
// lesson: a getter-only reconcile leaves the mutators acting on a stale field).
|
|
87
|
+
function sync() {
|
|
88
|
+
if (state === 'projecting' && measured && graph.isSettled()) {
|
|
89
|
+
stopAnims();
|
|
90
|
+
state = 'idle';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function failClosed(after) {
|
|
94
|
+
// A null read: unmeasured ⇒ identity + unsettled, retried next epoch. Never a garbage delta (REQ-LAYOUT-011).
|
|
95
|
+
stopAnims();
|
|
96
|
+
measured = false;
|
|
97
|
+
currentAfter = after;
|
|
98
|
+
acquireMeasureHandle();
|
|
99
|
+
state = 'projecting';
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
begin(token, before, after) {
|
|
103
|
+
void token; // identity binding is the measure tracker's concern (M3); the session takes resolved rects
|
|
104
|
+
sync();
|
|
105
|
+
if (before === null || after === null) {
|
|
106
|
+
failClosed(after);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Fresh projection: jump the visual rect to `before` (velocity 0) and spring to `after`.
|
|
110
|
+
releaseMeasureHandle();
|
|
111
|
+
vx.jump(before.x);
|
|
112
|
+
vy.jump(before.y);
|
|
113
|
+
vw.jump(before.width);
|
|
114
|
+
vh.jump(before.height);
|
|
115
|
+
measured = true;
|
|
116
|
+
currentAfter = after;
|
|
117
|
+
driveTo(after);
|
|
118
|
+
state = 'projecting';
|
|
119
|
+
},
|
|
120
|
+
retarget(after) {
|
|
121
|
+
sync();
|
|
122
|
+
if (state !== 'projecting' || !measured) {
|
|
123
|
+
// Nothing in flight to retarget. Fail loud, consistent with the total state machine (never a silent
|
|
124
|
+
// no-op) — the caller must begin() a measured projection first.
|
|
125
|
+
throw new Error(`layout: retarget() with no active measured projection (state='${state}', measured=${measured}) — begin() first`);
|
|
126
|
+
}
|
|
127
|
+
if (after === null) {
|
|
128
|
+
failClosed(after);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
// Continue from the LIVE visual position (+ velocity on spring path) toward the new target —
|
|
132
|
+
// no jump; the graph re-seeds each channel from live state ⇒ C0/C1 on spring (REQ-LAYOUT-018).
|
|
133
|
+
currentAfter = after;
|
|
134
|
+
driveTo(after);
|
|
135
|
+
},
|
|
136
|
+
settleAt(after) {
|
|
137
|
+
// Pin type:false — no multi-frame flight. Jump every channel to the post-layout rect and
|
|
138
|
+
// drop spring work so settledAgainst/isSettled read true on the same edge (REQ-API-035).
|
|
139
|
+
releaseMeasureHandle();
|
|
140
|
+
stopAnims();
|
|
141
|
+
vx.jump(after.x);
|
|
142
|
+
vy.jump(after.y);
|
|
143
|
+
vw.jump(after.width);
|
|
144
|
+
vh.jump(after.height);
|
|
145
|
+
measured = true;
|
|
146
|
+
currentAfter = after;
|
|
147
|
+
state = 'idle';
|
|
148
|
+
},
|
|
149
|
+
transform() {
|
|
150
|
+
sync();
|
|
151
|
+
if (!measured || currentAfter === null)
|
|
152
|
+
return IDENTITY_TRANSFORM;
|
|
153
|
+
return invert(visual(), currentAfter);
|
|
154
|
+
},
|
|
155
|
+
visualRect() {
|
|
156
|
+
sync();
|
|
157
|
+
return measured ? visual() : null;
|
|
158
|
+
},
|
|
159
|
+
// alloc-ok: js-thread — record-building read for the JS-side API; the frame path uses
|
|
160
|
+
// settledAgainst's in-place comparison.
|
|
161
|
+
velocity() {
|
|
162
|
+
return {
|
|
163
|
+
x: vx.getVelocity(),
|
|
164
|
+
y: vy.getVelocity(),
|
|
165
|
+
width: vw.getVelocity(),
|
|
166
|
+
height: vh.getVelocity(),
|
|
167
|
+
};
|
|
168
|
+
},
|
|
169
|
+
settledAgainst(target, epsPos, epsVel) {
|
|
170
|
+
sync();
|
|
171
|
+
if (!measured)
|
|
172
|
+
return false;
|
|
173
|
+
return (Math.abs(vx.get() - target.x) < epsPos &&
|
|
174
|
+
Math.abs(vy.get() - target.y) < epsPos &&
|
|
175
|
+
Math.abs(vw.get() - target.width) < epsPos &&
|
|
176
|
+
Math.abs(vh.get() - target.height) < epsPos &&
|
|
177
|
+
Math.abs(vx.getVelocity()) < epsVel &&
|
|
178
|
+
Math.abs(vy.getVelocity()) < epsVel &&
|
|
179
|
+
Math.abs(vw.getVelocity()) < epsVel &&
|
|
180
|
+
Math.abs(vh.getVelocity()) < epsVel);
|
|
181
|
+
},
|
|
182
|
+
writeVisualInto(out) {
|
|
183
|
+
sync();
|
|
184
|
+
if (!measured)
|
|
185
|
+
return false;
|
|
186
|
+
out.x = vx.get();
|
|
187
|
+
out.y = vy.get();
|
|
188
|
+
out.width = vw.get();
|
|
189
|
+
out.height = vh.get();
|
|
190
|
+
return true;
|
|
191
|
+
},
|
|
192
|
+
measured: () => measured,
|
|
193
|
+
isSettled: () => graph.isSettled(),
|
|
194
|
+
state: () => {
|
|
195
|
+
sync();
|
|
196
|
+
return state;
|
|
197
|
+
},
|
|
198
|
+
dispose() {
|
|
199
|
+
stopAnims();
|
|
200
|
+
releaseMeasureHandle();
|
|
201
|
+
measured = false;
|
|
202
|
+
currentAfter = null;
|
|
203
|
+
state = 'idle';
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|