@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,193 @@
|
|
|
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 { clamp, fit, fit01, lerp, MATH_SCOPE, noise, padzero, random, smooth } from "./math.js";
|
|
14
|
+
import { hasTimeReference, preprocessExpression } from "./preprocess.js";
|
|
15
|
+
import { TimeState } from "./time.js";
|
|
16
|
+
const DEPENDENCY_PATTERN = /\b(?:ch|chs|chv)\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
17
|
+
export class ExpressionEvaluator {
|
|
18
|
+
timeState = new TimeState();
|
|
19
|
+
compiledCache = new Map();
|
|
20
|
+
scope;
|
|
21
|
+
constructor(scope = null) {
|
|
22
|
+
this.scope = scope;
|
|
23
|
+
}
|
|
24
|
+
/** Bind (or rebind) the host scope. Clears nothing: compilation is scope-free. */
|
|
25
|
+
setScope(scope) {
|
|
26
|
+
this.scope = scope;
|
|
27
|
+
}
|
|
28
|
+
getScope() {
|
|
29
|
+
return this.scope;
|
|
30
|
+
}
|
|
31
|
+
// ============ Time ============
|
|
32
|
+
/** Set the current frame. Fractional frames are preserved. */
|
|
33
|
+
setFrame(frame) {
|
|
34
|
+
this.timeState.setFrame(frame);
|
|
35
|
+
}
|
|
36
|
+
/** Set the current time in seconds, keeping the sub-frame remainder. */
|
|
37
|
+
setTime(seconds) {
|
|
38
|
+
this.timeState.setTime(seconds);
|
|
39
|
+
}
|
|
40
|
+
setFps(fps) {
|
|
41
|
+
this.timeState.setFps(fps);
|
|
42
|
+
}
|
|
43
|
+
/** seconds — $T */
|
|
44
|
+
get time() {
|
|
45
|
+
return this.timeState.time;
|
|
46
|
+
}
|
|
47
|
+
/** integer frame — $F */
|
|
48
|
+
get frame() {
|
|
49
|
+
return this.timeState.frame;
|
|
50
|
+
}
|
|
51
|
+
/** fractional frame — $FF */
|
|
52
|
+
get fframe() {
|
|
53
|
+
return this.timeState.frameFraction;
|
|
54
|
+
}
|
|
55
|
+
/** rate — $FPS */
|
|
56
|
+
get fps() {
|
|
57
|
+
return this.timeState.fps;
|
|
58
|
+
}
|
|
59
|
+
// ============ Compile / evaluate ============
|
|
60
|
+
compile(expression) {
|
|
61
|
+
const cached = this.compiledCache.get(expression);
|
|
62
|
+
if (cached)
|
|
63
|
+
return cached;
|
|
64
|
+
const dependencies = this.extractDependencies(expression);
|
|
65
|
+
const isTimeDependent = hasTimeReference(expression);
|
|
66
|
+
const compiledSource = preprocessExpression(expression);
|
|
67
|
+
try {
|
|
68
|
+
const fnBody = `
|
|
69
|
+
with (context) {
|
|
70
|
+
return (${compiledSource});
|
|
71
|
+
}
|
|
72
|
+
`;
|
|
73
|
+
const evaluator = new Function("context", fnBody);
|
|
74
|
+
const compiled = {
|
|
75
|
+
source: expression,
|
|
76
|
+
compiledSource,
|
|
77
|
+
evaluate: evaluator,
|
|
78
|
+
dependencies,
|
|
79
|
+
isTimeDependent
|
|
80
|
+
};
|
|
81
|
+
this.compiledCache.set(expression, compiled);
|
|
82
|
+
return compiled;
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
return {
|
|
86
|
+
source: expression,
|
|
87
|
+
compiledSource,
|
|
88
|
+
evaluate: () => undefined,
|
|
89
|
+
dependencies,
|
|
90
|
+
isTimeDependent,
|
|
91
|
+
error: err instanceof Error ? err.message : String(err)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
evaluate(expression, node) {
|
|
96
|
+
const compiled = this.compile(expression);
|
|
97
|
+
if (compiled.error)
|
|
98
|
+
return { value: undefined, error: compiled.error };
|
|
99
|
+
try {
|
|
100
|
+
return { value: compiled.evaluate(this.createContext(node)) };
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
return {
|
|
104
|
+
value: undefined,
|
|
105
|
+
error: err instanceof Error ? err.message : String(err)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Build the context an expression sees. */
|
|
110
|
+
createContext(node) {
|
|
111
|
+
return {
|
|
112
|
+
time: this.timeState.time,
|
|
113
|
+
frame: this.timeState.frame,
|
|
114
|
+
fframe: this.timeState.frameFraction,
|
|
115
|
+
fps: this.timeState.fps,
|
|
116
|
+
self: node,
|
|
117
|
+
ch: (path) => this.ch(node, path),
|
|
118
|
+
chs: (path) => this.chs(node, path),
|
|
119
|
+
chv: (path) => this.chv(node, path),
|
|
120
|
+
opexist: (path) => (this.scope?.nodeExists(node, path) ? 1 : 0),
|
|
121
|
+
opinput: (index) => this.scope?.inputNode(node, index) ?? null,
|
|
122
|
+
fit,
|
|
123
|
+
fit01,
|
|
124
|
+
clamp,
|
|
125
|
+
lerp,
|
|
126
|
+
smooth,
|
|
127
|
+
noise,
|
|
128
|
+
random,
|
|
129
|
+
padzero,
|
|
130
|
+
// Bare maths — `sin($FF * 0.5)` rather than `Math.sin(...)`. Spread
|
|
131
|
+
// before `Math` so nothing here can shadow the namespace itself.
|
|
132
|
+
...MATH_SCOPE,
|
|
133
|
+
Math
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
// ============ Channel functions ============
|
|
137
|
+
ch(node, path) {
|
|
138
|
+
const resolved = this.resolveParam(node, path);
|
|
139
|
+
if (!resolved)
|
|
140
|
+
return 0;
|
|
141
|
+
const value = resolved.value;
|
|
142
|
+
if (typeof value === "number")
|
|
143
|
+
return value;
|
|
144
|
+
if (typeof value === "string")
|
|
145
|
+
return parseFloat(value) || 0;
|
|
146
|
+
if (typeof value === "boolean")
|
|
147
|
+
return value ? 1 : 0;
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
chs(node, path) {
|
|
151
|
+
const resolved = this.resolveParam(node, path);
|
|
152
|
+
if (!resolved)
|
|
153
|
+
return "";
|
|
154
|
+
return String(resolved.value ?? "");
|
|
155
|
+
}
|
|
156
|
+
chv(node, path) {
|
|
157
|
+
const resolved = this.resolveParam(node, path);
|
|
158
|
+
if (!resolved)
|
|
159
|
+
return [];
|
|
160
|
+
const value = resolved.value;
|
|
161
|
+
if (Array.isArray(value)) {
|
|
162
|
+
return value.map((v) => (typeof v === "number" ? v : parseFloat(String(v)) || 0));
|
|
163
|
+
}
|
|
164
|
+
if (typeof value === "number")
|
|
165
|
+
return [value];
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
resolveParam(node, path) {
|
|
169
|
+
return this.scope?.resolveParam(node, path) ?? null;
|
|
170
|
+
}
|
|
171
|
+
/** True if a referenced path resolves from this node. */
|
|
172
|
+
validatePath(fromNode, path) {
|
|
173
|
+
return this.resolveParam(fromNode, path) !== null;
|
|
174
|
+
}
|
|
175
|
+
/** Parameter paths referenced through ch/chs/chv. */
|
|
176
|
+
extractDependencies(expression) {
|
|
177
|
+
const deps = [];
|
|
178
|
+
DEPENDENCY_PATTERN.lastIndex = 0;
|
|
179
|
+
let match;
|
|
180
|
+
while ((match = DEPENDENCY_PATTERN.exec(expression)) !== null) {
|
|
181
|
+
deps.push(match[1]);
|
|
182
|
+
}
|
|
183
|
+
return deps;
|
|
184
|
+
}
|
|
185
|
+
/** True if the expression references time, in either the bare or `$` spelling. */
|
|
186
|
+
hasTimeReference(expression) {
|
|
187
|
+
return hasTimeReference(expression);
|
|
188
|
+
}
|
|
189
|
+
clearCache() {
|
|
190
|
+
this.compiledCache.clear();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=evaluator.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameter expressions, neutral runtime.
|
|
3
|
+
*
|
|
4
|
+
* Compilation, `$` time variables, math utilities and the host scope binding.
|
|
5
|
+
* Depends on nothing outside this folder, so both Studio and a headless host
|
|
6
|
+
* evaluate parameters with the same engine.
|
|
7
|
+
*/
|
|
8
|
+
export { ExpressionEvaluator } from "./evaluator.js";
|
|
9
|
+
export { clamp, fit, fit01, lerp, noise, padzero, random, smooth } from "./math.js";
|
|
10
|
+
export { TIME_IDENTIFIERS, TIME_TOKENS, hasTimeReference, preprocessExpression } from "./preprocess.js";
|
|
11
|
+
export { DEFAULT_FPS, MIN_FRAME, TimeState } from "./time.js";
|
|
12
|
+
export type { TimeVariables } from "./time.js";
|
|
13
|
+
export type { ExpressionScope, Node as ExpressionNode } from "./scope.js";
|
|
14
|
+
export type { CompiledExpression, ExpressionContext, ExpressionResult, PathWarning } from "./types.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameter expressions, neutral runtime.
|
|
3
|
+
*
|
|
4
|
+
* Compilation, `$` time variables, math utilities and the host scope binding.
|
|
5
|
+
* Depends on nothing outside this folder, so both Studio and a headless host
|
|
6
|
+
* evaluate parameters with the same engine.
|
|
7
|
+
*/
|
|
8
|
+
export { ExpressionEvaluator } from "./evaluator.js";
|
|
9
|
+
export { clamp, fit, fit01, lerp, noise, padzero, random, smooth } from "./math.js";
|
|
10
|
+
export { TIME_IDENTIFIERS, TIME_TOKENS, hasTimeReference, preprocessExpression } from "./preprocess.js";
|
|
11
|
+
export { DEFAULT_FPS, MIN_FRAME, TimeState } from "./time.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expression math utilities.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions, no ambient state, deterministic for a given input — the same
|
|
5
|
+
* contract the rest of the runtime holds. `noise` and `random` are seeded
|
|
6
|
+
* closed forms rather than anything drawn from process state.
|
|
7
|
+
*/
|
|
8
|
+
/** Remap a value from one range to another. */
|
|
9
|
+
export declare function fit(value: number, oldMin: number, oldMax: number, newMin: number, newMax: number): number;
|
|
10
|
+
/** Remap a 0..1 value into a new range. */
|
|
11
|
+
export declare function fit01(value: number, newMin: number, newMax: number): number;
|
|
12
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
13
|
+
export declare function lerp(a: number, b: number, t: number): number;
|
|
14
|
+
/** Smooth hermite interpolation (smoothstep). */
|
|
15
|
+
export declare function smooth(value: number, min: number, max: number): number;
|
|
16
|
+
/**
|
|
17
|
+
* Perlin-like noise in -1..1. Not true Perlin — a deterministic sum of sines.
|
|
18
|
+
*/
|
|
19
|
+
export declare function noise(...coords: number[]): number;
|
|
20
|
+
/** Deterministic random in 0..1 from a seed. */
|
|
21
|
+
export declare function random(seed: number): number;
|
|
22
|
+
/** Zero-pad a number to a given number of digits. */
|
|
23
|
+
export declare function padzero(digits: number, value: number): string;
|
|
24
|
+
/** Degrees to radians. */
|
|
25
|
+
export declare function radians(degrees: number): number;
|
|
26
|
+
/** Radians to degrees. */
|
|
27
|
+
export declare function degrees(radians: number): number;
|
|
28
|
+
/** Sine of an angle in degrees — Houdini's `sin()`. */
|
|
29
|
+
export declare function sind(degrees: number): number;
|
|
30
|
+
/** Cosine of an angle in degrees — Houdini's `cos()`. */
|
|
31
|
+
export declare function cosd(degrees: number): number;
|
|
32
|
+
/** Tangent of an angle in degrees — Houdini's `tan()`. */
|
|
33
|
+
export declare function tand(degrees: number): number;
|
|
34
|
+
/**
|
|
35
|
+
* The bare maths names available to every expression. Spread into the
|
|
36
|
+
* evaluation scope; kept as one object so adding a function is a one-line
|
|
37
|
+
* change here rather than a change in two files.
|
|
38
|
+
*/
|
|
39
|
+
export declare const MATH_SCOPE: {
|
|
40
|
+
readonly sin: (x: number) => number;
|
|
41
|
+
readonly cos: (x: number) => number;
|
|
42
|
+
readonly tan: (x: number) => number;
|
|
43
|
+
readonly asin: (x: number) => number;
|
|
44
|
+
readonly acos: (x: number) => number;
|
|
45
|
+
readonly atan: (x: number) => number;
|
|
46
|
+
readonly atan2: (y: number, x: number) => number;
|
|
47
|
+
readonly sinh: (x: number) => number;
|
|
48
|
+
readonly cosh: (x: number) => number;
|
|
49
|
+
readonly tanh: (x: number) => number;
|
|
50
|
+
readonly sind: typeof sind;
|
|
51
|
+
readonly cosd: typeof cosd;
|
|
52
|
+
readonly tand: typeof tand;
|
|
53
|
+
readonly radians: typeof radians;
|
|
54
|
+
readonly degrees: typeof degrees;
|
|
55
|
+
readonly sqrt: (x: number) => number;
|
|
56
|
+
readonly cbrt: (x: number) => number;
|
|
57
|
+
readonly pow: (x: number, y: number) => number;
|
|
58
|
+
readonly exp: (x: number) => number;
|
|
59
|
+
readonly log: (x: number) => number;
|
|
60
|
+
readonly log2: (x: number) => number;
|
|
61
|
+
readonly log10: (x: number) => number;
|
|
62
|
+
readonly hypot: (...values: number[]) => number;
|
|
63
|
+
readonly abs: (x: number) => number;
|
|
64
|
+
readonly sign: (x: number) => number;
|
|
65
|
+
readonly floor: (x: number) => number;
|
|
66
|
+
readonly ceil: (x: number) => number;
|
|
67
|
+
readonly round: (x: number) => number;
|
|
68
|
+
readonly trunc: (x: number) => number;
|
|
69
|
+
readonly min: (...values: number[]) => number;
|
|
70
|
+
readonly max: (...values: number[]) => number;
|
|
71
|
+
readonly PI: number;
|
|
72
|
+
readonly TAU: number;
|
|
73
|
+
readonly E: number;
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=math.d.ts.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expression math utilities.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions, no ambient state, deterministic for a given input — the same
|
|
5
|
+
* contract the rest of the runtime holds. `noise` and `random` are seeded
|
|
6
|
+
* closed forms rather than anything drawn from process state.
|
|
7
|
+
*/
|
|
8
|
+
/** Remap a value from one range to another. */
|
|
9
|
+
export function fit(value, oldMin, oldMax, newMin, newMax) {
|
|
10
|
+
if (oldMax === oldMin)
|
|
11
|
+
return newMin;
|
|
12
|
+
const t = (value - oldMin) / (oldMax - oldMin);
|
|
13
|
+
return newMin + t * (newMax - newMin);
|
|
14
|
+
}
|
|
15
|
+
/** Remap a 0..1 value into a new range. */
|
|
16
|
+
export function fit01(value, newMin, newMax) {
|
|
17
|
+
return newMin + value * (newMax - newMin);
|
|
18
|
+
}
|
|
19
|
+
export function clamp(value, min, max) {
|
|
20
|
+
return Math.max(min, Math.min(max, value));
|
|
21
|
+
}
|
|
22
|
+
export function lerp(a, b, t) {
|
|
23
|
+
return a + (b - a) * t;
|
|
24
|
+
}
|
|
25
|
+
/** Smooth hermite interpolation (smoothstep). */
|
|
26
|
+
export function smooth(value, min, max) {
|
|
27
|
+
if (value <= min)
|
|
28
|
+
return 0;
|
|
29
|
+
if (value >= max)
|
|
30
|
+
return 1;
|
|
31
|
+
const t = (value - min) / (max - min);
|
|
32
|
+
return t * t * (3 - 2 * t);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Perlin-like noise in -1..1. Not true Perlin — a deterministic sum of sines.
|
|
36
|
+
*/
|
|
37
|
+
export function noise(...coords) {
|
|
38
|
+
if (coords.length === 0)
|
|
39
|
+
return 0;
|
|
40
|
+
let result = 0;
|
|
41
|
+
const frequencies = [1, 2.3, 4.7, 8.1];
|
|
42
|
+
const amplitudes = [1, 0.5, 0.25, 0.125];
|
|
43
|
+
for (let i = 0; i < frequencies.length; i++) {
|
|
44
|
+
let sum = 0;
|
|
45
|
+
for (let j = 0; j < coords.length; j++) {
|
|
46
|
+
sum += Math.sin(coords[j] * frequencies[i] * (j + 1) * 1.7 + j * 3.14159);
|
|
47
|
+
}
|
|
48
|
+
result += sum * amplitudes[i];
|
|
49
|
+
}
|
|
50
|
+
return Math.sin(result * 0.5);
|
|
51
|
+
}
|
|
52
|
+
/** Deterministic random in 0..1 from a seed. */
|
|
53
|
+
export function random(seed) {
|
|
54
|
+
const x = Math.sin(seed * 12.9898) * 43758.5453;
|
|
55
|
+
return x - Math.floor(x);
|
|
56
|
+
}
|
|
57
|
+
/** Zero-pad a number to a given number of digits. */
|
|
58
|
+
export function padzero(digits, value) {
|
|
59
|
+
return String(Math.floor(value)).padStart(digits, "0");
|
|
60
|
+
}
|
|
61
|
+
// ============ Bare maths, as Houdini exposes it ============
|
|
62
|
+
//
|
|
63
|
+
// An expression language whose users write `sin($T)` and get "sin is not
|
|
64
|
+
// defined" is one they stop trusting. Houdini exposes the whole maths library
|
|
65
|
+
// bare, and so do we.
|
|
66
|
+
//
|
|
67
|
+
// Angles are RADIANS, unlike Houdini's degrees. Two reasons, and the second is
|
|
68
|
+
// the deciding one. `$T` in radians oscillates over 2pi seconds, which is a
|
|
69
|
+
// usable animation period, where in degrees `sin($T)` would take six minutes to
|
|
70
|
+
// come round. And `Math` is also in scope, so a degree-based `sin()` would
|
|
71
|
+
// disagree with `Math.sin()` inside the same expression — a trap far worse than
|
|
72
|
+
// having to type `radians()`. The `sind`/`cosd`/`tand` trio is there for anyone
|
|
73
|
+
// carrying a formula over from Houdini.
|
|
74
|
+
/** Degrees to radians. */
|
|
75
|
+
export function radians(degrees) {
|
|
76
|
+
return (degrees * Math.PI) / 180;
|
|
77
|
+
}
|
|
78
|
+
/** Radians to degrees. */
|
|
79
|
+
export function degrees(radians) {
|
|
80
|
+
return (radians * 180) / Math.PI;
|
|
81
|
+
}
|
|
82
|
+
/** Sine of an angle in degrees — Houdini's `sin()`. */
|
|
83
|
+
export function sind(degrees) {
|
|
84
|
+
return Math.sin(radians(degrees));
|
|
85
|
+
}
|
|
86
|
+
/** Cosine of an angle in degrees — Houdini's `cos()`. */
|
|
87
|
+
export function cosd(degrees) {
|
|
88
|
+
return Math.cos(radians(degrees));
|
|
89
|
+
}
|
|
90
|
+
/** Tangent of an angle in degrees — Houdini's `tan()`. */
|
|
91
|
+
export function tand(degrees) {
|
|
92
|
+
return Math.tan(radians(degrees));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The bare maths names available to every expression. Spread into the
|
|
96
|
+
* evaluation scope; kept as one object so adding a function is a one-line
|
|
97
|
+
* change here rather than a change in two files.
|
|
98
|
+
*/
|
|
99
|
+
export const MATH_SCOPE = {
|
|
100
|
+
// Trigonometry, in radians
|
|
101
|
+
sin: Math.sin,
|
|
102
|
+
cos: Math.cos,
|
|
103
|
+
tan: Math.tan,
|
|
104
|
+
asin: Math.asin,
|
|
105
|
+
acos: Math.acos,
|
|
106
|
+
atan: Math.atan,
|
|
107
|
+
atan2: Math.atan2,
|
|
108
|
+
sinh: Math.sinh,
|
|
109
|
+
cosh: Math.cosh,
|
|
110
|
+
tanh: Math.tanh,
|
|
111
|
+
// Trigonometry, in degrees, for formulae carried over from Houdini
|
|
112
|
+
sind,
|
|
113
|
+
cosd,
|
|
114
|
+
tand,
|
|
115
|
+
radians,
|
|
116
|
+
degrees,
|
|
117
|
+
// Powers and logs
|
|
118
|
+
sqrt: Math.sqrt,
|
|
119
|
+
cbrt: Math.cbrt,
|
|
120
|
+
pow: Math.pow,
|
|
121
|
+
exp: Math.exp,
|
|
122
|
+
log: Math.log,
|
|
123
|
+
log2: Math.log2,
|
|
124
|
+
log10: Math.log10,
|
|
125
|
+
hypot: Math.hypot,
|
|
126
|
+
// Rounding and sign
|
|
127
|
+
abs: Math.abs,
|
|
128
|
+
sign: Math.sign,
|
|
129
|
+
floor: Math.floor,
|
|
130
|
+
ceil: Math.ceil,
|
|
131
|
+
round: Math.round,
|
|
132
|
+
trunc: Math.trunc,
|
|
133
|
+
min: Math.min,
|
|
134
|
+
max: Math.max,
|
|
135
|
+
// Constants
|
|
136
|
+
PI: Math.PI,
|
|
137
|
+
TAU: Math.PI * 2,
|
|
138
|
+
E: Math.E
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=math.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expression preprocessing: Houdini-style `$` time variables.
|
|
3
|
+
*
|
|
4
|
+
* Expressions compile to raw JS (`return (<expr>)`) evaluated against a context
|
|
5
|
+
* object, so `$F` is not valid input. This pass rewrites the `$` spellings to
|
|
6
|
+
* the plain identifiers the context carries, before compilation. The bare
|
|
7
|
+
* identifiers remain valid aliases — `time`, `frame`, `fframe`, `fps` — so
|
|
8
|
+
* nothing already authored breaks.
|
|
9
|
+
*
|
|
10
|
+
* $F -> frame integer frame
|
|
11
|
+
* $FF -> fframe fractional frame
|
|
12
|
+
* $T -> time seconds
|
|
13
|
+
* $FPS -> fps rate
|
|
14
|
+
*
|
|
15
|
+
* `$` is rewritten only in code position: occurrences inside string or template
|
|
16
|
+
* literals are left alone, so `chs("$F")` stays a string.
|
|
17
|
+
*/
|
|
18
|
+
/** The `$` time tokens, longest first so `$FPS` wins over `$F`. */
|
|
19
|
+
export declare const TIME_TOKENS: ReadonlyArray<readonly [token: string, identifier: string]>;
|
|
20
|
+
/** Bare identifiers that carry time, kept as aliases for the `$` spellings. */
|
|
21
|
+
export declare const TIME_IDENTIFIERS: readonly string[];
|
|
22
|
+
/**
|
|
23
|
+
* Rewrite `$` time variables to their plain identifiers.
|
|
24
|
+
* Returns the source unchanged when it contains no `$`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function preprocessExpression(source: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* True if an expression references any time variable, in either spelling.
|
|
29
|
+
* This is what sets `isTimeDependent`, which is what lets a per-frame cook
|
|
30
|
+
* recompute only the time-dependent subgraph.
|
|
31
|
+
*/
|
|
32
|
+
export declare function hasTimeReference(source: string): boolean;
|
|
33
|
+
//# sourceMappingURL=preprocess.d.ts.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expression preprocessing: Houdini-style `$` time variables.
|
|
3
|
+
*
|
|
4
|
+
* Expressions compile to raw JS (`return (<expr>)`) evaluated against a context
|
|
5
|
+
* object, so `$F` is not valid input. This pass rewrites the `$` spellings to
|
|
6
|
+
* the plain identifiers the context carries, before compilation. The bare
|
|
7
|
+
* identifiers remain valid aliases — `time`, `frame`, `fframe`, `fps` — so
|
|
8
|
+
* nothing already authored breaks.
|
|
9
|
+
*
|
|
10
|
+
* $F -> frame integer frame
|
|
11
|
+
* $FF -> fframe fractional frame
|
|
12
|
+
* $T -> time seconds
|
|
13
|
+
* $FPS -> fps rate
|
|
14
|
+
*
|
|
15
|
+
* `$` is rewritten only in code position: occurrences inside string or template
|
|
16
|
+
* literals are left alone, so `chs("$F")` stays a string.
|
|
17
|
+
*/
|
|
18
|
+
/** The `$` time tokens, longest first so `$FPS` wins over `$F`. */
|
|
19
|
+
export const TIME_TOKENS = [
|
|
20
|
+
["$FPS", "fps"],
|
|
21
|
+
["$FF", "fframe"],
|
|
22
|
+
["$F", "frame"],
|
|
23
|
+
["$T", "time"]
|
|
24
|
+
];
|
|
25
|
+
/** Bare identifiers that carry time, kept as aliases for the `$` spellings. */
|
|
26
|
+
export const TIME_IDENTIFIERS = ["time", "frame", "fframe", "fps"];
|
|
27
|
+
const DOLLAR_TIME_PATTERN = /\$(FPS|FF|F|T)\b/g;
|
|
28
|
+
const BARE_TIME_PATTERN = /\b(?:time|frame|fframe|fps)\b/;
|
|
29
|
+
const DOLLAR_TO_IDENTIFIER = {
|
|
30
|
+
FPS: "fps",
|
|
31
|
+
FF: "fframe",
|
|
32
|
+
F: "frame",
|
|
33
|
+
T: "time"
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Split an expression into code and literal spans, so a rewrite can skip
|
|
37
|
+
* string and template literals. Deliberately small: it handles quotes and
|
|
38
|
+
* escapes, which is all an expression field contains.
|
|
39
|
+
*/
|
|
40
|
+
function codeSpans(source) {
|
|
41
|
+
const spans = [];
|
|
42
|
+
let spanStart = 0;
|
|
43
|
+
let index = 0;
|
|
44
|
+
while (index < source.length) {
|
|
45
|
+
const char = source[index];
|
|
46
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
47
|
+
spans.push({ start: spanStart, end: index });
|
|
48
|
+
const quote = char;
|
|
49
|
+
index += 1;
|
|
50
|
+
while (index < source.length) {
|
|
51
|
+
if (source[index] === "\\") {
|
|
52
|
+
index += 2;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (source[index] === quote) {
|
|
56
|
+
index += 1;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
index += 1;
|
|
60
|
+
}
|
|
61
|
+
spanStart = index;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
index += 1;
|
|
65
|
+
}
|
|
66
|
+
spans.push({ start: spanStart, end: source.length });
|
|
67
|
+
return spans;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Rewrite `$` time variables to their plain identifiers.
|
|
71
|
+
* Returns the source unchanged when it contains no `$`.
|
|
72
|
+
*/
|
|
73
|
+
export function preprocessExpression(source) {
|
|
74
|
+
if (!source.includes("$"))
|
|
75
|
+
return source;
|
|
76
|
+
let out = "";
|
|
77
|
+
let cursor = 0;
|
|
78
|
+
for (const span of codeSpans(source)) {
|
|
79
|
+
// Literal text between spans is copied verbatim.
|
|
80
|
+
if (span.start > cursor)
|
|
81
|
+
out += source.slice(cursor, span.start);
|
|
82
|
+
out += source
|
|
83
|
+
.slice(span.start, span.end)
|
|
84
|
+
.replace(DOLLAR_TIME_PATTERN, (_match, key) => DOLLAR_TO_IDENTIFIER[key]);
|
|
85
|
+
cursor = span.end;
|
|
86
|
+
}
|
|
87
|
+
if (cursor < source.length)
|
|
88
|
+
out += source.slice(cursor);
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* True if an expression references any time variable, in either spelling.
|
|
93
|
+
* This is what sets `isTimeDependent`, which is what lets a per-frame cook
|
|
94
|
+
* recompute only the time-dependent subgraph.
|
|
95
|
+
*/
|
|
96
|
+
export function hasTimeReference(source) {
|
|
97
|
+
if (BARE_TIME_PATTERN.test(source))
|
|
98
|
+
return true;
|
|
99
|
+
DOLLAR_TIME_PATTERN.lastIndex = 0;
|
|
100
|
+
return DOLLAR_TIME_PATTERN.test(source);
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=preprocess.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host binding for expression evaluation.
|
|
3
|
+
*
|
|
4
|
+
* The expression engine needs to read parameters off neighbouring nodes and to
|
|
5
|
+
* follow inputs, but it must not know what a node *is* — that is what kept it
|
|
6
|
+
* pinned to Studio's compatibility `Graph`/`Node`. A host supplies a scope; the
|
|
7
|
+
* engine stays neutral and works unchanged in a headless cook.
|
|
8
|
+
*/
|
|
9
|
+
/** The minimum a node must offer to be addressable from an expression. */
|
|
10
|
+
export interface Node {
|
|
11
|
+
id: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ExpressionScope<TNode = Node> {
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a channel path relative to a node, e.g. `./radius`,
|
|
16
|
+
* `../blur1/amount`, `/root/node/value`. Return null when it does not
|
|
17
|
+
* resolve — the engine turns that into 0 / '' / [] per accessor.
|
|
18
|
+
*/
|
|
19
|
+
resolveParam(from: TNode, path: string): {
|
|
20
|
+
value: unknown;
|
|
21
|
+
} | null;
|
|
22
|
+
/** True if a node exists at the path, relative to `from`. */
|
|
23
|
+
nodeExists(from: TNode, path: string): boolean;
|
|
24
|
+
/** The node connected to `from`'s input at `index`, if any. */
|
|
25
|
+
inputNode(from: TNode, index: number): TNode | null;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host binding for expression evaluation.
|
|
3
|
+
*
|
|
4
|
+
* The expression engine needs to read parameters off neighbouring nodes and to
|
|
5
|
+
* follow inputs, but it must not know what a node *is* — that is what kept it
|
|
6
|
+
* pinned to Studio's compatibility `Graph`/`Node`. A host supplies a scope; the
|
|
7
|
+
* engine stays neutral and works unchanged in a headless cook.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time state for expression evaluation.
|
|
3
|
+
*
|
|
4
|
+
* Frames are stored as a real number so sub-frame time exists. `frame` is the
|
|
5
|
+
* integer frame ($F), `frameFraction` is the exact fractional frame ($FF), and
|
|
6
|
+
* `time` is seconds ($T). Nothing here reads a clock: time is set by the caller
|
|
7
|
+
* (a cook request, a frame-range loop, a transport) and the engine only ever
|
|
8
|
+
* reports what it was told. That is what keeps a cook deterministic.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_FPS = 30;
|
|
11
|
+
export declare const MIN_FRAME = 1;
|
|
12
|
+
export declare class TimeState {
|
|
13
|
+
/** Exact, possibly fractional frame. Never below MIN_FRAME. */
|
|
14
|
+
private _frame;
|
|
15
|
+
private _fps;
|
|
16
|
+
/**
|
|
17
|
+
* Set the current frame. Fractional values are preserved; only the lower
|
|
18
|
+
* bound is enforced. Non-finite input is ignored.
|
|
19
|
+
*/
|
|
20
|
+
setFrame(frame: number): void;
|
|
21
|
+
/** Set the current time in seconds, keeping the sub-frame remainder. */
|
|
22
|
+
setTime(seconds: number): void;
|
|
23
|
+
setFps(fps: number): void;
|
|
24
|
+
/** Integer frame — $F. */
|
|
25
|
+
get frame(): number;
|
|
26
|
+
/** Fractional frame — $FF. */
|
|
27
|
+
get frameFraction(): number;
|
|
28
|
+
/**
|
|
29
|
+
* Seconds — $T. Derived from the fractional frame, so it round-trips.
|
|
30
|
+
*
|
|
31
|
+
* Zero at the first frame, which is Houdini's convention: $T = ($FF - 1)/$FPS.
|
|
32
|
+
* The previous definition was frame/fps, making $T equal 1/fps at frame 1 —
|
|
33
|
+
* so `sin($T)` started part-way through its cycle and an animation's first
|
|
34
|
+
* frame was never t=0. Changed 2026-09-07 because Marcus named Houdini as the
|
|
35
|
+
* reference for these variables, and because "time starts at zero" is the
|
|
36
|
+
* thing anybody writing an expression will assume.
|
|
37
|
+
*/
|
|
38
|
+
get time(): number;
|
|
39
|
+
/** Frame rate — $FPS. */
|
|
40
|
+
get fps(): number;
|
|
41
|
+
/** Immutable snapshot of the four time variables. */
|
|
42
|
+
snapshot(): TimeVariables;
|
|
43
|
+
}
|
|
44
|
+
export interface TimeVariables {
|
|
45
|
+
/** Integer frame — $F */
|
|
46
|
+
frame: number;
|
|
47
|
+
/** Fractional frame — $FF */
|
|
48
|
+
frameFraction: number;
|
|
49
|
+
/** Seconds — $T */
|
|
50
|
+
time: number;
|
|
51
|
+
/** Frame rate — $FPS */
|
|
52
|
+
fps: number;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=time.d.ts.map
|