@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.
Files changed (233) hide show
  1. package/AGENTS.md +47 -7
  2. package/ARCHITECTURE.md +30 -2
  3. package/CHANGELOG.md +107 -0
  4. package/DESIGN.md +10 -5
  5. package/README.md +103 -7
  6. package/dist/assets/CodeEditor-D22fVi-4.js +623 -0
  7. package/dist/assets/CodePanel-DPNEYfcR.js +2 -0
  8. package/dist/assets/CustomNodeDialog-DeHCAvgb.js +1 -0
  9. package/dist/assets/{ExportDialog-DWN4nhXI.js → ExportDialog-BJu2Bpvv.js} +2 -2
  10. package/dist/assets/ProjectSettings-DWpVtWEx.js +2 -0
  11. package/dist/assets/SettingsDialog-za00IS3L.js +1 -0
  12. package/dist/assets/VersionHistoryDialog-DTMGhI0_.js +3 -0
  13. package/dist/assets/{core-Bji4aZUU.js → core-JsyKMO1k.js} +29 -38
  14. package/dist/assets/{cssMode-B6Z_Q1Kh.js → cssMode-emdRheFK.js} +1 -1
  15. package/dist/assets/{editor.api-jMoW_43X.js → editor.api-xHX-fMSo.js} +1 -1
  16. package/dist/assets/{freemarker2-DvvPV6m5.js → freemarker2-C3M8_Gs3.js} +1 -1
  17. package/dist/assets/geo-BAR6lQp5.js +1 -0
  18. package/dist/assets/{handlebars-B8rfuSIa.js → handlebars-CBMurtFk.js} +1 -1
  19. package/dist/assets/{html-DyHm6JCp.js → html-xbLQwknw.js} +1 -1
  20. package/dist/assets/{htmlMode-E9XLWE5u.js → htmlMode-2wKdDTon.js} +1 -1
  21. package/dist/assets/{image-BUcQC7PG.js → image-i1j6nWKe.js} +26 -40
  22. package/dist/assets/index-BDV5xGBZ.css +1 -0
  23. package/dist/assets/index-CMcH8AgP.js +212 -0
  24. package/dist/assets/index-client-BoJgsN9M.js +1 -0
  25. package/dist/assets/{javascript-C_bM5PCN.js → javascript-DJaOo_2I.js} +1 -1
  26. package/dist/assets/{jsonMode-BdyVS7G4.js → jsonMode-Bheas8_0.js} +1 -1
  27. package/dist/assets/{legacy-DIu3XRwl.js → legacy-CAj2_7QE.js} +2 -2
  28. package/dist/assets/{liquid-CDkn3spA.js → liquid-BCj_BGbM.js} +1 -1
  29. package/dist/assets/{lspLanguageFeatures-AB5DMAsr.js → lspLanguageFeatures-BfIsx_xz.js} +1 -1
  30. package/dist/assets/{mdx-BvH_NjDN.js → mdx-DwiQDiVO.js} +1 -1
  31. package/dist/assets/{nodeTypeUtils-CweA3NHd.js → nodeTypeUtils-BBA1JqHx.js} +1 -1
  32. package/dist/assets/{python-B7uii4fi.js → python-BjrB5nqj.js} +1 -1
  33. package/dist/assets/{razor-Z48fWdJc.js → razor-CfoBmGDs.js} +1 -1
  34. package/dist/assets/{tsMode-BDgacVG4.js → tsMode-BcEoMSxp.js} +1 -1
  35. package/dist/assets/{typescript-Derb6Oqa.js → typescript-DIg7TEmQ.js} +1 -1
  36. package/dist/assets/{workers-BtoMX_pm.js → workers-vXYuwtPw.js} +1 -1
  37. package/dist/assets/{xml-B1CaFSrG.js → xml-DexLQIQy.js} +1 -1
  38. package/dist/assets/{yaml-HmIeA0KH.js → yaml-DUnD_1dE.js} +1 -1
  39. package/dist/cli/index.js +16467 -10200
  40. package/dist/cli/runtime/gpu.ts +76 -0
  41. package/dist/cli/runtime/io.ts +93 -7
  42. package/dist/contracts/animation.d.ts +40 -0
  43. package/dist/contracts/animation.js +14 -0
  44. package/dist/contracts/capabilities.d.ts +69 -6
  45. package/dist/contracts/definition.d.ts +60 -2
  46. package/dist/contracts/document.d.ts +15 -0
  47. package/dist/contracts/index.d.ts +1 -0
  48. package/dist/contracts/index.js +1 -0
  49. package/dist/contracts/schema.js +8 -5
  50. package/dist/contracts/validate.d.ts +10 -1
  51. package/dist/contracts/validate.js +20 -3
  52. package/dist/gpu.d.ts +71 -0
  53. package/dist/gpu.js +53 -0
  54. package/dist/graphs/default.cascade +1 -1
  55. package/dist/index.html +5 -5
  56. package/dist/io.d.ts +196 -0
  57. package/dist/io.js +303 -0
  58. package/dist/net.d.ts +1 -0
  59. package/dist/net.js +28 -0
  60. package/dist/runtime/animation/channel.d.ts +108 -0
  61. package/dist/runtime/animation/channel.js +204 -0
  62. package/dist/runtime/animation/frameRange.d.ts +78 -0
  63. package/dist/runtime/animation/frameRange.js +76 -0
  64. package/dist/runtime/animation/index.d.ts +10 -0
  65. package/dist/runtime/animation/index.js +8 -0
  66. package/dist/runtime/builtins/core/random.d.ts +1 -1
  67. package/dist/runtime/builtins/core/remap.d.ts +1 -1
  68. package/dist/runtime/builtins/geo/copyToPoints.d.ts +1 -1
  69. package/dist/runtime/builtins/geo/copyToPoints.js +1 -1
  70. package/dist/runtime/builtins/geo/generators.d.ts +1 -1
  71. package/dist/runtime/builtins/geo/merge.d.ts +1 -1
  72. package/dist/runtime/builtins/geo/svgExport.d.ts +1 -1
  73. package/dist/runtime/builtins/geo/svgExport.js +1 -1
  74. package/dist/runtime/builtins/geo/transform.d.ts +1 -1
  75. package/dist/runtime/builtins/geo/transform.js +1 -1
  76. package/dist/runtime/definition/architecture.d.ts +1 -1
  77. package/dist/runtime/definition/extract.d.ts +1 -1
  78. package/dist/runtime/definition/extract.js +1 -1
  79. package/dist/runtime/error.d.ts +1 -1
  80. package/dist/runtime/expressions/evaluator.d.ts +53 -0
  81. package/dist/runtime/expressions/evaluator.js +193 -0
  82. package/dist/runtime/expressions/index.d.ts +15 -0
  83. package/dist/runtime/expressions/index.js +12 -0
  84. package/dist/runtime/expressions/math.d.ts +75 -0
  85. package/dist/runtime/expressions/math.js +140 -0
  86. package/dist/runtime/expressions/preprocess.d.ts +33 -0
  87. package/dist/runtime/expressions/preprocess.js +102 -0
  88. package/dist/runtime/expressions/scope.d.ts +27 -0
  89. package/dist/runtime/expressions/scope.js +10 -0
  90. package/dist/runtime/expressions/time.d.ts +54 -0
  91. package/dist/runtime/expressions/time.js +71 -0
  92. package/dist/runtime/expressions/types.d.ts +66 -0
  93. package/dist/runtime/expressions/types.js +9 -0
  94. package/dist/runtime/geometry/attributes.d.ts +2 -2
  95. package/dist/runtime/geometry/attributes.js +1 -1
  96. package/dist/runtime/geometry/builder.d.ts +1 -1
  97. package/dist/runtime/geometry/builder.js +1 -1
  98. package/dist/runtime/geometry/copy.d.ts +1 -1
  99. package/dist/runtime/geometry/copy.js +1 -1
  100. package/dist/runtime/geometry/generate.d.ts +1 -1
  101. package/dist/runtime/geometry/groups.d.ts +1 -1
  102. package/dist/runtime/geometry/groups.js +1 -1
  103. package/dist/runtime/geometry/matrix.d.ts +1 -1
  104. package/dist/runtime/geometry/merge.d.ts +1 -1
  105. package/dist/runtime/geometry/merge.js +1 -1
  106. package/dist/runtime/geometry/primitives.d.ts +1 -1
  107. package/dist/runtime/geometry/primitives.js +1 -1
  108. package/dist/runtime/geometry/promote.d.ts +1 -1
  109. package/dist/runtime/geometry/promote.js +1 -1
  110. package/dist/runtime/geometry/resample.d.ts +1 -1
  111. package/dist/runtime/geometry/resample.js +1 -1
  112. package/dist/runtime/geometry/svg.d.ts +1 -1
  113. package/dist/runtime/geometry/svg.js +1 -1
  114. package/dist/runtime/geometry/transform.d.ts +1 -1
  115. package/dist/runtime/geometry/transform.js +1 -1
  116. package/dist/runtime/index.d.ts +4 -1
  117. package/dist/runtime/index.js +4 -1
  118. package/dist/runtime/params/animator.d.ts +74 -0
  119. package/dist/runtime/params/animator.js +137 -0
  120. package/dist/runtime/params/index.d.ts +11 -0
  121. package/dist/runtime/params/index.js +9 -0
  122. package/dist/runtime/params/resolve.d.ts +73 -0
  123. package/dist/runtime/params/resolve.js +62 -0
  124. package/dist/runtime/runtime.d.ts +42 -0
  125. package/dist/runtime/runtime.js +351 -29
  126. package/dist/runtime/types.d.ts +32 -6
  127. package/dist/shell.d.ts +3 -3
  128. package/doc/NODE_REFERENCE.md +395 -0
  129. package/doc/PROJECT_AUTHORING.md +148 -6
  130. package/package.json +30 -3
  131. package/dist/assets/CodeEditor-DoaNEUaM.js +0 -641
  132. package/dist/assets/CodePanel-B0_2XJ-t.js +0 -2
  133. package/dist/assets/CustomNodeDialog-026lzecD.js +0 -1
  134. package/dist/assets/ProjectSettings-BzT164Yx.js +0 -2
  135. package/dist/assets/SettingsDialog-DmHb1V0r.js +0 -1
  136. package/dist/assets/VersionHistoryDialog-hH6VsXLX.js +0 -3
  137. package/dist/assets/geo-CXmCtzMM.js +0 -2
  138. package/dist/assets/index-BXYXSdgh.js +0 -224
  139. package/dist/assets/index-CrNke7SW.css +0 -1
  140. package/dist/assets/index-client-DN_FvjN8.js +0 -1
  141. package/dist/assets/quill-DjZ69wL_.js +0 -121
  142. package/dist/cli/index.js.map +0 -7
  143. package/dist/contracts/capabilities.d.ts.map +0 -1
  144. package/dist/contracts/capabilities.js.map +0 -1
  145. package/dist/contracts/color.d.ts.map +0 -1
  146. package/dist/contracts/color.js.map +0 -1
  147. package/dist/contracts/definition.d.ts.map +0 -1
  148. package/dist/contracts/definition.js.map +0 -1
  149. package/dist/contracts/document.d.ts.map +0 -1
  150. package/dist/contracts/document.js.map +0 -1
  151. package/dist/contracts/geometry.d.ts.map +0 -1
  152. package/dist/contracts/geometry.js.map +0 -1
  153. package/dist/contracts/index.d.ts.map +0 -1
  154. package/dist/contracts/index.js.map +0 -1
  155. package/dist/contracts/schema.d.ts.map +0 -1
  156. package/dist/contracts/schema.js.map +0 -1
  157. package/dist/contracts/validate.d.ts.map +0 -1
  158. package/dist/contracts/validate.js.map +0 -1
  159. package/dist/contracts/values.d.ts.map +0 -1
  160. package/dist/contracts/values.js.map +0 -1
  161. package/dist/graphs/default-orig.cascade +0 -128
  162. package/dist/runtime/browser.d.ts.map +0 -1
  163. package/dist/runtime/browser.js.map +0 -1
  164. package/dist/runtime/builtins/core/index.d.ts.map +0 -1
  165. package/dist/runtime/builtins/core/index.js.map +0 -1
  166. package/dist/runtime/builtins/core/random.d.ts.map +0 -1
  167. package/dist/runtime/builtins/core/random.js.map +0 -1
  168. package/dist/runtime/builtins/core/remap.d.ts.map +0 -1
  169. package/dist/runtime/builtins/core/remap.js.map +0 -1
  170. package/dist/runtime/builtins/core/routing.d.ts.map +0 -1
  171. package/dist/runtime/builtins/core/routing.js.map +0 -1
  172. package/dist/runtime/builtins/core/structural.d.ts.map +0 -1
  173. package/dist/runtime/builtins/core/structural.js.map +0 -1
  174. package/dist/runtime/builtins/geo/copyToPoints.d.ts.map +0 -1
  175. package/dist/runtime/builtins/geo/copyToPoints.js.map +0 -1
  176. package/dist/runtime/builtins/geo/definitions.d.ts.map +0 -1
  177. package/dist/runtime/builtins/geo/definitions.js.map +0 -1
  178. package/dist/runtime/builtins/geo/generators.d.ts.map +0 -1
  179. package/dist/runtime/builtins/geo/generators.js.map +0 -1
  180. package/dist/runtime/builtins/geo/index.d.ts.map +0 -1
  181. package/dist/runtime/builtins/geo/index.js.map +0 -1
  182. package/dist/runtime/builtins/geo/merge.d.ts.map +0 -1
  183. package/dist/runtime/builtins/geo/merge.js.map +0 -1
  184. package/dist/runtime/builtins/geo/namespace.d.ts.map +0 -1
  185. package/dist/runtime/builtins/geo/namespace.js.map +0 -1
  186. package/dist/runtime/builtins/geo/svgExport.d.ts.map +0 -1
  187. package/dist/runtime/builtins/geo/svgExport.js.map +0 -1
  188. package/dist/runtime/builtins/geo/transform.d.ts.map +0 -1
  189. package/dist/runtime/builtins/geo/transform.js.map +0 -1
  190. package/dist/runtime/builtins/index.d.ts.map +0 -1
  191. package/dist/runtime/builtins/index.js.map +0 -1
  192. package/dist/runtime/definition/architecture.d.ts.map +0 -1
  193. package/dist/runtime/definition/architecture.js.map +0 -1
  194. package/dist/runtime/definition/extract.d.ts.map +0 -1
  195. package/dist/runtime/definition/extract.js.map +0 -1
  196. package/dist/runtime/error.d.ts.map +0 -1
  197. package/dist/runtime/error.js.map +0 -1
  198. package/dist/runtime/geometry/attributes.d.ts.map +0 -1
  199. package/dist/runtime/geometry/attributes.js.map +0 -1
  200. package/dist/runtime/geometry/builder.d.ts.map +0 -1
  201. package/dist/runtime/geometry/builder.js.map +0 -1
  202. package/dist/runtime/geometry/copy.d.ts.map +0 -1
  203. package/dist/runtime/geometry/copy.js.map +0 -1
  204. package/dist/runtime/geometry/generate.d.ts.map +0 -1
  205. package/dist/runtime/geometry/generate.js.map +0 -1
  206. package/dist/runtime/geometry/groups.d.ts.map +0 -1
  207. package/dist/runtime/geometry/groups.js.map +0 -1
  208. package/dist/runtime/geometry/index.d.ts.map +0 -1
  209. package/dist/runtime/geometry/index.js.map +0 -1
  210. package/dist/runtime/geometry/matrix.d.ts.map +0 -1
  211. package/dist/runtime/geometry/matrix.js.map +0 -1
  212. package/dist/runtime/geometry/merge.d.ts.map +0 -1
  213. package/dist/runtime/geometry/merge.js.map +0 -1
  214. package/dist/runtime/geometry/primitives.d.ts.map +0 -1
  215. package/dist/runtime/geometry/primitives.js.map +0 -1
  216. package/dist/runtime/geometry/promote.d.ts.map +0 -1
  217. package/dist/runtime/geometry/promote.js.map +0 -1
  218. package/dist/runtime/geometry/resample.d.ts.map +0 -1
  219. package/dist/runtime/geometry/resample.js.map +0 -1
  220. package/dist/runtime/geometry/svg.d.ts.map +0 -1
  221. package/dist/runtime/geometry/svg.js.map +0 -1
  222. package/dist/runtime/geometry/transform.d.ts.map +0 -1
  223. package/dist/runtime/geometry/transform.js.map +0 -1
  224. package/dist/runtime/index.d.ts.map +0 -1
  225. package/dist/runtime/index.js.map +0 -1
  226. package/dist/runtime/node.d.ts.map +0 -1
  227. package/dist/runtime/node.js.map +0 -1
  228. package/dist/runtime/runtime.d.ts.map +0 -1
  229. package/dist/runtime/runtime.js.map +0 -1
  230. package/dist/runtime/types.d.ts.map +0 -1
  231. package/dist/runtime/types.js.map +0 -1
  232. package/dist/shell.js.map +0 -7
  233. package/dist/stage.js.map +0 -7
@@ -0,0 +1,71 @@
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 const DEFAULT_FPS = 30;
11
+ export const MIN_FRAME = 1;
12
+ export class TimeState {
13
+ /** Exact, possibly fractional frame. Never below MIN_FRAME. */
14
+ _frame = MIN_FRAME;
15
+ _fps = DEFAULT_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) {
21
+ if (!Number.isFinite(frame))
22
+ return;
23
+ this._frame = Math.max(MIN_FRAME, frame);
24
+ }
25
+ /** Set the current time in seconds, keeping the sub-frame remainder. */
26
+ setTime(seconds) {
27
+ if (!Number.isFinite(seconds))
28
+ return;
29
+ this.setFrame(seconds * this._fps + MIN_FRAME);
30
+ }
31
+ setFps(fps) {
32
+ if (!Number.isFinite(fps))
33
+ return;
34
+ this._fps = Math.max(1, fps);
35
+ }
36
+ /** Integer frame — $F. */
37
+ get frame() {
38
+ return Math.floor(this._frame);
39
+ }
40
+ /** Fractional frame — $FF. */
41
+ get frameFraction() {
42
+ return this._frame;
43
+ }
44
+ /**
45
+ * Seconds — $T. Derived from the fractional frame, so it round-trips.
46
+ *
47
+ * Zero at the first frame, which is Houdini's convention: $T = ($FF - 1)/$FPS.
48
+ * The previous definition was frame/fps, making $T equal 1/fps at frame 1 —
49
+ * so `sin($T)` started part-way through its cycle and an animation's first
50
+ * frame was never t=0. Changed 2026-09-07 because Marcus named Houdini as the
51
+ * reference for these variables, and because "time starts at zero" is the
52
+ * thing anybody writing an expression will assume.
53
+ */
54
+ get time() {
55
+ return (this._frame - MIN_FRAME) / this._fps;
56
+ }
57
+ /** Frame rate — $FPS. */
58
+ get fps() {
59
+ return this._fps;
60
+ }
61
+ /** Immutable snapshot of the four time variables. */
62
+ snapshot() {
63
+ return {
64
+ frame: this.frame,
65
+ frameFraction: this.frameFraction,
66
+ time: this.time,
67
+ fps: this.fps
68
+ };
69
+ }
70
+ }
71
+ //# sourceMappingURL=time.js.map
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Expression types.
3
+ *
4
+ * These live in the runtime rather than in contracts because a compiled
5
+ * expression carries a function, so it is runtime machinery rather than a
6
+ * serializable DTO. Contracts stays dependency-free and data-only.
7
+ */
8
+ import type { Node as NodeShape } from "./scope.js";
9
+ /**
10
+ * The context an expression is evaluated against. Everything an expression can
11
+ * see is on this object — there is no ambient global, and in particular no
12
+ * clock: the time variables are values placed here by the caller.
13
+ */
14
+ export interface ExpressionContext<TNode = NodeShape> {
15
+ /** seconds — $T */
16
+ time: number;
17
+ /** integer frame — $F */
18
+ frame: number;
19
+ /** fractional frame — $FF */
20
+ fframe: number;
21
+ /** rate — $FPS */
22
+ fps: number;
23
+ /** The node whose parameter is being evaluated. */
24
+ self: TNode;
25
+ ch: (path: string) => number;
26
+ chs: (path: string) => string;
27
+ chv: (path: string) => number[];
28
+ opexist: (path: string) => 0 | 1;
29
+ opinput: (index: number) => TNode | null;
30
+ fit: (value: number, oldMin: number, oldMax: number, newMin: number, newMax: number) => number;
31
+ fit01: (value: number, newMin: number, newMax: number) => number;
32
+ clamp: (value: number, min: number, max: number) => number;
33
+ lerp: (a: number, b: number, t: number) => number;
34
+ smooth: (value: number, min: number, max: number) => number;
35
+ noise: (...coords: number[]) => number;
36
+ random: (seed: number) => number;
37
+ padzero: (digits: number, value: number) => string;
38
+ Math: typeof Math;
39
+ }
40
+ /** A compiled, cached expression ready to evaluate against a context. */
41
+ export interface CompiledExpression<TNode = NodeShape> {
42
+ /** The expression as authored, `$` spellings intact. */
43
+ source: string;
44
+ /** The expression after `$` preprocessing — what was actually compiled. */
45
+ compiledSource: string;
46
+ evaluate: (context: ExpressionContext<TNode>) => unknown;
47
+ /** Parameter paths referenced through ch/chs/chv, for dirty tracking. */
48
+ dependencies: string[];
49
+ /** True if the expression references any time variable, in either spelling. */
50
+ isTimeDependent: boolean;
51
+ error?: string;
52
+ }
53
+ /** Result of evaluating one expression. */
54
+ export interface ExpressionResult {
55
+ value: unknown;
56
+ error?: string;
57
+ }
58
+ /** A referenced path that does not resolve. */
59
+ export interface PathWarning {
60
+ nodeId: string;
61
+ nodePath: string;
62
+ propName: string;
63
+ expression: string;
64
+ brokenPath: string;
65
+ }
66
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Expression types.
3
+ *
4
+ * These live in the runtime rather than in contracts because a compiled
5
+ * expression carries a function, so it is runtime machinery rather than a
6
+ * serializable DTO. Contracts stays dependency-free and data-only.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -1,4 +1,4 @@
1
- import { type AnyAttribute, type Attribute, type AttributeLevel, type AttributeSet, type AttributeStorage, type DetailValue, type ElementLevel, type Geometry, type StringAttribute } from "cascade/contracts";
1
+ import { type AnyAttribute, type Attribute, type AttributeLevel, type AttributeSet, type AttributeStorage, type DetailValue, type ElementLevel, type Geometry, type StringAttribute } from "@field/cascade/contracts";
2
2
  /**
3
3
  * Attribute reads and writes. Every write returns a new `Geometry` and shares
4
4
  * the arrays it did not touch, which is the whole reason the format is
@@ -61,5 +61,5 @@ export interface AttributeCreateOptions {
61
61
  */
62
62
  export declare function attributeCreate(geometry: Geometry, options: AttributeCreateOptions): Geometry;
63
63
  /** A same-storage copy of an attribute's array, for a write-in-place pass. */
64
- export declare function copyAttributeArray(attribute: Attribute): import("cascade/contracts").AttributeArray;
64
+ export declare function copyAttributeArray(attribute: Attribute): import("@field/cascade/contracts").AttributeArray;
65
65
  //# sourceMappingURL=attributes.d.ts.map
@@ -1,4 +1,4 @@
1
- import { attributeArray, attributeArrayFrom, createGeometry, isStringAttribute, levelCount, POSITION_ATTRIBUTE, } from "cascade/contracts";
1
+ import { attributeArray, attributeArrayFrom, createGeometry, isStringAttribute, levelCount, POSITION_ATTRIBUTE, } from "@field/cascade/contracts";
2
2
  /**
3
3
  * Attribute reads and writes. Every write returns a new `Geometry` and shares
4
4
  * the arrays it did not touch, which is the whole reason the format is
@@ -1,4 +1,4 @@
1
- import { emptyGeometry, type AnyAttribute, type AttributeStorage, type DetailValue, type ElementLevel, type Geometry, type PrimitiveKind } from "cascade/contracts";
1
+ import { emptyGeometry, type AnyAttribute, type AttributeStorage, type DetailValue, type ElementLevel, type Geometry, type PrimitiveKind } from "@field/cascade/contracts";
2
2
  export { emptyGeometry };
3
3
  export interface GeometryBuilderOptions {
4
4
  /** 2 by default: the reference workloads are two-component. */
@@ -1,4 +1,4 @@
1
- import { attributeArrayFrom, createGeometry, emptyGeometry, POSITION_ATTRIBUTE, PRIMITIVE_KINDS, } from "cascade/contracts";
1
+ import { attributeArrayFrom, createGeometry, emptyGeometry, POSITION_ATTRIBUTE, PRIMITIVE_KINDS, } from "@field/cascade/contracts";
2
2
  import { createStringAttribute, geometryError } from "./attributes.js";
3
3
  export { emptyGeometry };
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { type Geometry } from "cascade/contracts";
1
+ import { type Geometry } from "@field/cascade/contracts";
2
2
  /**
3
3
  * Copy to Points: one geometry instanced onto every point of another.
4
4
  *
@@ -1,4 +1,4 @@
1
- import { emptyGeometry, isStringAttribute, POSITION_ATTRIBUTE, } from "cascade/contracts";
1
+ import { emptyGeometry, isStringAttribute, POSITION_ATTRIBUTE, } from "@field/cascade/contracts";
2
2
  import { createAttribute, createStringAttribute, geometryError, getPosition, numericAttribute, positionSize, readComponent, readElement, readString, setAttribute, } from "./attributes.js";
3
3
  import { groupIndices, groupMask } from "./groups.js";
4
4
  import { transformMatrix } from "./matrix.js";
@@ -1,4 +1,4 @@
1
- import type { Geometry } from "cascade/contracts";
1
+ import type { Geometry } from "@field/cascade/contracts";
2
2
  /**
3
3
  * The generators. Each returns one primitive in a geometry of its own, so a
4
4
  * graph composes them with `Merge` rather than with a multi-shape node.
@@ -1,4 +1,4 @@
1
- import { type Geometry } from "cascade/contracts";
1
+ import { type Geometry } from "@field/cascade/contracts";
2
2
  /**
3
3
  * Groups are named subsets, stored as one byte per element. A mask is the right
4
4
  * shape because membership is tested far more often than it is enumerated, and
@@ -1,4 +1,4 @@
1
- import { createGeometry, levelCount } from "cascade/contracts";
1
+ import { createGeometry, levelCount } from "@field/cascade/contracts";
2
2
  import { geometryError } from "./attributes.js";
3
3
  const KEYS = {
4
4
  point: "pointGroups",
@@ -1,4 +1,4 @@
1
- import type { Mat3 } from "cascade/contracts";
1
+ import type { Mat3 } from "@field/cascade/contracts";
2
2
  /**
3
3
  * The `mat3` a transform node or an instancing operation needs, built from the
4
4
  * four parameters a Houdini user already knows: translate, rotate, scale and
@@ -1,4 +1,4 @@
1
- import { type AttributeStorage, type Geometry } from "cascade/contracts";
1
+ import { type AttributeStorage, type Geometry } from "@field/cascade/contracts";
2
2
  /**
3
3
  * Merge concatenates geometries and renumbers points, vertices and primitives.
4
4
  *
@@ -1,4 +1,4 @@
1
- import { attributeArray, createGeometry, emptyGeometry, isStringAttribute, levelCount, } from "cascade/contracts";
1
+ import { attributeArray, createGeometry, emptyGeometry, isStringAttribute, levelCount, } from "@field/cascade/contracts";
2
2
  import { createStringAttribute, geometryError, readString } from "./attributes.js";
3
3
  /**
4
4
  * Merge concatenates geometries and renumbers points, vertices and primitives.
@@ -1,4 +1,4 @@
1
- import type { Geometry, PrimitiveKind } from "cascade/contracts";
1
+ import type { Geometry, PrimitiveKind } from "@field/cascade/contracts";
2
2
  /** Topology and measurement reads that the operations share. */
3
3
  export declare function primitiveKind(geometry: Geometry, primitive: number): PrimitiveKind;
4
4
  export declare function isClosed(geometry: Geometry, primitive: number): boolean;
@@ -1,4 +1,4 @@
1
- import { BEZIER_STRIDE, PRIMITIVE_KINDS } from "cascade/contracts";
1
+ import { BEZIER_STRIDE, PRIMITIVE_KINDS } from "@field/cascade/contracts";
2
2
  import { geometryError, positionAttribute, positionSize, } from "./attributes.js";
3
3
  /** Topology and measurement reads that the operations share. */
4
4
  export function primitiveKind(geometry, primitive) {
@@ -1,4 +1,4 @@
1
- import { type AttributeLevel, type ElementLevel, type Geometry } from "cascade/contracts";
1
+ import { type AttributeLevel, type ElementLevel, type Geometry } from "@field/cascade/contracts";
2
2
  /**
3
3
  * Moving an attribute between levels, and copying one between geometries.
4
4
  *
@@ -1,4 +1,4 @@
1
- import { attributeArray, isStringAttribute, levelCount, POSITION_ATTRIBUTE, } from "cascade/contracts";
1
+ import { attributeArray, isStringAttribute, levelCount, POSITION_ATTRIBUTE, } from "@field/cascade/contracts";
2
2
  import { createStringAttribute, geometryError, readString, removeAttribute, removeDetail, setAttribute, setDetail, } from "./attributes.js";
3
3
  /**
4
4
  * Which source elements feed each target element. This one table is the whole
@@ -1,4 +1,4 @@
1
- import { type Geometry } from "cascade/contracts";
1
+ import { type Geometry } from "@field/cascade/contracts";
2
2
  /**
3
3
  * Rewrite each primitive at a uniform arc-length spacing, or at a target
4
4
  * vertex count. Most curve operations want a uniformly sampled chain, so this
@@ -1,4 +1,4 @@
1
- import { attributeArray, createGeometry, isStringAttribute, } from "cascade/contracts";
1
+ import { attributeArray, createGeometry, isStringAttribute, } from "@field/cascade/contracts";
2
2
  import { createStringAttribute, geometryError, readString, } from "./attributes.js";
3
3
  import { groupMask } from "./groups.js";
4
4
  import { arcLengths, isBezier, isClosed, loosePointMask, primitivePoints, } from "./primitives.js";
@@ -1,4 +1,4 @@
1
- import { Color, type Geometry } from "cascade/contracts";
1
+ import { Color, type Geometry } from "@field/cascade/contracts";
2
2
  /**
3
3
  * Geometry to an SVG document. The node that makes the library provably useful,
4
4
  * because it produces a file to print or plot rather than an assertion.
@@ -1,4 +1,4 @@
1
- import { Color, isStringAttribute, } from "cascade/contracts";
1
+ import { Color, isStringAttribute, } from "@field/cascade/contracts";
2
2
  import { geometryError, numericAttribute, positionAttribute, readComponent, } from "./attributes.js";
3
3
  import { groupNames } from "./groups.js";
4
4
  import { bezierSegments, geometryBounds, isBezier, isClosed, loosePointMask, primitivePoints, } from "./primitives.js";
@@ -1,4 +1,4 @@
1
- import { type Geometry, type Mat3, type Mat4 } from "cascade/contracts";
1
+ import { type Geometry, type Mat3, type Mat4 } from "@field/cascade/contracts";
2
2
  /**
3
3
  * Transform `P`. Matrices are column-major, as the `mat3` and `mat4` types are
4
4
  * already documented to be, so a `mat3`'s third column carries the 2D
@@ -1,4 +1,4 @@
1
- import { POSITION_ATTRIBUTE, } from "cascade/contracts";
1
+ import { POSITION_ATTRIBUTE, } from "@field/cascade/contracts";
2
2
  import { attributeSet, copyAttributeArray, geometryError, positionAttribute, withAttributeSet, } from "./attributes.js";
3
3
  /**
4
4
  * Transform `P`. Matrices are column-major, as the `mat3` and `mat4` types are
@@ -1,5 +1,8 @@
1
1
  export { CascadeRuntimeError } from "./error.js";
2
+ export * from "./animation/index.js";
3
+ export * from "./expressions/index.js";
2
4
  export * from "./geometry/index.js";
3
- export { createRuntime } from "./runtime.js";
5
+ export * from "./params/index.js";
6
+ export { createRuntime, PREFLIGHT_WARNING_CODES } from "./runtime.js";
4
7
  export type * from "./types.js";
5
8
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,7 @@
1
1
  export { CascadeRuntimeError } from "./error.js";
2
+ export * from "./animation/index.js";
3
+ export * from "./expressions/index.js";
2
4
  export * from "./geometry/index.js";
3
- export { createRuntime } from "./runtime.js";
5
+ export * from "./params/index.js";
6
+ export { createRuntime, PREFLIGHT_WARNING_CODES } from "./runtime.js";
4
7
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Resolving a graph's bound parameters at a frame.
3
+ *
4
+ * `resolvePropBinding` answers "what is this one parameter worth" and owns the
5
+ * order. This is the layer above it: a graph's worth of parameters, an
6
+ * expression engine bound to that graph, and the frame they are all resolved
7
+ * at. The deterministic runtime had neither half — it read `saved.value` and
8
+ * stopped — so a definition-v1 prop carrying `$T` or a drawn curve rendered as
9
+ * a constant offline while the same document animated in Studio.
10
+ *
11
+ * It is generic over the node rather than importing the runtime's own, so this
12
+ * file has no dependency on `runtime.ts` and can be exercised on its own.
13
+ *
14
+ * Resolution is LAZY and cycle-guarded, which is not a detail: `ch('../a/x')`
15
+ * may name a parameter that is itself an expression, and resolving in node
16
+ * order would hand out last frame's value for half the graph depending on
17
+ * where the reference pointed. Pulling on demand gives the same answer
18
+ * whatever order the nodes are in, which is the whole promise of the word
19
+ * deterministic.
20
+ */
21
+ import type { PropBinding } from "./resolve.js";
22
+ /** The minimum a node must be for its parameters to be resolvable. */
23
+ export interface AnimatableNode {
24
+ readonly id: string;
25
+ /** Resolved values, written by `resolveAll`. */
26
+ readonly props: Record<string, unknown>;
27
+ readonly inputs: Record<string, unknown>;
28
+ /** Only the props that carry an expression or a channel. */
29
+ readonly bindings: Record<string, PropBinding>;
30
+ }
31
+ export interface PropAnimatorTopology<TNode extends AnimatableNode> {
32
+ readonly nodes: readonly TNode[];
33
+ node(id: string): TNode | undefined;
34
+ /** The node feeding `node`'s data input at `index`, for `opinput()`. */
35
+ inputNode(node: TNode, index: number): TNode | null;
36
+ /** A failed expression. The parameter has already fallen back to its stored
37
+ * value; this is so the host can say so rather than render a wrong number
38
+ * in silence. */
39
+ report?(nodeId: string, propName: string, error: string): void;
40
+ }
41
+ export declare class PropAnimator<TNode extends AnimatableNode> {
42
+ private readonly topology;
43
+ private readonly evaluator;
44
+ private readonly resolving;
45
+ constructor(topology: PropAnimatorTopology<TNode>);
46
+ /** True when any parameter in the graph is bound at all — so a host can skip
47
+ * the whole pass, and a graph with no animation costs nothing. */
48
+ get animated(): boolean;
49
+ setFrame(frame: number): void;
50
+ setFps(fps: number): void;
51
+ /** Write every bound parameter's value at the current frame into `props`. */
52
+ resolveAll(): void;
53
+ /**
54
+ * One parameter, at the current frame.
55
+ *
56
+ * Re-entry returns the stored value rather than throwing: a parameter whose
57
+ * expression refers back to itself is an author's mistake, and the render
58
+ * that names it is more use than the one that dies.
59
+ */
60
+ private resolve;
61
+ /**
62
+ * The host binding the expression engine reads through.
63
+ *
64
+ * A path names a parameter — `./radius`, `../blur1/amount`,
65
+ * `/root/blur1/amount` — and the last segment is always the parameter, the
66
+ * one before it the node. Anything a graph does not have resolves to null,
67
+ * which the engine turns into 0 / '' / [] per accessor rather than an
68
+ * exception: one broken reference must not take a render down.
69
+ */
70
+ private scope;
71
+ /** The node a path's leading segments name, or `from` when they name none. */
72
+ private owner;
73
+ }
74
+ //# sourceMappingURL=animator.d.ts.map
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Resolving a graph's bound parameters at a frame.
3
+ *
4
+ * `resolvePropBinding` answers "what is this one parameter worth" and owns the
5
+ * order. This is the layer above it: a graph's worth of parameters, an
6
+ * expression engine bound to that graph, and the frame they are all resolved
7
+ * at. The deterministic runtime had neither half — it read `saved.value` and
8
+ * stopped — so a definition-v1 prop carrying `$T` or a drawn curve rendered as
9
+ * a constant offline while the same document animated in Studio.
10
+ *
11
+ * It is generic over the node rather than importing the runtime's own, so this
12
+ * file has no dependency on `runtime.ts` and can be exercised on its own.
13
+ *
14
+ * Resolution is LAZY and cycle-guarded, which is not a detail: `ch('../a/x')`
15
+ * may name a parameter that is itself an expression, and resolving in node
16
+ * order would hand out last frame's value for half the graph depending on
17
+ * where the reference pointed. Pulling on demand gives the same answer
18
+ * whatever order the nodes are in, which is the whole promise of the word
19
+ * deterministic.
20
+ */
21
+ import { ExpressionEvaluator } from "../expressions/evaluator.js";
22
+ import { isBoundProp, resolvePropBinding } from "./resolve.js";
23
+ export class PropAnimator {
24
+ topology;
25
+ evaluator;
26
+ resolving = new Set();
27
+ constructor(topology) {
28
+ this.topology = topology;
29
+ this.evaluator = new ExpressionEvaluator(this.scope());
30
+ }
31
+ /** True when any parameter in the graph is bound at all — so a host can skip
32
+ * the whole pass, and a graph with no animation costs nothing. */
33
+ get animated() {
34
+ return this.topology.nodes.some((node) => Object.values(node.bindings).some((binding) => isBoundProp(binding)));
35
+ }
36
+ setFrame(frame) {
37
+ this.evaluator.setFrame(frame);
38
+ }
39
+ setFps(fps) {
40
+ this.evaluator.setFps(fps);
41
+ }
42
+ /** Write every bound parameter's value at the current frame into `props`. */
43
+ resolveAll() {
44
+ for (const node of this.topology.nodes)
45
+ for (const name of Object.keys(node.bindings))
46
+ this.resolve(node, name);
47
+ }
48
+ /**
49
+ * One parameter, at the current frame.
50
+ *
51
+ * Re-entry returns the stored value rather than throwing: a parameter whose
52
+ * expression refers back to itself is an author's mistake, and the render
53
+ * that names it is more use than the one that dies.
54
+ */
55
+ resolve(node, name) {
56
+ const binding = node.bindings[name];
57
+ if (!binding)
58
+ return node.props[name];
59
+ const key = `${node.id}.${name}`;
60
+ if (this.resolving.has(key))
61
+ return binding.value;
62
+ this.resolving.add(key);
63
+ try {
64
+ const resolved = resolvePropBinding(binding, {
65
+ frame: this.evaluator.fframe,
66
+ evaluateExpression: (expression) => this.evaluator.evaluate(expression, node),
67
+ });
68
+ if (resolved.error)
69
+ this.topology.report?.(node.id, name, resolved.error);
70
+ node.props[name] = resolved.value;
71
+ return resolved.value;
72
+ }
73
+ finally {
74
+ this.resolving.delete(key);
75
+ }
76
+ }
77
+ /**
78
+ * The host binding the expression engine reads through.
79
+ *
80
+ * A path names a parameter — `./radius`, `../blur1/amount`,
81
+ * `/root/blur1/amount` — and the last segment is always the parameter, the
82
+ * one before it the node. Anything a graph does not have resolves to null,
83
+ * which the engine turns into 0 / '' / [] per accessor rather than an
84
+ * exception: one broken reference must not take a render down.
85
+ */
86
+ scope() {
87
+ return {
88
+ resolveParam: (from, path) => {
89
+ const segments = split(path);
90
+ if (!segments.length)
91
+ return null;
92
+ const name = segments[segments.length - 1];
93
+ const owner = this.owner(from, segments.slice(0, -1));
94
+ if (!owner)
95
+ return null;
96
+ // Through `resolve`, so a reference to a keyed or expressed parameter
97
+ // reads what it is worth at this frame rather than what it was saved
98
+ // as. Reading the stored value here is the bug that makes `ch()` look
99
+ // like it works until somebody keys the parameter it points at.
100
+ if (owner.bindings[name])
101
+ return { value: this.resolve(owner, name) };
102
+ if (name in owner.props)
103
+ return { value: owner.props[name] };
104
+ if (name in owner.inputs)
105
+ return { value: owner.inputs[name] };
106
+ return null;
107
+ },
108
+ nodeExists: (from, path) => {
109
+ const segments = split(path);
110
+ if (!segments.length)
111
+ return false;
112
+ const last = segments[segments.length - 1];
113
+ if (last === "..")
114
+ return true;
115
+ return last === from.id || !!this.topology.node(last);
116
+ },
117
+ inputNode: (from, index) => this.topology.inputNode(from, index),
118
+ };
119
+ }
120
+ /** The node a path's leading segments name, or `from` when they name none. */
121
+ owner(from, segments) {
122
+ for (let i = segments.length - 1; i >= 0; i--) {
123
+ if (segments[i] === "..")
124
+ continue;
125
+ return this.topology.node(segments[i]);
126
+ }
127
+ return from;
128
+ }
129
+ }
130
+ /** Path segments, with the no-ops (`.`, empty, a trailing root slash) dropped
131
+ * but `..` kept, because it is the one that means something. */
132
+ function split(path) {
133
+ return String(path ?? "")
134
+ .split("/")
135
+ .filter((segment) => segment.length > 0 && segment !== ".");
136
+ }
137
+ //# sourceMappingURL=animator.js.map
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Parameter resolution: the order in which a parameter becomes a value.
3
+ *
4
+ * One module, one order, both hosts. See ./resolve.ts for why evaluation
5
+ * itself is a callback rather than a dependency.
6
+ */
7
+ export { PropAnimator } from "./animator.js";
8
+ export type { AnimatableNode, PropAnimatorTopology } from "./animator.js";
9
+ export { isBoundProp, resolvePropBinding, } from "./resolve.js";
10
+ export type { PropBinding, PropBindingSource, PropResolutionContext, ResolvedProp, } from "./resolve.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Parameter resolution: the order in which a parameter becomes a value.
3
+ *
4
+ * One module, one order, both hosts. See ./resolve.ts for why evaluation
5
+ * itself is a callback rather than a dependency.
6
+ */
7
+ export { PropAnimator } from "./animator.js";
8
+ export { isBoundProp, resolvePropBinding, } from "./resolve.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The one place a parameter becomes a value.
3
+ *
4
+ * Three bindings, in one order: a keyframe channel, an expression, the stored
5
+ * value. That order was implemented once, in Studio's `Node.evalParm`, which
6
+ * meant the deterministic runtime had no answer at all — a definition-v1 prop
7
+ * carrying `$T` or a drawn curve rendered as a static number offline. Copying
8
+ * `evalParm` into the runtime would have produced a second answer to the same
9
+ * question, which is exactly the two-parameter-stores mistake this codebase
10
+ * has already paid for once. So the order moved here, into the neutral
11
+ * runtime, and both hosts call it.
12
+ *
13
+ * What it deliberately does NOT own: the expression *engine*. Evaluation is
14
+ * passed in as a callback, because each host has its own scope (Studio's
15
+ * `ExpressionEngine` reaches into the compatibility graph; the runtime binds
16
+ * its own node map) and its own error bookkeeping (Studio stores the message
17
+ * on the prop for the Inspector to show). Keeping evaluation out is also what
18
+ * keeps this module dependent on nothing but the channel sampler — no cycle
19
+ * between `animation` and `expressions`, and no clock anywhere: the frame is a
20
+ * value the caller hands in, which is what keeps a cook deterministic.
21
+ */
22
+ import type { Channel } from "../animation/channel.js";
23
+ /**
24
+ * A parameter and everything bound to it. Structurally what Studio's `Prop`
25
+ * already is, and what `CascadeSerializedProp` deserialises to, so neither
26
+ * host has to build an adapter object to ask this question.
27
+ */
28
+ export interface PropBinding {
29
+ /** The stored value — what the author typed, and what the file records. */
30
+ readonly value: unknown;
31
+ readonly expression?: string | undefined;
32
+ readonly channel?: Channel | null | undefined;
33
+ }
34
+ /** Which of the three bindings actually produced the value. */
35
+ export type PropBindingSource = "channel" | "expression" | "value";
36
+ export interface ResolvedProp {
37
+ readonly value: unknown;
38
+ readonly source: PropBindingSource;
39
+ /** An expression that failed. The value has fallen back to the stored one. */
40
+ readonly error?: string;
41
+ }
42
+ export interface PropResolutionContext {
43
+ /** Fractional frame — a channel is sampled at it, and it is what the host's
44
+ * expression engine should already be set to. */
45
+ readonly frame: number;
46
+ /**
47
+ * Evaluate one expression. Omitted by a caller with no engine (a static
48
+ * inspection), in which case an expression is skipped rather than guessed
49
+ * at and the stored value stands.
50
+ */
51
+ readonly evaluateExpression?: (expression: string) => {
52
+ value: unknown;
53
+ error?: string;
54
+ } | undefined;
55
+ }
56
+ /** True when a parameter carries something beyond its stored value, and so has
57
+ * to be resolved per frame rather than read once. */
58
+ export declare function isBoundProp(binding: PropBinding | null | undefined): boolean;
59
+ /**
60
+ * Resolve a parameter at a frame.
61
+ *
62
+ * A parameter may carry both a channel and an expression, and the channel
63
+ * wins. The expression is kept, inert, and comes back when the channel is
64
+ * deleted: silently discarding what an author wrote is the worse of the two
65
+ * failures. `contracts/animation.ts` states the same rule for the document, so
66
+ * the file format and the resolver agree by construction.
67
+ *
68
+ * An empty channel resolves to nothing rather than to zero, so a parameter
69
+ * whose last key was deleted falls through to its expression or its value
70
+ * instead of snapping to the origin.
71
+ */
72
+ export declare function resolvePropBinding(binding: PropBinding, context: PropResolutionContext): ResolvedProp;
73
+ //# sourceMappingURL=resolve.d.ts.map