@field/cascade 0.2.22 → 0.3.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/AGENTS.md +47 -7
- package/ARCHITECTURE.md +30 -2
- package/CHANGELOG.md +107 -0
- package/DESIGN.md +10 -5
- package/README.md +103 -7
- package/dist/assets/CodeEditor-D22fVi-4.js +623 -0
- package/dist/assets/CodePanel-DPNEYfcR.js +2 -0
- package/dist/assets/CustomNodeDialog-DeHCAvgb.js +1 -0
- package/dist/assets/{ExportDialog-DWN4nhXI.js → ExportDialog-BJu2Bpvv.js} +2 -2
- package/dist/assets/ProjectSettings-DWpVtWEx.js +2 -0
- package/dist/assets/SettingsDialog-za00IS3L.js +1 -0
- package/dist/assets/VersionHistoryDialog-DTMGhI0_.js +3 -0
- package/dist/assets/{core-Bji4aZUU.js → core-JsyKMO1k.js} +29 -38
- package/dist/assets/{cssMode-B6Z_Q1Kh.js → cssMode-emdRheFK.js} +1 -1
- package/dist/assets/{editor.api-jMoW_43X.js → editor.api-xHX-fMSo.js} +1 -1
- package/dist/assets/{freemarker2-DvvPV6m5.js → freemarker2-C3M8_Gs3.js} +1 -1
- package/dist/assets/geo-BAR6lQp5.js +1 -0
- package/dist/assets/{handlebars-B8rfuSIa.js → handlebars-CBMurtFk.js} +1 -1
- package/dist/assets/{html-DyHm6JCp.js → html-xbLQwknw.js} +1 -1
- package/dist/assets/{htmlMode-E9XLWE5u.js → htmlMode-2wKdDTon.js} +1 -1
- package/dist/assets/{image-BUcQC7PG.js → image-i1j6nWKe.js} +26 -40
- package/dist/assets/index-BDV5xGBZ.css +1 -0
- package/dist/assets/index-CMcH8AgP.js +212 -0
- package/dist/assets/index-client-BoJgsN9M.js +1 -0
- package/dist/assets/{javascript-C_bM5PCN.js → javascript-DJaOo_2I.js} +1 -1
- package/dist/assets/{jsonMode-BdyVS7G4.js → jsonMode-Bheas8_0.js} +1 -1
- package/dist/assets/{legacy-DIu3XRwl.js → legacy-CAj2_7QE.js} +2 -2
- package/dist/assets/{liquid-CDkn3spA.js → liquid-BCj_BGbM.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-AB5DMAsr.js → lspLanguageFeatures-BfIsx_xz.js} +1 -1
- package/dist/assets/{mdx-BvH_NjDN.js → mdx-DwiQDiVO.js} +1 -1
- package/dist/assets/{nodeTypeUtils-CweA3NHd.js → nodeTypeUtils-BBA1JqHx.js} +1 -1
- package/dist/assets/{python-B7uii4fi.js → python-BjrB5nqj.js} +1 -1
- package/dist/assets/{razor-Z48fWdJc.js → razor-CfoBmGDs.js} +1 -1
- package/dist/assets/{tsMode-BDgacVG4.js → tsMode-BcEoMSxp.js} +1 -1
- package/dist/assets/{typescript-Derb6Oqa.js → typescript-DIg7TEmQ.js} +1 -1
- package/dist/assets/{workers-BtoMX_pm.js → workers-vXYuwtPw.js} +1 -1
- package/dist/assets/{xml-B1CaFSrG.js → xml-DexLQIQy.js} +1 -1
- package/dist/assets/{yaml-HmIeA0KH.js → yaml-DUnD_1dE.js} +1 -1
- package/dist/cli/index.js +16467 -10200
- package/dist/cli/runtime/gpu.ts +76 -0
- package/dist/cli/runtime/io.ts +93 -7
- package/dist/contracts/animation.d.ts +40 -0
- package/dist/contracts/animation.js +14 -0
- package/dist/contracts/capabilities.d.ts +69 -6
- package/dist/contracts/definition.d.ts +60 -2
- package/dist/contracts/document.d.ts +15 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/schema.js +8 -5
- package/dist/contracts/validate.d.ts +10 -1
- package/dist/contracts/validate.js +20 -3
- package/dist/gpu.d.ts +71 -0
- package/dist/gpu.js +53 -0
- package/dist/graphs/default.cascade +1 -1
- package/dist/index.html +5 -5
- package/dist/io.d.ts +196 -0
- package/dist/io.js +303 -0
- package/dist/net.d.ts +1 -0
- package/dist/net.js +28 -0
- package/dist/runtime/animation/channel.d.ts +108 -0
- package/dist/runtime/animation/channel.js +204 -0
- package/dist/runtime/animation/frameRange.d.ts +78 -0
- package/dist/runtime/animation/frameRange.js +76 -0
- package/dist/runtime/animation/index.d.ts +10 -0
- package/dist/runtime/animation/index.js +8 -0
- package/dist/runtime/builtins/core/random.d.ts +1 -1
- package/dist/runtime/builtins/core/remap.d.ts +1 -1
- package/dist/runtime/builtins/geo/copyToPoints.d.ts +1 -1
- package/dist/runtime/builtins/geo/copyToPoints.js +1 -1
- package/dist/runtime/builtins/geo/generators.d.ts +1 -1
- package/dist/runtime/builtins/geo/merge.d.ts +1 -1
- package/dist/runtime/builtins/geo/svgExport.d.ts +1 -1
- package/dist/runtime/builtins/geo/svgExport.js +1 -1
- package/dist/runtime/builtins/geo/transform.d.ts +1 -1
- package/dist/runtime/builtins/geo/transform.js +1 -1
- package/dist/runtime/definition/architecture.d.ts +1 -1
- package/dist/runtime/definition/extract.d.ts +1 -1
- package/dist/runtime/definition/extract.js +1 -1
- package/dist/runtime/error.d.ts +1 -1
- package/dist/runtime/expressions/evaluator.d.ts +53 -0
- package/dist/runtime/expressions/evaluator.js +193 -0
- package/dist/runtime/expressions/index.d.ts +15 -0
- package/dist/runtime/expressions/index.js +12 -0
- package/dist/runtime/expressions/math.d.ts +75 -0
- package/dist/runtime/expressions/math.js +140 -0
- package/dist/runtime/expressions/preprocess.d.ts +33 -0
- package/dist/runtime/expressions/preprocess.js +102 -0
- package/dist/runtime/expressions/scope.d.ts +27 -0
- package/dist/runtime/expressions/scope.js +10 -0
- package/dist/runtime/expressions/time.d.ts +54 -0
- package/dist/runtime/expressions/time.js +71 -0
- package/dist/runtime/expressions/types.d.ts +66 -0
- package/dist/runtime/expressions/types.js +9 -0
- package/dist/runtime/geometry/attributes.d.ts +2 -2
- package/dist/runtime/geometry/attributes.js +1 -1
- package/dist/runtime/geometry/builder.d.ts +1 -1
- package/dist/runtime/geometry/builder.js +1 -1
- package/dist/runtime/geometry/copy.d.ts +1 -1
- package/dist/runtime/geometry/copy.js +1 -1
- package/dist/runtime/geometry/generate.d.ts +1 -1
- package/dist/runtime/geometry/groups.d.ts +1 -1
- package/dist/runtime/geometry/groups.js +1 -1
- package/dist/runtime/geometry/matrix.d.ts +1 -1
- package/dist/runtime/geometry/merge.d.ts +1 -1
- package/dist/runtime/geometry/merge.js +1 -1
- package/dist/runtime/geometry/primitives.d.ts +1 -1
- package/dist/runtime/geometry/primitives.js +1 -1
- package/dist/runtime/geometry/promote.d.ts +1 -1
- package/dist/runtime/geometry/promote.js +1 -1
- package/dist/runtime/geometry/resample.d.ts +1 -1
- package/dist/runtime/geometry/resample.js +1 -1
- package/dist/runtime/geometry/svg.d.ts +1 -1
- package/dist/runtime/geometry/svg.js +1 -1
- package/dist/runtime/geometry/transform.d.ts +1 -1
- package/dist/runtime/geometry/transform.js +1 -1
- package/dist/runtime/index.d.ts +4 -1
- package/dist/runtime/index.js +4 -1
- package/dist/runtime/params/animator.d.ts +74 -0
- package/dist/runtime/params/animator.js +137 -0
- package/dist/runtime/params/index.d.ts +11 -0
- package/dist/runtime/params/index.js +9 -0
- package/dist/runtime/params/resolve.d.ts +73 -0
- package/dist/runtime/params/resolve.js +62 -0
- package/dist/runtime/runtime.d.ts +42 -0
- package/dist/runtime/runtime.js +351 -29
- package/dist/runtime/types.d.ts +32 -6
- package/dist/shell.d.ts +3 -3
- package/doc/NODE_REFERENCE.md +395 -0
- package/doc/PROJECT_AUTHORING.md +148 -6
- package/package.json +30 -3
- package/dist/assets/CodeEditor-DoaNEUaM.js +0 -641
- package/dist/assets/CodePanel-B0_2XJ-t.js +0 -2
- package/dist/assets/CustomNodeDialog-026lzecD.js +0 -1
- package/dist/assets/ProjectSettings-BzT164Yx.js +0 -2
- package/dist/assets/SettingsDialog-DmHb1V0r.js +0 -1
- package/dist/assets/VersionHistoryDialog-hH6VsXLX.js +0 -3
- package/dist/assets/geo-CXmCtzMM.js +0 -2
- package/dist/assets/index-BXYXSdgh.js +0 -224
- package/dist/assets/index-CrNke7SW.css +0 -1
- package/dist/assets/index-client-DN_FvjN8.js +0 -1
- package/dist/assets/quill-DjZ69wL_.js +0 -121
- package/dist/cli/index.js.map +0 -7
- package/dist/contracts/capabilities.d.ts.map +0 -1
- package/dist/contracts/capabilities.js.map +0 -1
- package/dist/contracts/color.d.ts.map +0 -1
- package/dist/contracts/color.js.map +0 -1
- package/dist/contracts/definition.d.ts.map +0 -1
- package/dist/contracts/definition.js.map +0 -1
- package/dist/contracts/document.d.ts.map +0 -1
- package/dist/contracts/document.js.map +0 -1
- package/dist/contracts/geometry.d.ts.map +0 -1
- package/dist/contracts/geometry.js.map +0 -1
- package/dist/contracts/index.d.ts.map +0 -1
- package/dist/contracts/index.js.map +0 -1
- package/dist/contracts/schema.d.ts.map +0 -1
- package/dist/contracts/schema.js.map +0 -1
- package/dist/contracts/validate.d.ts.map +0 -1
- package/dist/contracts/validate.js.map +0 -1
- package/dist/contracts/values.d.ts.map +0 -1
- package/dist/contracts/values.js.map +0 -1
- package/dist/graphs/default-orig.cascade +0 -128
- package/dist/runtime/browser.d.ts.map +0 -1
- package/dist/runtime/browser.js.map +0 -1
- package/dist/runtime/builtins/core/index.d.ts.map +0 -1
- package/dist/runtime/builtins/core/index.js.map +0 -1
- package/dist/runtime/builtins/core/random.d.ts.map +0 -1
- package/dist/runtime/builtins/core/random.js.map +0 -1
- package/dist/runtime/builtins/core/remap.d.ts.map +0 -1
- package/dist/runtime/builtins/core/remap.js.map +0 -1
- package/dist/runtime/builtins/core/routing.d.ts.map +0 -1
- package/dist/runtime/builtins/core/routing.js.map +0 -1
- package/dist/runtime/builtins/core/structural.d.ts.map +0 -1
- package/dist/runtime/builtins/core/structural.js.map +0 -1
- package/dist/runtime/builtins/geo/copyToPoints.d.ts.map +0 -1
- package/dist/runtime/builtins/geo/copyToPoints.js.map +0 -1
- package/dist/runtime/builtins/geo/definitions.d.ts.map +0 -1
- package/dist/runtime/builtins/geo/definitions.js.map +0 -1
- package/dist/runtime/builtins/geo/generators.d.ts.map +0 -1
- package/dist/runtime/builtins/geo/generators.js.map +0 -1
- package/dist/runtime/builtins/geo/index.d.ts.map +0 -1
- package/dist/runtime/builtins/geo/index.js.map +0 -1
- package/dist/runtime/builtins/geo/merge.d.ts.map +0 -1
- package/dist/runtime/builtins/geo/merge.js.map +0 -1
- package/dist/runtime/builtins/geo/namespace.d.ts.map +0 -1
- package/dist/runtime/builtins/geo/namespace.js.map +0 -1
- package/dist/runtime/builtins/geo/svgExport.d.ts.map +0 -1
- package/dist/runtime/builtins/geo/svgExport.js.map +0 -1
- package/dist/runtime/builtins/geo/transform.d.ts.map +0 -1
- package/dist/runtime/builtins/geo/transform.js.map +0 -1
- package/dist/runtime/builtins/index.d.ts.map +0 -1
- package/dist/runtime/builtins/index.js.map +0 -1
- package/dist/runtime/definition/architecture.d.ts.map +0 -1
- package/dist/runtime/definition/architecture.js.map +0 -1
- package/dist/runtime/definition/extract.d.ts.map +0 -1
- package/dist/runtime/definition/extract.js.map +0 -1
- package/dist/runtime/error.d.ts.map +0 -1
- package/dist/runtime/error.js.map +0 -1
- package/dist/runtime/geometry/attributes.d.ts.map +0 -1
- package/dist/runtime/geometry/attributes.js.map +0 -1
- package/dist/runtime/geometry/builder.d.ts.map +0 -1
- package/dist/runtime/geometry/builder.js.map +0 -1
- package/dist/runtime/geometry/copy.d.ts.map +0 -1
- package/dist/runtime/geometry/copy.js.map +0 -1
- package/dist/runtime/geometry/generate.d.ts.map +0 -1
- package/dist/runtime/geometry/generate.js.map +0 -1
- package/dist/runtime/geometry/groups.d.ts.map +0 -1
- package/dist/runtime/geometry/groups.js.map +0 -1
- package/dist/runtime/geometry/index.d.ts.map +0 -1
- package/dist/runtime/geometry/index.js.map +0 -1
- package/dist/runtime/geometry/matrix.d.ts.map +0 -1
- package/dist/runtime/geometry/matrix.js.map +0 -1
- package/dist/runtime/geometry/merge.d.ts.map +0 -1
- package/dist/runtime/geometry/merge.js.map +0 -1
- package/dist/runtime/geometry/primitives.d.ts.map +0 -1
- package/dist/runtime/geometry/primitives.js.map +0 -1
- package/dist/runtime/geometry/promote.d.ts.map +0 -1
- package/dist/runtime/geometry/promote.js.map +0 -1
- package/dist/runtime/geometry/resample.d.ts.map +0 -1
- package/dist/runtime/geometry/resample.js.map +0 -1
- package/dist/runtime/geometry/svg.d.ts.map +0 -1
- package/dist/runtime/geometry/svg.js.map +0 -1
- package/dist/runtime/geometry/transform.d.ts.map +0 -1
- package/dist/runtime/geometry/transform.js.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/node.d.ts.map +0 -1
- package/dist/runtime/node.js.map +0 -1
- package/dist/runtime/runtime.d.ts.map +0 -1
- package/dist/runtime/runtime.js.map +0 -1
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js.map +0 -1
- package/dist/shell.js.map +0 -7
- package/dist/stage.js.map +0 -7
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyframe channels, neutral runtime.
|
|
3
|
+
*
|
|
4
|
+
* A channel is the third way a parameter becomes a value, beside a literal and
|
|
5
|
+
* an expression. It is an ordered list of keys and a rule for what happens
|
|
6
|
+
* between them, and it is sampled — never pushed. The host asks for the value
|
|
7
|
+
* at a frame; nothing here holds a clock, a graph or a node, which is what
|
|
8
|
+
* keeps the whole thing deterministic and testable on its own.
|
|
9
|
+
*
|
|
10
|
+
* `sin($T)` and a drawn curve are the same shape: one written, one drawn.
|
|
11
|
+
*
|
|
12
|
+
* Every function here is pure. A channel is treated as immutable — `setKey`
|
|
13
|
+
* and `deleteKey` return a new channel rather than editing one in place — so a
|
|
14
|
+
* caller can hold a channel across a cook without it changing underneath.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* What happens between a key and the next one. The mode belongs to the key on
|
|
18
|
+
* the LEFT of a segment: it describes how the value leaves that key.
|
|
19
|
+
*
|
|
20
|
+
* constant — hold the left value until the next key. Steps, not motion
|
|
21
|
+
* linear — straight line. Constant velocity, visible corners at the keys
|
|
22
|
+
* smooth — Hermite with automatic tangents. The default, because linear
|
|
23
|
+
* everywhere is how animation looks when nobody chose
|
|
24
|
+
*/
|
|
25
|
+
export type Interpolation = "constant" | "linear" | "smooth";
|
|
26
|
+
/** Smooth, deliberately: an unset default of `linear` animates like a robot. */
|
|
27
|
+
export declare const DEFAULT_INTERPOLATION: Interpolation;
|
|
28
|
+
export declare function isInterpolation(value: unknown): value is Interpolation;
|
|
29
|
+
/** One key. `frame` may be fractional; `value` is a plain number. */
|
|
30
|
+
export interface Keyframe {
|
|
31
|
+
readonly frame: number;
|
|
32
|
+
readonly value: number;
|
|
33
|
+
readonly interpolation: Interpolation;
|
|
34
|
+
}
|
|
35
|
+
/** A key as a caller may hand it in — interpolation optional. */
|
|
36
|
+
export interface KeyframeInput {
|
|
37
|
+
readonly frame: number;
|
|
38
|
+
readonly value: number;
|
|
39
|
+
readonly interpolation?: Interpolation | string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* An animated parameter. `keys` is always sorted by frame and holds at most
|
|
43
|
+
* one key per frame — `createChannel` is the only way that invariant is
|
|
44
|
+
* established, and every other function here preserves it.
|
|
45
|
+
*/
|
|
46
|
+
export interface Channel {
|
|
47
|
+
readonly keys: readonly Keyframe[];
|
|
48
|
+
}
|
|
49
|
+
/** The empty channel. Sampling it returns undefined, so a caller falls back. */
|
|
50
|
+
export declare const EMPTY_CHANNEL: Channel;
|
|
51
|
+
/**
|
|
52
|
+
* Build a channel from keys in any order, with anything unusable dropped.
|
|
53
|
+
*
|
|
54
|
+
* Unordered input is sorted rather than rejected — a file edited by hand and a
|
|
55
|
+
* UI that appends are both legitimate sources. A duplicate frame keeps the
|
|
56
|
+
* LAST key given, which is what "set this key again" means.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createChannel(keys?: readonly KeyframeInput[]): Channel;
|
|
59
|
+
export declare function isChannel(value: unknown): value is Channel;
|
|
60
|
+
/** A channel with no keys resolves to nothing, so the raw value still applies. */
|
|
61
|
+
export declare function isEmptyChannel(channel: Channel | null | undefined): boolean;
|
|
62
|
+
export declare function keyAt(channel: Channel, frame: number): Keyframe | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Set a key, replacing any key already on that frame.
|
|
65
|
+
*
|
|
66
|
+
* Re-keying an existing frame keeps that key's interpolation unless a new one
|
|
67
|
+
* is given: dragging a value should not silently change its easing.
|
|
68
|
+
*/
|
|
69
|
+
export declare function setKey(channel: Channel | null | undefined, frame: number, value: number, interpolation?: Interpolation): Channel;
|
|
70
|
+
/** Remove the key on that frame. Removing the last key leaves an empty channel. */
|
|
71
|
+
export declare function deleteKey(channel: Channel | null | undefined, frame: number): Channel;
|
|
72
|
+
/** Change one key's outgoing interpolation without touching its value. */
|
|
73
|
+
export declare function setKeyInterpolation(channel: Channel | null | undefined, frame: number, interpolation: Interpolation): Channel;
|
|
74
|
+
/**
|
|
75
|
+
* The value at any frame, fractional included.
|
|
76
|
+
*
|
|
77
|
+
* Returns undefined for a channel with no keys — that is the signal to fall
|
|
78
|
+
* back to the parameter's raw value rather than to invent a zero.
|
|
79
|
+
*
|
|
80
|
+
* Outside the key range the end values are HELD, in both directions. No
|
|
81
|
+
* extrapolation: a curve that keeps climbing past its last key is never what
|
|
82
|
+
* anyone meant, and it is the failure that is hardest to see in a render.
|
|
83
|
+
*/
|
|
84
|
+
export declare function sampleChannel(channel: Channel | null | undefined, frame: number): number | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* The on-disk form. Interpolation is omitted when it is the default, so a
|
|
87
|
+
* plain channel reads as `{ "keys": [{ "frame": 1, "value": 0 }] }` and a diff
|
|
88
|
+
* shows easing changes rather than noise.
|
|
89
|
+
*
|
|
90
|
+
* Structurally identical to `CascadeSerializedChannel` in the contracts
|
|
91
|
+
* package, which is where the document shape is declared. It is restated here
|
|
92
|
+
* so this module keeps depending on nothing.
|
|
93
|
+
*/
|
|
94
|
+
export interface SerializedKeyframe {
|
|
95
|
+
frame: number;
|
|
96
|
+
value: number;
|
|
97
|
+
interpolation?: Interpolation;
|
|
98
|
+
}
|
|
99
|
+
export interface SerializedChannel {
|
|
100
|
+
keys: SerializedKeyframe[];
|
|
101
|
+
}
|
|
102
|
+
export declare function serializeChannel(channel: Channel | null | undefined): SerializedChannel | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Read a channel back. Anything unrecognised yields the empty channel rather
|
|
105
|
+
* than an exception — a bad channel in one parameter must not fail a load.
|
|
106
|
+
*/
|
|
107
|
+
export declare function deserializeChannel(data: unknown): Channel;
|
|
108
|
+
//# sourceMappingURL=channel.d.ts.map
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyframe channels, neutral runtime.
|
|
3
|
+
*
|
|
4
|
+
* A channel is the third way a parameter becomes a value, beside a literal and
|
|
5
|
+
* an expression. It is an ordered list of keys and a rule for what happens
|
|
6
|
+
* between them, and it is sampled — never pushed. The host asks for the value
|
|
7
|
+
* at a frame; nothing here holds a clock, a graph or a node, which is what
|
|
8
|
+
* keeps the whole thing deterministic and testable on its own.
|
|
9
|
+
*
|
|
10
|
+
* `sin($T)` and a drawn curve are the same shape: one written, one drawn.
|
|
11
|
+
*
|
|
12
|
+
* Every function here is pure. A channel is treated as immutable — `setKey`
|
|
13
|
+
* and `deleteKey` return a new channel rather than editing one in place — so a
|
|
14
|
+
* caller can hold a channel across a cook without it changing underneath.
|
|
15
|
+
*/
|
|
16
|
+
/** Smooth, deliberately: an unset default of `linear` animates like a robot. */
|
|
17
|
+
export const DEFAULT_INTERPOLATION = "smooth";
|
|
18
|
+
const INTERPOLATIONS = ["constant", "linear", "smooth"];
|
|
19
|
+
export function isInterpolation(value) {
|
|
20
|
+
return typeof value === "string" && INTERPOLATIONS.includes(value);
|
|
21
|
+
}
|
|
22
|
+
/** The empty channel. Sampling it returns undefined, so a caller falls back. */
|
|
23
|
+
export const EMPTY_CHANNEL = Object.freeze({ keys: Object.freeze([]) });
|
|
24
|
+
/**
|
|
25
|
+
* Build a channel from keys in any order, with anything unusable dropped.
|
|
26
|
+
*
|
|
27
|
+
* Unordered input is sorted rather than rejected — a file edited by hand and a
|
|
28
|
+
* UI that appends are both legitimate sources. A duplicate frame keeps the
|
|
29
|
+
* LAST key given, which is what "set this key again" means.
|
|
30
|
+
*/
|
|
31
|
+
export function createChannel(keys = []) {
|
|
32
|
+
const byFrame = new Map();
|
|
33
|
+
for (const key of keys) {
|
|
34
|
+
if (!key)
|
|
35
|
+
continue;
|
|
36
|
+
const frame = Number(key.frame);
|
|
37
|
+
const value = Number(key.value);
|
|
38
|
+
if (!Number.isFinite(frame) || !Number.isFinite(value))
|
|
39
|
+
continue;
|
|
40
|
+
byFrame.set(frame, {
|
|
41
|
+
frame,
|
|
42
|
+
value,
|
|
43
|
+
interpolation: isInterpolation(key.interpolation) ? key.interpolation : DEFAULT_INTERPOLATION
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const sorted = [...byFrame.values()].sort((a, b) => a.frame - b.frame);
|
|
47
|
+
return { keys: sorted };
|
|
48
|
+
}
|
|
49
|
+
export function isChannel(value) {
|
|
50
|
+
return !!value && typeof value === "object" && Array.isArray(value.keys);
|
|
51
|
+
}
|
|
52
|
+
/** A channel with no keys resolves to nothing, so the raw value still applies. */
|
|
53
|
+
export function isEmptyChannel(channel) {
|
|
54
|
+
return !channel || channel.keys.length === 0;
|
|
55
|
+
}
|
|
56
|
+
export function keyAt(channel, frame) {
|
|
57
|
+
return channel.keys.find(key => key.frame === frame);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Set a key, replacing any key already on that frame.
|
|
61
|
+
*
|
|
62
|
+
* Re-keying an existing frame keeps that key's interpolation unless a new one
|
|
63
|
+
* is given: dragging a value should not silently change its easing.
|
|
64
|
+
*/
|
|
65
|
+
export function setKey(channel, frame, value, interpolation) {
|
|
66
|
+
const base = channel ?? EMPTY_CHANNEL;
|
|
67
|
+
if (!Number.isFinite(frame) || !Number.isFinite(value))
|
|
68
|
+
return createChannel(base.keys);
|
|
69
|
+
const existing = keyAt(base, frame);
|
|
70
|
+
const mode = interpolation ?? existing?.interpolation ?? DEFAULT_INTERPOLATION;
|
|
71
|
+
const kept = base.keys.filter(key => key.frame !== frame);
|
|
72
|
+
return createChannel([...kept, { frame, value, interpolation: mode }]);
|
|
73
|
+
}
|
|
74
|
+
/** Remove the key on that frame. Removing the last key leaves an empty channel. */
|
|
75
|
+
export function deleteKey(channel, frame) {
|
|
76
|
+
const base = channel ?? EMPTY_CHANNEL;
|
|
77
|
+
return createChannel(base.keys.filter(key => key.frame !== frame));
|
|
78
|
+
}
|
|
79
|
+
/** Change one key's outgoing interpolation without touching its value. */
|
|
80
|
+
export function setKeyInterpolation(channel, frame, interpolation) {
|
|
81
|
+
const base = channel ?? EMPTY_CHANNEL;
|
|
82
|
+
const existing = keyAt(base, frame);
|
|
83
|
+
if (!existing)
|
|
84
|
+
return createChannel(base.keys);
|
|
85
|
+
return setKey(base, frame, existing.value, interpolation);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The value at any frame, fractional included.
|
|
89
|
+
*
|
|
90
|
+
* Returns undefined for a channel with no keys — that is the signal to fall
|
|
91
|
+
* back to the parameter's raw value rather than to invent a zero.
|
|
92
|
+
*
|
|
93
|
+
* Outside the key range the end values are HELD, in both directions. No
|
|
94
|
+
* extrapolation: a curve that keeps climbing past its last key is never what
|
|
95
|
+
* anyone meant, and it is the failure that is hardest to see in a render.
|
|
96
|
+
*/
|
|
97
|
+
export function sampleChannel(channel, frame) {
|
|
98
|
+
if (!channel || channel.keys.length === 0)
|
|
99
|
+
return undefined;
|
|
100
|
+
const keys = channel.keys;
|
|
101
|
+
if (!Number.isFinite(frame))
|
|
102
|
+
return keys[0].value;
|
|
103
|
+
if (keys.length === 1)
|
|
104
|
+
return keys[0].value;
|
|
105
|
+
if (frame <= keys[0].frame)
|
|
106
|
+
return keys[0].value;
|
|
107
|
+
if (frame >= keys[keys.length - 1].frame)
|
|
108
|
+
return keys[keys.length - 1].value;
|
|
109
|
+
const i = segmentIndex(keys, frame);
|
|
110
|
+
const k0 = keys[i];
|
|
111
|
+
const k1 = keys[i + 1];
|
|
112
|
+
const span = k1.frame - k0.frame;
|
|
113
|
+
if (span <= 0)
|
|
114
|
+
return k1.value;
|
|
115
|
+
const t = (frame - k0.frame) / span;
|
|
116
|
+
switch (k0.interpolation) {
|
|
117
|
+
case "constant":
|
|
118
|
+
return k0.value;
|
|
119
|
+
case "linear":
|
|
120
|
+
return k0.value + (k1.value - k0.value) * t;
|
|
121
|
+
case "smooth":
|
|
122
|
+
default:
|
|
123
|
+
return hermite(keys, i, t, span);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Index of the key at or before `frame`, binary searched. */
|
|
127
|
+
function segmentIndex(keys, frame) {
|
|
128
|
+
let lo = 0;
|
|
129
|
+
let hi = keys.length - 1;
|
|
130
|
+
while (lo < hi) {
|
|
131
|
+
const mid = (lo + hi + 1) >> 1;
|
|
132
|
+
if (keys[mid].frame <= frame)
|
|
133
|
+
lo = mid;
|
|
134
|
+
else
|
|
135
|
+
hi = mid - 1;
|
|
136
|
+
}
|
|
137
|
+
return Math.min(lo, keys.length - 2);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Cubic Hermite across segment `i`, with tangents derived from the neighbours.
|
|
141
|
+
*
|
|
142
|
+
* There are no tangent handles yet — that is the curve editor, and it is the
|
|
143
|
+
* largest single piece of UI in the plan. Until it exists the tangents are
|
|
144
|
+
* automatic, in the way both Houdini and After Effects pick them by default:
|
|
145
|
+
*
|
|
146
|
+
* - a key with a neighbour on each side takes the central difference, which
|
|
147
|
+
* is what makes velocity continuous through a key rather than kinked
|
|
148
|
+
* - the first and last key are FLAT, so a two-key smooth channel eases in
|
|
149
|
+
* and out. This is the behaviour people mean by "smooth"; a straight line
|
|
150
|
+
* between two keys would be indistinguishable from linear
|
|
151
|
+
* - a constant segment next door flattens the tangent it touches, so a hold
|
|
152
|
+
* does not make the neighbouring curve bulge to reach it
|
|
153
|
+
*/
|
|
154
|
+
function hermite(keys, i, t, span) {
|
|
155
|
+
const k0 = keys[i];
|
|
156
|
+
const k1 = keys[i + 1];
|
|
157
|
+
const m0 = tangent(keys, i, "out");
|
|
158
|
+
const m1 = tangent(keys, i + 1, "in");
|
|
159
|
+
const t2 = t * t;
|
|
160
|
+
const t3 = t2 * t;
|
|
161
|
+
const h00 = 2 * t3 - 3 * t2 + 1;
|
|
162
|
+
const h10 = t3 - 2 * t2 + t;
|
|
163
|
+
const h01 = -2 * t3 + 3 * t2;
|
|
164
|
+
const h11 = t3 - t2;
|
|
165
|
+
return h00 * k0.value + h10 * span * m0 + h01 * k1.value + h11 * span * m1;
|
|
166
|
+
}
|
|
167
|
+
/** Value change per frame at key `i`, seen from the segment on `side`. */
|
|
168
|
+
function tangent(keys, i, side) {
|
|
169
|
+
const prev = keys[i - 1];
|
|
170
|
+
const next = keys[i + 1];
|
|
171
|
+
if (!prev || !next)
|
|
172
|
+
return 0;
|
|
173
|
+
// A hold on either side means the curve arrives, or leaves, level.
|
|
174
|
+
if (side === "out" && prev.interpolation === "constant")
|
|
175
|
+
return 0;
|
|
176
|
+
if (side === "in" && keys[i].interpolation === "constant")
|
|
177
|
+
return 0;
|
|
178
|
+
const span = next.frame - prev.frame;
|
|
179
|
+
if (span <= 0)
|
|
180
|
+
return 0;
|
|
181
|
+
return (next.value - prev.value) / span;
|
|
182
|
+
}
|
|
183
|
+
export function serializeChannel(channel) {
|
|
184
|
+
if (isEmptyChannel(channel))
|
|
185
|
+
return undefined;
|
|
186
|
+
return {
|
|
187
|
+
keys: channel.keys.map(key => (key.interpolation === DEFAULT_INTERPOLATION
|
|
188
|
+
? { frame: key.frame, value: key.value }
|
|
189
|
+
: { frame: key.frame, value: key.value, interpolation: key.interpolation }))
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Read a channel back. Anything unrecognised yields the empty channel rather
|
|
194
|
+
* than an exception — a bad channel in one parameter must not fail a load.
|
|
195
|
+
*/
|
|
196
|
+
export function deserializeChannel(data) {
|
|
197
|
+
if (!data || typeof data !== "object")
|
|
198
|
+
return EMPTY_CHANNEL;
|
|
199
|
+
const keys = data.keys;
|
|
200
|
+
if (!Array.isArray(keys))
|
|
201
|
+
return EMPTY_CHANNEL;
|
|
202
|
+
return createChannel(keys);
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=channel.js.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame-range evaluation, neutral runtime.
|
|
3
|
+
*
|
|
4
|
+
* The loop the engine has always been missing: set the frame, mark the
|
|
5
|
+
* time-dependent nodes dirty, hand the frame to a caller, repeat. It writes no
|
|
6
|
+
* files and spawns no encoder — frames are produced where the canvas is, so
|
|
7
|
+
* what the caller does with each frame is the caller's business.
|
|
8
|
+
*
|
|
9
|
+
* The loop owns no graph and no clock of its own. It drives a `FrameClock`,
|
|
10
|
+
* which a host binds to its own engine state.
|
|
11
|
+
*/
|
|
12
|
+
export interface FrameClock {
|
|
13
|
+
/** Set the current frame. Fractional frames must be preserved. */
|
|
14
|
+
setFrame(frame: number): void;
|
|
15
|
+
/** Dirty every node whose parameters depend on time. */
|
|
16
|
+
markTimeDependentDirty(): void;
|
|
17
|
+
/** Integer frame — $F */
|
|
18
|
+
getFrame(): number;
|
|
19
|
+
/** Fractional frame — $FF */
|
|
20
|
+
getFrameFraction(): number;
|
|
21
|
+
/** Seconds — $T */
|
|
22
|
+
getTime(): number;
|
|
23
|
+
/** Rate — $FPS */
|
|
24
|
+
getFps(): number;
|
|
25
|
+
}
|
|
26
|
+
/** What a caller is told about the frame it has been handed. */
|
|
27
|
+
export interface FrameInfo {
|
|
28
|
+
/** 0-based position in the range. */
|
|
29
|
+
index: number;
|
|
30
|
+
/** Total frames in the range. */
|
|
31
|
+
total: number;
|
|
32
|
+
/** Integer frame — $F */
|
|
33
|
+
frame: number;
|
|
34
|
+
/** Fractional frame — $FF */
|
|
35
|
+
fframe: number;
|
|
36
|
+
/** Seconds — $T */
|
|
37
|
+
time: number;
|
|
38
|
+
/** Rate — $FPS */
|
|
39
|
+
fps: number;
|
|
40
|
+
}
|
|
41
|
+
export interface FrameRangeOptions {
|
|
42
|
+
/** First frame, inclusive. */
|
|
43
|
+
start: number;
|
|
44
|
+
/** Last frame, inclusive when the step lands on it. */
|
|
45
|
+
end: number;
|
|
46
|
+
/** Frame increment. Must be > 0; may be fractional for sub-frame stepping. */
|
|
47
|
+
step?: number;
|
|
48
|
+
/** Called once per frame, after the frame is set and dirt is marked. */
|
|
49
|
+
onFrame: (info: FrameInfo) => void | Promise<void>;
|
|
50
|
+
/** Cooperative cancellation, checked before each frame. */
|
|
51
|
+
signal?: {
|
|
52
|
+
readonly aborted: boolean;
|
|
53
|
+
};
|
|
54
|
+
/** Restore the clock's frame when the loop finishes. Default true. */
|
|
55
|
+
restoreFrame?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface FrameRangeResult {
|
|
58
|
+
/** The fractional frames visited, in order. */
|
|
59
|
+
frames: number[];
|
|
60
|
+
frameCount: number;
|
|
61
|
+
/** True if a signal cut the loop short. */
|
|
62
|
+
aborted: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The frames a range visits. Pure — no clock, no side effects — so a caller can
|
|
66
|
+
* size a progress bar or a sequence without running anything.
|
|
67
|
+
*/
|
|
68
|
+
export declare function frameRange(start: number, end: number, step?: number): number[];
|
|
69
|
+
/**
|
|
70
|
+
* Walk a frame range against a clock, calling back once per frame.
|
|
71
|
+
*
|
|
72
|
+
* Per frame, in this order: set the frame, mark time-dependent nodes dirty,
|
|
73
|
+
* then call `onFrame`. The callback is where a cook happens — the loop does not
|
|
74
|
+
* assume one, which is what lets the same loop serve a render, a test, or a
|
|
75
|
+
* headless evaluation.
|
|
76
|
+
*/
|
|
77
|
+
export declare function runFrameRange(clock: FrameClock, options: FrameRangeOptions): Promise<FrameRangeResult>;
|
|
78
|
+
//# sourceMappingURL=frameRange.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame-range evaluation, neutral runtime.
|
|
3
|
+
*
|
|
4
|
+
* The loop the engine has always been missing: set the frame, mark the
|
|
5
|
+
* time-dependent nodes dirty, hand the frame to a caller, repeat. It writes no
|
|
6
|
+
* files and spawns no encoder — frames are produced where the canvas is, so
|
|
7
|
+
* what the caller does with each frame is the caller's business.
|
|
8
|
+
*
|
|
9
|
+
* The loop owns no graph and no clock of its own. It drives a `FrameClock`,
|
|
10
|
+
* which a host binds to its own engine state.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The frames a range visits. Pure — no clock, no side effects — so a caller can
|
|
14
|
+
* size a progress bar or a sequence without running anything.
|
|
15
|
+
*/
|
|
16
|
+
export function frameRange(start, end, step = 1) {
|
|
17
|
+
if (!Number.isFinite(start) || !Number.isFinite(end)) {
|
|
18
|
+
throw new Error(`frameRange: start and end must be finite (got ${start}, ${end})`);
|
|
19
|
+
}
|
|
20
|
+
if (!Number.isFinite(step) || step <= 0) {
|
|
21
|
+
throw new Error(`frameRange: step must be a finite number greater than 0 (got ${step})`);
|
|
22
|
+
}
|
|
23
|
+
if (end < start) {
|
|
24
|
+
throw new Error(`frameRange: end (${end}) must not be before start (${start})`);
|
|
25
|
+
}
|
|
26
|
+
// Count from the span rather than accumulating, so a fractional step does not
|
|
27
|
+
// drift and an inclusive end lands exactly.
|
|
28
|
+
const span = end - start;
|
|
29
|
+
const count = Math.floor(span / step + 1e-9) + 1;
|
|
30
|
+
const frames = new Array(count);
|
|
31
|
+
for (let i = 0; i < count; i++)
|
|
32
|
+
frames[i] = start + i * step;
|
|
33
|
+
return frames;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Walk a frame range against a clock, calling back once per frame.
|
|
37
|
+
*
|
|
38
|
+
* Per frame, in this order: set the frame, mark time-dependent nodes dirty,
|
|
39
|
+
* then call `onFrame`. The callback is where a cook happens — the loop does not
|
|
40
|
+
* assume one, which is what lets the same loop serve a render, a test, or a
|
|
41
|
+
* headless evaluation.
|
|
42
|
+
*/
|
|
43
|
+
export async function runFrameRange(clock, options) {
|
|
44
|
+
const { start, end, step = 1, onFrame, signal, restoreFrame = true } = options;
|
|
45
|
+
const frames = frameRange(start, end, step);
|
|
46
|
+
const previousFrame = clock.getFrameFraction();
|
|
47
|
+
const visited = [];
|
|
48
|
+
let aborted = false;
|
|
49
|
+
try {
|
|
50
|
+
for (let index = 0; index < frames.length; index++) {
|
|
51
|
+
if (signal?.aborted) {
|
|
52
|
+
aborted = true;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
clock.setFrame(frames[index]);
|
|
56
|
+
clock.markTimeDependentDirty();
|
|
57
|
+
await onFrame({
|
|
58
|
+
index,
|
|
59
|
+
total: frames.length,
|
|
60
|
+
frame: clock.getFrame(),
|
|
61
|
+
fframe: clock.getFrameFraction(),
|
|
62
|
+
time: clock.getTime(),
|
|
63
|
+
fps: clock.getFps()
|
|
64
|
+
});
|
|
65
|
+
visited.push(clock.getFrameFraction());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
if (restoreFrame) {
|
|
70
|
+
clock.setFrame(previousFrame);
|
|
71
|
+
clock.markTimeDependentDirty();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { frames: visited, frameCount: visited.length, aborted };
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=frameRange.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation: keyframe channels, and frame-range evaluation over a host-bound
|
|
3
|
+
* clock. Neither holds a clock of its own — the host owns time, which is what
|
|
4
|
+
* keeps a cook deterministic.
|
|
5
|
+
*/
|
|
6
|
+
export { DEFAULT_INTERPOLATION, EMPTY_CHANNEL, createChannel, deleteKey, deserializeChannel, isChannel, isEmptyChannel, isInterpolation, keyAt, sampleChannel, serializeChannel, setKey, setKeyInterpolation } from "./channel.js";
|
|
7
|
+
export type { Channel, Interpolation, Keyframe, KeyframeInput, SerializedChannel, SerializedKeyframe } from "./channel.js";
|
|
8
|
+
export { frameRange, runFrameRange } from "./frameRange.js";
|
|
9
|
+
export type { FrameClock, FrameInfo, FrameRangeOptions, FrameRangeResult } from "./frameRange.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Animation: keyframe channels, and frame-range evaluation over a host-bound
|
|
3
|
+
* clock. Neither holds a clock of its own — the host owns time, which is what
|
|
4
|
+
* keeps a cook deterministic.
|
|
5
|
+
*/
|
|
6
|
+
export { DEFAULT_INTERPOLATION, EMPTY_CHANNEL, createChannel, deleteKey, deserializeChannel, isChannel, isEmptyChannel, isInterpolation, keyAt, sampleChannel, serializeChannel, setKey, setKeyInterpolation } from "./channel.js";
|
|
7
|
+
export { frameRange, runFrameRange } from "./frameRange.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NodeExecutionContext } from "cascade/contracts";
|
|
1
|
+
import type { NodeExecutionContext } from "@field/cascade/contracts";
|
|
2
2
|
import { copyToPointsDefinition } from "./definitions.js";
|
|
3
3
|
/**
|
|
4
4
|
* Copy to Points SOP: the source instanced onto every point of the target.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { emptyGeometry } from "cascade/contracts";
|
|
1
|
+
import { emptyGeometry } from "@field/cascade/contracts";
|
|
2
2
|
import { copyToPoints } from "../../geometry/copy.js";
|
|
3
3
|
import { copyToPointsDefinition } from "./definitions.js";
|
|
4
4
|
import { geoModuleId } from "./namespace.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NodeExecutionContext } from "cascade/contracts";
|
|
1
|
+
import type { NodeExecutionContext } from "@field/cascade/contracts";
|
|
2
2
|
import { circleDefinition, rectangleDefinition } from "./definitions.js";
|
|
3
3
|
/**
|
|
4
4
|
* The two generators. Houdini has no Rectangle and its Grid with two rows does
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NodeExecutionContext } from "cascade/contracts";
|
|
1
|
+
import type { NodeExecutionContext } from "@field/cascade/contracts";
|
|
2
2
|
import { mergeDefinition } from "./definitions.js";
|
|
3
3
|
/**
|
|
4
4
|
* Merge SOP: N geometries concatenated, with points, vertices and primitives
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { emptyGeometry } from "cascade/contracts";
|
|
1
|
+
import { emptyGeometry } from "@field/cascade/contracts";
|
|
2
2
|
import { geometryToSvg, utf8Bytes } from "../../geometry/svg.js";
|
|
3
3
|
import { svgExportDefinition } from "./definitions.js";
|
|
4
4
|
import { geoModuleId } from "./namespace.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { NodeExecutionContext } from "cascade/contracts";
|
|
1
|
+
import type { NodeExecutionContext } from "@field/cascade/contracts";
|
|
2
2
|
import { transformDefinition } from "./definitions.js";
|
|
3
3
|
/**
|
|
4
4
|
* Transform SOP's parameters, by its names: translate, rotate, scale, pivot,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { emptyGeometry } from "cascade/contracts";
|
|
1
|
+
import { emptyGeometry } from "@field/cascade/contracts";
|
|
2
2
|
import { transformMatrix } from "../../geometry/matrix.js";
|
|
3
3
|
import { transformGeometry } from "../../geometry/transform.js";
|
|
4
4
|
import { transformDefinition } from "./definitions.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as TypeScript from "typescript";
|
|
2
|
-
import type { Diagnostic } from "cascade/contracts";
|
|
2
|
+
import type { Diagnostic } from "@field/cascade/contracts";
|
|
3
3
|
/**
|
|
4
4
|
* Enforces the statically inspectable node-module boundary. This is an
|
|
5
5
|
* architecture check for trusted project code, not a security sandbox.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as TypeScript from "typescript";
|
|
2
|
-
import { type Diagnostic, type NodeDefinition } from "cascade/contracts";
|
|
2
|
+
import { type Diagnostic, type NodeDefinition } from "@field/cascade/contracts";
|
|
3
3
|
export type DefinitionExtractionResult = {
|
|
4
4
|
readonly ok: true;
|
|
5
5
|
readonly definition: NodeDefinition;
|
package/dist/runtime/error.d.ts
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The neutral expression evaluator.
|
|
3
|
+
*
|
|
4
|
+
* Compiles parameter expressions and evaluates them against a time state and a
|
|
5
|
+
* host-supplied scope. It imports nothing from Studio and touches no platform
|
|
6
|
+
* API, so a headless cook can resolve `sin($T)` on a parameter exactly as the
|
|
7
|
+
* editor does.
|
|
8
|
+
*
|
|
9
|
+
* Determinism: an expression is a parameter resolved *before* execute runs, so
|
|
10
|
+
* the node receives a plain number and stays pure. Same graph plus same frame
|
|
11
|
+
* gives the same output.
|
|
12
|
+
*/
|
|
13
|
+
import type { ExpressionScope, Node as NodeShape } from "./scope.js";
|
|
14
|
+
import { TimeState } from "./time.js";
|
|
15
|
+
import type { CompiledExpression, ExpressionContext, ExpressionResult } from "./types.js";
|
|
16
|
+
export declare class ExpressionEvaluator<TNode extends NodeShape = NodeShape> {
|
|
17
|
+
protected readonly timeState: TimeState;
|
|
18
|
+
private readonly compiledCache;
|
|
19
|
+
private scope;
|
|
20
|
+
constructor(scope?: ExpressionScope<TNode> | null);
|
|
21
|
+
/** Bind (or rebind) the host scope. Clears nothing: compilation is scope-free. */
|
|
22
|
+
setScope(scope: ExpressionScope<TNode> | null): void;
|
|
23
|
+
getScope(): ExpressionScope<TNode> | null;
|
|
24
|
+
/** Set the current frame. Fractional frames are preserved. */
|
|
25
|
+
setFrame(frame: number): void;
|
|
26
|
+
/** Set the current time in seconds, keeping the sub-frame remainder. */
|
|
27
|
+
setTime(seconds: number): void;
|
|
28
|
+
setFps(fps: number): void;
|
|
29
|
+
/** seconds — $T */
|
|
30
|
+
get time(): number;
|
|
31
|
+
/** integer frame — $F */
|
|
32
|
+
get frame(): number;
|
|
33
|
+
/** fractional frame — $FF */
|
|
34
|
+
get fframe(): number;
|
|
35
|
+
/** rate — $FPS */
|
|
36
|
+
get fps(): number;
|
|
37
|
+
compile(expression: string): CompiledExpression<TNode>;
|
|
38
|
+
evaluate(expression: string, node: TNode): ExpressionResult;
|
|
39
|
+
/** Build the context an expression sees. */
|
|
40
|
+
createContext(node: TNode): ExpressionContext<TNode>;
|
|
41
|
+
ch(node: TNode, path: string): number;
|
|
42
|
+
chs(node: TNode, path: string): string;
|
|
43
|
+
chv(node: TNode, path: string): number[];
|
|
44
|
+
private resolveParam;
|
|
45
|
+
/** True if a referenced path resolves from this node. */
|
|
46
|
+
validatePath(fromNode: TNode, path: string): boolean;
|
|
47
|
+
/** Parameter paths referenced through ch/chs/chv. */
|
|
48
|
+
extractDependencies(expression: string): string[];
|
|
49
|
+
/** True if the expression references time, in either the bare or `$` spelling. */
|
|
50
|
+
hasTimeReference(expression: string): boolean;
|
|
51
|
+
clearCache(): void;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=evaluator.d.ts.map
|