@field/cascade 0.8.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/AGENTS.md +36 -3
  2. package/CHANGELOG.md +401 -0
  3. package/DESIGN.md +14 -0
  4. package/README.md +41 -26
  5. package/dist/assets/CodeEditor-4O7FQ__R.js +623 -0
  6. package/dist/assets/CodePanel-BU-autmq.js +2 -0
  7. package/dist/assets/{CustomNodeDialog-BQbEhIwL.js → CustomNodeDialog-BRpDfOV5.js} +1 -1
  8. package/dist/assets/OpenGraphDialog-BwbRlC0X.js +1 -0
  9. package/dist/assets/ProjectSettings-DLOXLxcA.js +2 -0
  10. package/dist/assets/SettingsDialog-Bz8JxISH.js +1 -0
  11. package/dist/assets/{VersionHistoryDialog-D6VBzSQ0.js → VersionHistoryDialog-D5Z0nJ5-.js} +2 -2
  12. package/dist/assets/{core-BInLG4gw.js → core-Yqi0Ni61.js} +8 -8
  13. package/dist/assets/{cssMode-DfR2qmYG.js → cssMode-CxUzc4vp.js} +1 -1
  14. package/dist/assets/definitions-GreEMXLV.js +1 -0
  15. package/dist/assets/{editor.api-Ck8_vgQV.js → editor.api-BNFb25CX.js} +1 -1
  16. package/dist/assets/{freemarker2-fVn10Wn3.js → freemarker2-0ZDnWxeM.js} +1 -1
  17. package/dist/assets/geo-u0n_jOsD.js +1 -0
  18. package/dist/assets/{handlebars-BCSchLT2.js → handlebars-Cy18MoaD.js} +1 -1
  19. package/dist/assets/{html-CVpSv_k9.js → html-B-2Dx7nq.js} +1 -1
  20. package/dist/assets/{htmlMode-Da4ESTv0.js → htmlMode-DUAc9B_d.js} +1 -1
  21. package/dist/assets/{image-BLn--oSF.js → image-cAa-Nu9O.js} +3 -3
  22. package/dist/assets/index-5ZtDAuy2.js +220 -0
  23. package/dist/assets/index-7vae6YFC.css +1 -0
  24. package/dist/assets/index-client-DqYmKstU.js +1 -0
  25. package/dist/assets/{javascript-BpX0Gvcf.js → javascript-DeQsDqP5.js} +1 -1
  26. package/dist/assets/{jsonMode-CsLqaou7.js → jsonMode-BiFwJVSk.js} +1 -1
  27. package/dist/assets/legacy-CTEeXeqF.js +2 -0
  28. package/dist/assets/{liquid-efUz51Wf.js → liquid-BLoHH68a.js} +1 -1
  29. package/dist/assets/{lspLanguageFeatures-DCi5-K0n.js → lspLanguageFeatures-CNFgm1Kv.js} +1 -1
  30. package/dist/assets/math-DioIQOPJ.js +1 -0
  31. package/dist/assets/{mdx-DP_sjGVy.js → mdx-CFXQVNfs.js} +1 -1
  32. package/dist/assets/pop-DC_TQG9t.js +1 -0
  33. package/dist/assets/{python-aAuP3LUi.js → python-Cf7S7p2E.js} +1 -1
  34. package/dist/assets/{razor-7M6sMmC5.js → razor-CMHBcG-g.js} +1 -1
  35. package/dist/assets/studioScenes-8sq9FfFg.js +1 -0
  36. package/dist/assets/{tsMode-Bf4CnoYK.js → tsMode-BEih0GDj.js} +1 -1
  37. package/dist/assets/{typescript-ddhYBGyT.js → typescript-COEKYD_a.js} +1 -1
  38. package/dist/assets/{workers-X5la8DDT.js → workers-BNhAhMNn.js} +1 -1
  39. package/dist/assets/{xml-BzbXa3Ps.js → xml-CPiMpnPl.js} +1 -1
  40. package/dist/assets/{yaml-CXs-VzxL.js → yaml-Bgp1ITLR.js} +1 -1
  41. package/dist/cli/Graph-ZJJTRQTM.js +2975 -0
  42. package/dist/cli/chunk-2KXB47FP.js +115 -0
  43. package/dist/cli/chunk-35D343DC.js +1447 -0
  44. package/dist/cli/chunk-36P5K3SD.js +631 -0
  45. package/dist/cli/chunk-3JTJHF7V.js +267 -0
  46. package/dist/cli/chunk-4UDFYF5G.js +132 -0
  47. package/dist/cli/chunk-53NMBJAO.js +35 -0
  48. package/dist/cli/chunk-A4OAMOZH.js +310 -0
  49. package/dist/cli/chunk-BIJO5OFS.js +71 -0
  50. package/dist/cli/chunk-BP7UKF36.js +376 -0
  51. package/dist/cli/chunk-CKS2FQWR.js +89 -0
  52. package/dist/cli/chunk-CO22XHNG.js +593 -0
  53. package/dist/cli/chunk-D6BUOHUG.js +634 -0
  54. package/dist/cli/chunk-DEQSEFFH.js +153 -0
  55. package/dist/cli/chunk-DUIUFVU2.js +241 -0
  56. package/dist/cli/chunk-EUIAC3U3.js +3006 -0
  57. package/dist/cli/chunk-FCDXJ6EK.js +2208 -0
  58. package/dist/cli/chunk-GFS6W3C5.js +308 -0
  59. package/dist/cli/chunk-HFHWYL55.js +139 -0
  60. package/dist/cli/chunk-HSNOBIVT.js +263 -0
  61. package/dist/cli/chunk-ICH7N5PN.js +26 -0
  62. package/dist/cli/chunk-J7VK7MNF.js +418 -0
  63. package/dist/cli/chunk-KH4MUBKF.js +1551 -0
  64. package/dist/cli/chunk-MIIEFUGG.js +623 -0
  65. package/dist/cli/chunk-OWWBYLHC.js +136 -0
  66. package/dist/cli/chunk-POMMNKZZ.js +672 -0
  67. package/dist/cli/chunk-QBBKRNGY.js +90 -0
  68. package/dist/cli/chunk-QQPA2ZHH.js +273 -0
  69. package/dist/cli/chunk-RLCAJBVR.js +224 -0
  70. package/dist/cli/chunk-RQZ4SX5R.js +1125 -0
  71. package/dist/cli/chunk-RUU3YWXU.js +99 -0
  72. package/dist/cli/chunk-RXDIMXN5.js +98 -0
  73. package/dist/cli/chunk-SGJAET2L.js +197 -0
  74. package/dist/cli/chunk-VVGSLVSF.js +151 -0
  75. package/dist/cli/chunk-WD62JIP6.js +118 -0
  76. package/dist/cli/chunk-WIH4TEIS.js +104 -0
  77. package/dist/cli/chunk-WKURDVYP.js +1119 -0
  78. package/dist/cli/chunk-WPUDGEFY.js +20 -0
  79. package/dist/cli/chunk-XWDSKG7N.js +1301 -0
  80. package/dist/cli/chunk-YAT5Y25U.js +121 -0
  81. package/dist/cli/chunk-ZL4JTG5Y.js +37 -0
  82. package/dist/cli/cliCommands-UQSFGEHF.js +3464 -0
  83. package/dist/cli/commands-Q23M26QM.js +1292 -0
  84. package/dist/cli/definitions-HF2FTHYU.js +32 -0
  85. package/dist/cli/doctorCommand-B7BPQMN6.js +76 -0
  86. package/dist/cli/edit-JHCPTQFT.js +28 -0
  87. package/dist/cli/editorCommands-UIF5YLUZ.js +33 -0
  88. package/dist/cli/encode-HAUZTYPT.js +17 -0
  89. package/dist/cli/frames-LSAUBZ7P.js +16 -0
  90. package/dist/cli/geo-4RNK3QVN.js +43 -0
  91. package/dist/cli/graphCommands-6BMAKGQE.js +184 -0
  92. package/dist/cli/graphFile-VPSQ6OF7.js +286 -0
  93. package/dist/cli/headlessCanvas-5XLDZT7M.js +15 -0
  94. package/dist/cli/helpCommand-3VRE3KVO.js +155 -0
  95. package/dist/cli/index.js +106 -14207
  96. package/dist/cli/math-ZRMLKU2D.js +16 -0
  97. package/dist/cli/mcp-ZLEEMYGF.js +164 -0
  98. package/dist/cli/parity-KINARCP2.js +328 -0
  99. package/dist/cli/pop-USDFXB7H.js +39 -0
  100. package/dist/cli/project-K3QIPGBB.js +12 -0
  101. package/dist/cli/projectDefinition-DMTZN2NE.js +27 -0
  102. package/dist/cli/projectRuntime-Z5PJOH47.js +48 -0
  103. package/dist/cli/registerStandardNodes-S2VLMF6J.js +3081 -0
  104. package/dist/cli/render-YURAPPUI.js +436 -0
  105. package/dist/cli/runCommand-PI764LEG.js +198 -0
  106. package/dist/cli/runner-RA6UKEPD.js +37 -0
  107. package/dist/cli/runtime/image.ts +277 -0
  108. package/dist/cli/runtime/stage.ts +74 -11
  109. package/dist/cli/stageRunner-KUNIMQKJ.js +10 -0
  110. package/dist/cli/verbs-RCE3K4VQ.js +19 -0
  111. package/dist/cli/webBuild-GT4PZT5D.js +37 -0
  112. package/dist/contracts/bundle.d.ts +75 -0
  113. package/dist/contracts/bundle.js +112 -0
  114. package/dist/contracts/definition.d.ts +67 -4
  115. package/dist/contracts/document.d.ts +29 -0
  116. package/dist/contracts/documentFormat.d.ts +18 -0
  117. package/dist/contracts/documentFormat.js +195 -0
  118. package/dist/contracts/editor.d.ts +189 -0
  119. package/dist/contracts/editor.js +2 -0
  120. package/dist/contracts/index.d.ts +3 -0
  121. package/dist/contracts/index.js +3 -0
  122. package/dist/contracts/validate.js +59 -3
  123. package/dist/contracts/values.d.ts +3 -3
  124. package/dist/contracts/values.js +1 -0
  125. package/dist/image.d.ts +141 -0
  126. package/dist/image.js +127 -0
  127. package/dist/index.html +4 -4
  128. package/dist/player/app.js +17 -5
  129. package/dist/player/editor.js +1919 -0
  130. package/dist/player/editorEngine.js +477 -0
  131. package/dist/runtime/builtins/core/bundle.d.ts +70 -0
  132. package/dist/runtime/builtins/core/bundle.js +81 -0
  133. package/dist/runtime/builtins/core/index.d.ts +3 -2
  134. package/dist/runtime/builtins/core/index.js +6 -2
  135. package/dist/runtime/builtins/core/routing.d.ts +27 -0
  136. package/dist/runtime/builtins/core/routing.js +28 -0
  137. package/dist/runtime/builtins/headless.d.ts +19 -0
  138. package/dist/runtime/builtins/headless.js +25 -0
  139. package/dist/runtime/builtins/image/contactSheet.d.ts +226 -0
  140. package/dist/runtime/builtins/image/contactSheet.js +199 -0
  141. package/dist/runtime/builtins/image/index.d.ts +9 -0
  142. package/dist/runtime/builtins/image/index.js +9 -0
  143. package/dist/runtime/edit/document.d.ts +94 -0
  144. package/dist/runtime/edit/document.js +194 -0
  145. package/dist/runtime/edit/graphEditor.d.ts +223 -0
  146. package/dist/runtime/edit/graphEditor.js +893 -0
  147. package/dist/runtime/edit/index.d.ts +12 -0
  148. package/dist/runtime/edit/index.js +6 -0
  149. package/dist/runtime/edit/layout.d.ts +50 -0
  150. package/dist/runtime/edit/layout.js +126 -0
  151. package/dist/runtime/edit/operations.d.ts +65 -0
  152. package/dist/runtime/edit/operations.js +57 -0
  153. package/dist/runtime/edit/parameters.d.ts +75 -0
  154. package/dist/runtime/edit/parameters.js +284 -0
  155. package/dist/runtime/edit/read.d.ts +35 -0
  156. package/dist/runtime/edit/read.js +65 -0
  157. package/dist/runtime/editor/index.d.ts +104 -0
  158. package/dist/runtime/editor/index.js +439 -0
  159. package/dist/runtime/expressions/bundlePaths.d.ts +37 -0
  160. package/dist/runtime/expressions/bundlePaths.js +55 -0
  161. package/dist/runtime/expressions/index.d.ts +2 -0
  162. package/dist/runtime/expressions/index.js +1 -0
  163. package/dist/runtime/index.d.ts +2 -0
  164. package/dist/runtime/index.js +2 -0
  165. package/dist/runtime/params/animator.d.ts +5 -0
  166. package/dist/runtime/params/animator.js +16 -0
  167. package/dist/runtime/runtime.js +238 -12
  168. package/dist/runtime/seed.d.ts +29 -0
  169. package/dist/runtime/seed.js +52 -0
  170. package/dist/runtime/types.d.ts +12 -0
  171. package/dist/stage.js +43 -9
  172. package/dist/studio/panel.d.ts +2 -0
  173. package/doc/CLI_REFERENCE.md +171 -0
  174. package/doc/EDITOR_EXPORT.md +235 -0
  175. package/doc/HEADLESS_GPU.md +27 -0
  176. package/doc/MCP.md +69 -0
  177. package/doc/NODE_REFERENCE.md +208 -0
  178. package/doc/PROJECT_AUTHORING.md +258 -1
  179. package/package.json +29 -8
  180. package/dist/assets/CodeEditor-BEk-kIBd.js +0 -623
  181. package/dist/assets/CodePanel-CeSdjXkg.js +0 -2
  182. package/dist/assets/ExportDialog-CjTbbTZJ.js +0 -358
  183. package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
  184. package/dist/assets/OpenGraphDialog-DCwH9itM.js +0 -1
  185. package/dist/assets/ProjectSettings-Dg7M5xt9.js +0 -2
  186. package/dist/assets/SettingsDialog-e7GgsjB9.js +0 -1
  187. package/dist/assets/definitions-DZpOjWKR.js +0 -1
  188. package/dist/assets/geo-D2VPjAdt.js +0 -1
  189. package/dist/assets/index-CMiBdEuT.js +0 -212
  190. package/dist/assets/index-DMIuml9a.css +0 -1
  191. package/dist/assets/index-client-BI2H82FJ.js +0 -1
  192. package/dist/assets/legacy-ZA7textG.js +0 -2
  193. package/dist/assets/math-B6hpxPJO.js +0 -1
  194. package/dist/assets/pop-CYUIjT6x.js +0 -1
  195. package/dist/assets/projectSettingsApi-C1WvIwfP.js +0 -1
@@ -0,0 +1,1447 @@
1
+ import {
2
+ projectionMatrix,
3
+ viewMatrix
4
+ } from "./chunk-DEQSEFFH.js";
5
+ import {
6
+ GeometryBuilder,
7
+ attributeSet,
8
+ copyAttributeArray,
9
+ createAttribute,
10
+ createStringAttribute,
11
+ geometryError,
12
+ getPosition,
13
+ numericAttribute,
14
+ positionAttribute,
15
+ positionSize,
16
+ readComponent,
17
+ readElement,
18
+ readString,
19
+ setAttribute,
20
+ withAttributeSet
21
+ } from "./chunk-HSNOBIVT.js";
22
+ import {
23
+ BEZIER_STRIDE,
24
+ POSITION_ATTRIBUTE,
25
+ PRIMITIVE_KINDS,
26
+ attributeArray,
27
+ createGeometry,
28
+ emptyGeometry,
29
+ isStringAttribute,
30
+ levelCount
31
+ } from "./chunk-J7VK7MNF.js";
32
+
33
+ // packages/contracts/src/color.ts
34
+ var HEX = /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
35
+ function clamp(value) {
36
+ return !Number.isFinite(value) ? 0 : value < 0 ? 0 : value > 1 ? 1 : value;
37
+ }
38
+ function byte(value) {
39
+ return Math.round(clamp(value) * 255);
40
+ }
41
+ function pair(value) {
42
+ return byte(value).toString(16).padStart(2, "0");
43
+ }
44
+ function unit(value) {
45
+ return String(Number(clamp(value).toFixed(4)));
46
+ }
47
+ function expand(digits) {
48
+ const wide = digits.length <= 4 ? [...digits].map((digit) => `${digit}${digit}`) : digits.match(/../g) ?? [];
49
+ return wide.map((component) => Number.parseInt(component, 16) / 255);
50
+ }
51
+ var Color = Object.freeze({
52
+ /**
53
+ * `#rgb`, `#rgba`, `#rrggbb` or `#rrggbbaa` to a colour. Alpha defaults to 1
54
+ * when the string carries none. The three- and four-digit shorthands are
55
+ * accepted because CSS accepts them and hand-written palettes use them;
56
+ * anything else throws, since a colour that silently parses to black is the
57
+ * failure this whole ruling exists to remove.
58
+ */
59
+ fromHex(value) {
60
+ if (!HEX.test(value))
61
+ throw new TypeError(
62
+ `color/invalid-hex: ${value} is not #rgb, #rgba, #rrggbb or #rrggbbaa`
63
+ );
64
+ const components = expand(value.slice(1));
65
+ return [
66
+ components[0],
67
+ components[1],
68
+ components[2],
69
+ components[3] ?? 1
70
+ ];
71
+ },
72
+ /** Whether `fromHex` would accept this string. */
73
+ isHex(value) {
74
+ return typeof value === "string" && HEX.test(value);
75
+ },
76
+ /**
77
+ * `#rrggbb`, or `#rrggbbaa` with `includeAlpha`. The default drops alpha
78
+ * because eight-digit hex is CSS Color 4 and plotter and vector-editor
79
+ * software of the kind this library exists to feed does not read it. Callers
80
+ * that need the alpha to survive emit it as a separate opacity, which is what
81
+ * the SVG writer does.
82
+ */
83
+ toHex(color, includeAlpha = false) {
84
+ return `#${pair(color[0])}${pair(color[1])}${pair(color[2])}${includeAlpha ? pair(color[3]) : ""}`;
85
+ },
86
+ /**
87
+ * A CSS colour: `rgb(255, 128, 0)`, or `rgba(255, 128, 0, 0.5)` when alpha is
88
+ * below 1. The legacy comma forms rather than the modern space-separated
89
+ * ones, for the same compatibility reason `toHex` drops alpha by default.
90
+ */
91
+ toString(color) {
92
+ const channels = `${byte(color[0])}, ${byte(color[1])}, ${byte(color[2])}`;
93
+ return clamp(color[3]) >= 1 ? `rgb(${channels})` : `rgba(${channels}, ${unit(color[3])})`;
94
+ },
95
+ /** Alpha on its own, for a renderer that carries opacity separately. */
96
+ alpha(color) {
97
+ return clamp(color[3]);
98
+ },
99
+ /** The same colour at full alpha. */
100
+ opaque(color) {
101
+ return [clamp(color[0]), clamp(color[1]), clamp(color[2]), 1];
102
+ },
103
+ /**
104
+ * Four unit components from anything indexable, clamping and defaulting a
105
+ * missing alpha to 1. The reader for a `Cd` attribute's flat array, and the
106
+ * one place that decides what an under-sized colour means.
107
+ */
108
+ fromComponents(components, offset = 0, size = 4) {
109
+ const at = (component) => component < size ? clamp(components[offset + component] ?? 0) : 0;
110
+ return [at(0), at(1), at(2), size < 4 ? 1 : at(3)];
111
+ }
112
+ });
113
+
114
+ // packages/contracts/src/scene.ts
115
+ var LIGHT_DEFAULTS = {
116
+ type: "point",
117
+ translate: [0, 0, 0],
118
+ direction: [0, 0, -1],
119
+ color: [1, 1, 1, 1],
120
+ intensity: 1,
121
+ coneangle: 45
122
+ };
123
+ var EMPTY_SCENE = { geometry: [], camera: null, lights: [] };
124
+ function isLight(value) {
125
+ if (typeof value !== "object" || value === null) return false;
126
+ const light = value;
127
+ return (light.type === "point" || light.type === "distant" || light.type === "ambient" || light.type === "spot") && Array.isArray(light.translate) && Array.isArray(light.direction) && Array.isArray(light.color) && typeof light.intensity === "number";
128
+ }
129
+ function isScene(value) {
130
+ if (typeof value !== "object" || value === null) return false;
131
+ const scene = value;
132
+ return Array.isArray(scene.geometry) && Array.isArray(scene.lights) && "camera" in scene && (scene.camera === null || typeof scene.camera === "object");
133
+ }
134
+ function sceneFromGeometry(geometry) {
135
+ return { geometry: [geometry], camera: null, lights: [] };
136
+ }
137
+ function asScene(value) {
138
+ if (value === void 0 || value === null) return EMPTY_SCENE;
139
+ if (isScene(value)) return value;
140
+ if (typeof value === "object" && value.kind === "geometry")
141
+ return sceneFromGeometry(value);
142
+ return EMPTY_SCENE;
143
+ }
144
+
145
+ // packages/runtime/src/geometry/groups.ts
146
+ var KEYS = {
147
+ point: "pointGroups",
148
+ primitive: "primitiveGroups"
149
+ };
150
+ function groupMask(geometry, level, name) {
151
+ return geometry[KEYS[level]][name];
152
+ }
153
+ function groupIndices(mask) {
154
+ const indices = [];
155
+ for (let index = 0; index < mask.length; index += 1)
156
+ if (mask[index]) indices.push(index);
157
+ return indices;
158
+ }
159
+ function groupNames(geometry, level) {
160
+ return Object.keys(geometry[KEYS[level]]);
161
+ }
162
+
163
+ // packages/runtime/src/geometry/matrix.ts
164
+ function transformMatrix(parameters) {
165
+ const [tx, ty] = parameters.translate ?? [0, 0];
166
+ const [sx, sy] = parameters.scale ?? [1, 1];
167
+ const [px, py] = parameters.pivot ?? [0, 0];
168
+ const rotate = parameters.rotate ?? 0;
169
+ if (![tx, ty, sx, sy, px, py, rotate].every(Number.isFinite))
170
+ throw new TypeError("geometry/transform: transform parameters must be finite");
171
+ const radians = rotate * Math.PI / 180;
172
+ const cos = Math.cos(radians);
173
+ const sin = Math.sin(radians);
174
+ const a00 = cos * sx;
175
+ const a10 = sin * sx;
176
+ const a01 = -sin * sy;
177
+ const a11 = cos * sy;
178
+ return [
179
+ a00,
180
+ a10,
181
+ 0,
182
+ a01,
183
+ a11,
184
+ 0,
185
+ tx + px - (a00 * px + a01 * py),
186
+ ty + py - (a10 * px + a11 * py),
187
+ 1
188
+ ];
189
+ }
190
+
191
+ // packages/runtime/src/geometry/merge.ts
192
+ function mergeGeometries(inputs) {
193
+ if (inputs.length === 0) return emptyGeometry(2);
194
+ if (inputs.length === 1) return inputs[0];
195
+ const pointCount = sum(inputs, (input) => input.pointCount);
196
+ const vertexCount = sum(inputs, (input) => input.vertexCount);
197
+ const primitiveCount = sum(inputs, (input) => input.primitiveCount);
198
+ const vertexPoints = new Int32Array(vertexCount);
199
+ const offsets = new Int32Array(primitiveCount + 1);
200
+ const kinds = new Uint8Array(primitiveCount);
201
+ const closed = new Uint8Array(primitiveCount);
202
+ let pointBase = 0;
203
+ let vertexBase = 0;
204
+ let primitiveBase = 0;
205
+ for (const input of inputs) {
206
+ for (let vertex = 0; vertex < input.vertexCount; vertex += 1)
207
+ vertexPoints[vertexBase + vertex] = input.topology.vertexPoints[vertex] + pointBase;
208
+ for (let primitive = 0; primitive < input.primitiveCount; primitive += 1) {
209
+ offsets[primitiveBase + primitive] = input.topology.offsets[primitive] + vertexBase;
210
+ kinds[primitiveBase + primitive] = input.topology.kinds[primitive];
211
+ closed[primitiveBase + primitive] = input.topology.closed[primitive];
212
+ }
213
+ pointBase += input.pointCount;
214
+ vertexBase += input.vertexCount;
215
+ primitiveBase += input.primitiveCount;
216
+ }
217
+ offsets[primitiveCount] = vertexCount;
218
+ const detail = {};
219
+ for (const input of inputs)
220
+ for (const [name, value] of Object.entries(input.detail))
221
+ if (!Object.prototype.hasOwnProperty.call(detail, name))
222
+ detail[name] = value;
223
+ return createGeometry({
224
+ pointCount,
225
+ point: mergeLevel(inputs, "point"),
226
+ vertex: mergeLevel(inputs, "vertex"),
227
+ primitive: mergeLevel(inputs, "primitive"),
228
+ detail,
229
+ topology: { vertexPoints, offsets, kinds, closed },
230
+ pointGroups: mergeGroups(inputs, "point"),
231
+ primitiveGroups: mergeGroups(inputs, "primitive")
232
+ });
233
+ }
234
+ function sum(inputs, read) {
235
+ let total = 0;
236
+ for (const input of inputs) total += read(input);
237
+ return total;
238
+ }
239
+ var STORAGE_RANK = {
240
+ u8: 0,
241
+ i32: 1,
242
+ f32: 2,
243
+ f64: 3
244
+ };
245
+ function widerStorage(a, b) {
246
+ if (a === b) return a;
247
+ if (a === "f64" || b === "f64") return "f64";
248
+ if (a === "i32" && b === "f32" || a === "f32" && b === "i32") return "f64";
249
+ return STORAGE_RANK[a] > STORAGE_RANK[b] ? a : b;
250
+ }
251
+ function mergeLevel(inputs, level) {
252
+ const names = [];
253
+ for (const input of inputs)
254
+ for (const name of Object.keys(input[level]))
255
+ if (!names.includes(name)) names.push(name);
256
+ const total = sum(inputs, (input) => levelCount(input, level));
257
+ const result = {};
258
+ for (const name of names) {
259
+ const present = inputs.map((input) => input[level][name]).filter((attribute) => attribute !== void 0);
260
+ const strings = present.filter(isStringAttribute).length;
261
+ if (strings !== 0 && strings !== present.length)
262
+ geometryError(
263
+ "attribute-conflict",
264
+ `${level}.${name} is a string attribute on one input and numeric on another`
265
+ );
266
+ result[name] = strings === present.length ? mergeStringAttribute(inputs, level, name, total) : mergeNumericAttribute(inputs, level, name, total);
267
+ }
268
+ return result;
269
+ }
270
+ function mergeNumericAttribute(inputs, level, name, total) {
271
+ let size = 1;
272
+ let storage;
273
+ for (const input of inputs) {
274
+ const attribute = input[level][name];
275
+ if (attribute === void 0 || isStringAttribute(attribute)) continue;
276
+ size = Math.max(size, attribute.size);
277
+ storage = storage === void 0 ? attribute.storage : widerStorage(storage, attribute.storage);
278
+ }
279
+ const data = attributeArray(storage ?? "f64", size * total);
280
+ let base = 0;
281
+ for (const input of inputs) {
282
+ const count = levelCount(input, level);
283
+ const attribute = input[level][name];
284
+ if (attribute !== void 0 && !isStringAttribute(attribute))
285
+ for (let index = 0; index < count; index += 1)
286
+ for (let component = 0; component < attribute.size; component += 1)
287
+ data[(base + index) * size + component] = attribute.data[index * attribute.size + component];
288
+ base += count;
289
+ }
290
+ return { storage: storage ?? "f64", size, data };
291
+ }
292
+ function mergeStringAttribute(inputs, level, name, total) {
293
+ const values = new Array(total).fill("");
294
+ let base = 0;
295
+ for (const input of inputs) {
296
+ const count = levelCount(input, level);
297
+ const attribute = input[level][name];
298
+ if (attribute !== void 0 && isStringAttribute(attribute))
299
+ for (let index = 0; index < count; index += 1)
300
+ values[base + index] = readString(attribute, index);
301
+ base += count;
302
+ }
303
+ return createStringAttribute(values);
304
+ }
305
+ function mergeGroups(inputs, level) {
306
+ const key = level === "point" ? "pointGroups" : "primitiveGroups";
307
+ const names = [];
308
+ for (const input of inputs)
309
+ for (const name of Object.keys(input[key]))
310
+ if (!names.includes(name)) names.push(name);
311
+ const total = sum(inputs, (input) => levelCount(input, level));
312
+ const result = {};
313
+ for (const name of names) {
314
+ const mask = new Uint8Array(total);
315
+ let base = 0;
316
+ for (const input of inputs) {
317
+ const count = levelCount(input, level);
318
+ const source = input[key][name];
319
+ if (source !== void 0)
320
+ for (let index = 0; index < count; index += 1)
321
+ mask[base + index] = source[index];
322
+ base += count;
323
+ }
324
+ result[name] = mask;
325
+ }
326
+ return result;
327
+ }
328
+
329
+ // packages/runtime/src/geometry/transform.ts
330
+ function transformGeometry(geometry, matrix) {
331
+ if (matrix.length !== 9 && matrix.length !== 16)
332
+ geometryError("matrix", "transform needs a mat3 or a mat4");
333
+ if (!matrix.every(Number.isFinite))
334
+ geometryError("matrix", "transform matrix values must be finite");
335
+ if (geometry.pointCount === 0) return geometry;
336
+ const position = positionAttribute(geometry);
337
+ const size = position.size;
338
+ const data = copyAttributeArray(position);
339
+ if (matrix.length === 9) {
340
+ const m = matrix;
341
+ for (let point = 0; point < geometry.pointCount; point += 1) {
342
+ const base = point * size;
343
+ const x = data[base];
344
+ const y = data[base + 1];
345
+ data[base] = m[0] * x + m[3] * y + m[6];
346
+ data[base + 1] = m[1] * x + m[4] * y + m[7];
347
+ }
348
+ } else {
349
+ const m = matrix;
350
+ for (let point = 0; point < geometry.pointCount; point += 1) {
351
+ const base = point * size;
352
+ const x = data[base];
353
+ const y = data[base + 1];
354
+ const z = size === 3 ? data[base + 2] : 0;
355
+ data[base] = m[0] * x + m[4] * y + m[8] * z + m[12];
356
+ data[base + 1] = m[1] * x + m[5] * y + m[9] * z + m[13];
357
+ if (size === 3)
358
+ data[base + 2] = m[2] * x + m[6] * y + m[10] * z + m[14];
359
+ }
360
+ }
361
+ return withAttributeSet(geometry, "point", {
362
+ ...attributeSet(geometry, "point"),
363
+ [POSITION_ATTRIBUTE]: { storage: position.storage, size, data }
364
+ });
365
+ }
366
+
367
+ // packages/runtime/src/geometry/copy.ts
368
+ function copyToPoints(source, target, options = {}) {
369
+ const width = positionSize(source) === 3 ? 3 : 2;
370
+ const selection = options.targetGroup === void 0 || options.targetGroup === "" ? void 0 : groupMask(target, "point", options.targetGroup) ?? geometryError(
371
+ "missing-group",
372
+ `point group ${options.targetGroup} is not present on the target`
373
+ );
374
+ const indices = selection === void 0 ? range(target.pointCount) : groupIndices(selection);
375
+ if (indices.length === 0 || source.pointCount === 0)
376
+ return emptyGeometry(width);
377
+ const orient = options.useTargetOrientations ?? true;
378
+ const pscale = orient ? numericAttribute(target.point.pscale, "point.pscale", [1]) : void 0;
379
+ const normal = orient ? numericAttribute(target.point.N, "point.N", [2, 3]) : void 0;
380
+ const copyNumber = options.copyNumberAttribute ?? "copynum";
381
+ const transfer = options.transferTargetAttributes ?? true ? Object.keys(target.point).filter(
382
+ (name) => name !== POSITION_ATTRIBUTE && !Object.prototype.hasOwnProperty.call(source.point, name)
383
+ ) : [];
384
+ const copies = [];
385
+ for (const index of indices) {
386
+ const position = getPosition(target, index);
387
+ const scale = pscale === void 0 ? 1 : readComponent(pscale, index);
388
+ const rotate = normal === void 0 ? 0 : Math.atan2(
389
+ readComponent(normal, index, 1),
390
+ readComponent(normal, index, 0)
391
+ ) * 180 / Math.PI;
392
+ let copy = transformGeometry(
393
+ source,
394
+ transformMatrix({
395
+ translate: [position[0], position[1]],
396
+ rotate,
397
+ scale: [scale, scale]
398
+ })
399
+ );
400
+ if (copyNumber !== "")
401
+ copy = setAttribute(copy, "primitive", copyNumber, {
402
+ storage: "i32",
403
+ size: 1,
404
+ data: new Int32Array(copy.primitiveCount).fill(index)
405
+ });
406
+ for (const name of transfer)
407
+ copy = setAttribute(
408
+ copy,
409
+ "point",
410
+ name,
411
+ spread(target.point[name], index, copy.pointCount)
412
+ );
413
+ copies.push(copy);
414
+ }
415
+ return mergeGeometries(copies);
416
+ }
417
+ function range(count) {
418
+ const indices = new Array(count);
419
+ for (let index = 0; index < count; index += 1) indices[index] = index;
420
+ return indices;
421
+ }
422
+ function spread(attribute, index, count) {
423
+ if (isStringAttribute(attribute))
424
+ return createStringAttribute(
425
+ new Array(count).fill(readString(attribute, index))
426
+ );
427
+ return createAttribute(
428
+ count,
429
+ attribute.size,
430
+ attribute.storage,
431
+ readElement(attribute, index)
432
+ );
433
+ }
434
+
435
+ // packages/runtime/src/geometry/rects.ts
436
+ function coordinates(rect) {
437
+ if (Array.isArray(rect.rect)) {
438
+ const box = rect.rect;
439
+ if (box.length < 4 || box.some((value) => typeof value !== "number")) return null;
440
+ return [box[0], box[1], box[2], box[3]];
441
+ }
442
+ const declared = rect;
443
+ if (typeof declared?.x !== "number" || typeof declared?.y !== "number" || typeof declared?.width !== "number" || typeof declared?.height !== "number")
444
+ return null;
445
+ return [declared.x, declared.y, declared.width, declared.height];
446
+ }
447
+ function rectsToGeometry(value, options = {}) {
448
+ const list = Array.isArray(value) ? value : value?.rects ?? [];
449
+ const flip = options.origin === "top-left" ? options.height ?? 0 : 0;
450
+ const builder = new GeometryBuilder();
451
+ const tags = [];
452
+ let skipped = 0;
453
+ let tagged = false;
454
+ for (const rect of list) {
455
+ const box = coordinates(rect);
456
+ if (!box || box.some((component) => !Number.isFinite(component))) {
457
+ skipped += 1;
458
+ continue;
459
+ }
460
+ const [x, y, width, height] = box;
461
+ const bottom = flip > 0 ? flip - (y + height) : y;
462
+ const top = bottom + height;
463
+ builder.addPolygon([x, bottom, x + width, bottom, x + width, top, x, top], {
464
+ closed: true
465
+ });
466
+ const tag = rect.tag;
467
+ tags.push(typeof tag === "string" ? tag : "");
468
+ if (typeof tag === "string" && tag.length > 0) tagged = true;
469
+ }
470
+ if (tagged) builder.setStringAttribute("primitive", "tag", tags);
471
+ if (skipped > 0) builder.setDetail("rects_skipped", skipped);
472
+ return builder.build();
473
+ }
474
+
475
+ // packages/runtime/src/builtins/geo/definitions.ts
476
+ var rectangleDefinition = {
477
+ apiVersion: 1,
478
+ label: "Rectangle",
479
+ description: "A closed four-point polygon, counter-clockwise from bottom-left.",
480
+ icon: "Square",
481
+ runsOn: "portable",
482
+ inputs: {
483
+ size: { kind: "data", type: "vec2", default: [1, 1] },
484
+ center: { kind: "data", type: "vec2", default: [0, 0] }
485
+ },
486
+ outputs: { geometry: { kind: "data", type: "geometry" } }
487
+ };
488
+ var circleDefinition = {
489
+ apiVersion: 1,
490
+ label: "Circle",
491
+ description: "A closed circle, as one cubic Bezier chain or as a polygon of divisions segments.",
492
+ icon: "Circle",
493
+ runsOn: "portable",
494
+ inputs: {
495
+ center: { kind: "data", type: "vec2", default: [0, 0] },
496
+ radius: { kind: "data", type: "vec2", default: [1, 1] },
497
+ divisions: {
498
+ kind: "data",
499
+ type: "int",
500
+ default: 32,
501
+ min: 3,
502
+ step: 1,
503
+ description: "Segments of the polygonal form. Ignored by the Bezier form."
504
+ }
505
+ },
506
+ outputs: { geometry: { kind: "data", type: "geometry" } },
507
+ props: {
508
+ type: {
509
+ type: "string",
510
+ default: "bezier",
511
+ label: "Primitive Type",
512
+ control: "select",
513
+ options: ["bezier", "poly"],
514
+ description: "A Bezier circle is a circle; a polygon of any division count is visibly a polygon in print."
515
+ }
516
+ }
517
+ };
518
+ var transformDefinition = {
519
+ apiVersion: 1,
520
+ label: "Transform",
521
+ description: "Translate, rotate and scale geometry about a pivot.",
522
+ icon: "Move",
523
+ runsOn: "portable",
524
+ inputs: {
525
+ geometry: { kind: "data", type: "geometry" },
526
+ translate: { kind: "data", type: "vec2", default: [0, 0] },
527
+ rotate: {
528
+ kind: "data",
529
+ type: "float",
530
+ default: 0,
531
+ description: "Degrees, counter-clockwise."
532
+ },
533
+ scale: { kind: "data", type: "vec2", default: [1, 1] },
534
+ pivot: { kind: "data", type: "vec2", default: [0, 0] }
535
+ },
536
+ outputs: { geometry: { kind: "data", type: "geometry" } }
537
+ };
538
+ var mergeDefinition = {
539
+ apiVersion: 1,
540
+ label: "Merge",
541
+ description: "Concatenate geometries, taking the union of their attributes.",
542
+ icon: "GitMerge",
543
+ runsOn: "portable",
544
+ inputs: { inputs: { kind: "data", type: "geometry", variadic: true } },
545
+ outputs: { geometry: { kind: "data", type: "geometry" } }
546
+ };
547
+ var copyToPointsDefinition = {
548
+ apiVersion: 1,
549
+ label: "Copy to Points",
550
+ description: "Instance one geometry onto every point of another.",
551
+ icon: "Copy",
552
+ runsOn: "portable",
553
+ inputs: {
554
+ source: { kind: "data", type: "geometry" },
555
+ target: { kind: "data", type: "geometry" }
556
+ },
557
+ outputs: { geometry: { kind: "data", type: "geometry" } },
558
+ props: {
559
+ targetGroup: {
560
+ type: "string",
561
+ default: "",
562
+ label: "Target Group",
563
+ description: "Copy onto this point group only; empty means every point."
564
+ },
565
+ useTargetOrientations: {
566
+ type: "bool",
567
+ default: true,
568
+ label: "Transform Using Target Point Orientations",
569
+ description: "Read pscale and N from the target points."
570
+ }
571
+ }
572
+ };
573
+ var svgExportDefinition = {
574
+ apiVersion: 1,
575
+ label: "SVG Export",
576
+ description: "Write geometry as an SVG document, one group element per group.",
577
+ icon: "FileDown",
578
+ runsOn: "portable",
579
+ capabilities: ["assets"],
580
+ inputs: { geometry: { kind: "data", type: "geometry" } },
581
+ outputs: {
582
+ svg: { kind: "data", type: "string" },
583
+ asset: { kind: "data", type: "asset" }
584
+ },
585
+ props: {
586
+ filename: { type: "string", default: "geometry.svg", label: "Filename" },
587
+ width: {
588
+ type: "float",
589
+ default: 0,
590
+ min: 0,
591
+ label: "Width",
592
+ description: "0 derives the width from the geometry bounds."
593
+ },
594
+ height: { type: "float", default: 0, min: 0, label: "Height" },
595
+ margin: { type: "float", default: 0, label: "Margin" },
596
+ /**
597
+ * An explicit world rectangle, `[minX, minY, maxX, maxY]`, instead of the
598
+ * geometry's own bounds. All zero derives them as before.
599
+ *
600
+ * A frame sequence needs it: derived bounds give every frame its own
601
+ * viewBox, so a growing drawing appears to zoom out over the animation.
602
+ */
603
+ bounds: { type: "vec4", default: [0, 0, 0, 0], label: "Bounds" },
604
+ /** A ground behind the drawing. Fully transparent leaves the document
605
+ * transparent, which is what plotter work wants. */
606
+ background: {
607
+ type: "color",
608
+ default: [0, 0, 0, 0],
609
+ label: "Background",
610
+ description: "A ground behind the drawing. Transparent by default, because a plotter wants paper."
611
+ },
612
+ stroke: {
613
+ type: "color",
614
+ default: [0, 0, 0, 1],
615
+ label: "Stroke",
616
+ description: "Fallback for a primitive with no Cd attribute."
617
+ },
618
+ strokeWidth: {
619
+ type: "float",
620
+ default: 1,
621
+ min: 0,
622
+ label: "Stroke Width",
623
+ description: "Fallback for a primitive with no width attribute."
624
+ },
625
+ opacity: {
626
+ type: "float",
627
+ default: 1,
628
+ min: 0,
629
+ max: 1,
630
+ label: "Opacity",
631
+ description: "Fallback for a primitive with no opacity attribute."
632
+ },
633
+ fillMode: {
634
+ type: "string",
635
+ default: "none",
636
+ label: "Fill Mode",
637
+ control: "select",
638
+ options: ["none", "solid"],
639
+ description: "none leaves primitives unfilled, which is what plotter work wants."
640
+ },
641
+ fill: {
642
+ type: "color",
643
+ default: [1, 1, 1, 1],
644
+ label: "Fill",
645
+ description: "Fill colour, used only when Fill Mode is solid."
646
+ },
647
+ pointRadius: {
648
+ type: "float",
649
+ default: 0.5,
650
+ min: 0,
651
+ label: "Point Radius",
652
+ description: "Fallback radius for a loose point with no pscale."
653
+ },
654
+ precision: {
655
+ type: "int",
656
+ default: 3,
657
+ min: 0,
658
+ max: 15,
659
+ step: 1,
660
+ label: "Precision",
661
+ description: "Decimal places on emitted coordinates."
662
+ }
663
+ }
664
+ };
665
+ var renderDefinition = {
666
+ apiVersion: 1,
667
+ label: "Render",
668
+ description: "Draw a scene through a camera, as a raster.",
669
+ icon: "Camera",
670
+ runsOn: "portable",
671
+ capabilities: ["assets"],
672
+ inputs: {
673
+ /**
674
+ * A scene, or a geometry — `geometry` widens to `scene`, so wiring a
675
+ * geometry straight in still works and arrives through `asScene`.
676
+ *
677
+ * Taking a scene rather than a geometry is what makes the viewport and this
678
+ * node incapable of disagreeing about framing: both read the same object,
679
+ * so the picture on screen and the picture in the PNG come from one camera
680
+ * and one list of geometry rather than from two paths that have to be kept
681
+ * in step by hand.
682
+ */
683
+ scene: { kind: "data", type: "scene" },
684
+ /**
685
+ * The camera, and required in the sense that matters: it may come from the
686
+ * scene instead, but it may not be invented. A render with an implicit
687
+ * camera is a render nobody can reproduce, and `cascade.core.Camera` is one
688
+ * node away.
689
+ *
690
+ * Wired here it **overrides** the scene's own camera, which is what lets one
691
+ * scene be rendered from several viewpoints without rebuilding it — and is
692
+ * also why the viewport's navigated camera never reaches this node. A view
693
+ * you dragged is not a document.
694
+ */
695
+ camera: { kind: "data", type: "camera" }
696
+ },
697
+ outputs: {
698
+ image: { kind: "data", type: "image" },
699
+ asset: { kind: "data", type: "asset" }
700
+ },
701
+ props: {
702
+ /** Pixels. Separate from the camera's own `resolution`, which describes
703
+ * the frame the projection assumes — so a preview renders small and frames
704
+ * identically to the full-size render. */
705
+ size: { type: "vec2i", default: [1280, 720], min: 16, max: 8192 },
706
+ filename: { type: "string", default: "render.png", label: "Filename" },
707
+ background: { type: "color", default: [0, 0, 0, 0], label: "Background" },
708
+ /** Fallback for a primitive with no `Cd`, exactly as SvgExport's is. */
709
+ stroke: { type: "color", default: [0, 0, 0, 1], label: "Stroke" },
710
+ strokeWidth: { type: "float", default: 1, min: 0, max: 200, step: 0.05 },
711
+ opacity: { type: "float", default: 1, min: 0, max: 1, step: 0.01 },
712
+ /** Points no primitive claimed. Off by default: a trail system has
713
+ * thousands of vertices and a dot on every one buries the strokes. */
714
+ drawPoints: { type: "bool", default: false, label: "Draw Loose Points" },
715
+ pointRadius: { type: "float", default: 1.5, min: 0, max: 50, step: 0.1 }
716
+ }
717
+ };
718
+ var fromRectsDefinition = {
719
+ apiVersion: 1,
720
+ label: "From Rects",
721
+ description: "Convert rects into geometry, one closed polygon each, keeping the tag.",
722
+ icon: "Frame",
723
+ runsOn: "portable",
724
+ inputs: {
725
+ rects: { kind: "data", type: "rects" },
726
+ /** The frame the rects were measured against, for the flip. */
727
+ height: {
728
+ kind: "data",
729
+ type: "float",
730
+ default: 0,
731
+ min: 0,
732
+ description: "Image height, for Top Left. Zero means no flip."
733
+ }
734
+ },
735
+ outputs: { geometry: { kind: "data", type: "geometry" } },
736
+ props: {
737
+ origin: {
738
+ type: "string",
739
+ default: "top-left",
740
+ control: "select",
741
+ options: [
742
+ { value: "top-left", label: "Top Left - raster, needs a height" },
743
+ { value: "bottom-left", label: "Bottom Left - already +Y up" }
744
+ ],
745
+ label: "Origin",
746
+ description: "Where y is measured from. Raster rects need flipping or the SVG comes out mirrored."
747
+ }
748
+ }
749
+ };
750
+ function executeFromRects(context) {
751
+ const incoming = context.inputs.rects;
752
+ const origin = context.props.origin === "bottom-left" ? "bottom-left" : "top-left";
753
+ const height = context.inputs.height ?? 0;
754
+ if (origin === "top-left" && height <= 0 && rectCount(incoming) > 0)
755
+ throw new Error(
756
+ "cascade.geo.FromRects needs the image height to flip raster rects \u2014 wire it, or set Origin to Bottom Left if the rects are already +Y up"
757
+ );
758
+ context.outputs.geometry.set(
759
+ rectsToGeometry(incoming, { origin, height })
760
+ );
761
+ }
762
+ function rectCount(value) {
763
+ if (Array.isArray(value)) return value.length;
764
+ const rects = value?.rects;
765
+ return Array.isArray(rects) ? rects.length : 0;
766
+ }
767
+ var fromRectsRegistration = {
768
+ kind: "definition-v1",
769
+ moduleId: "cascade.geo.FromRects",
770
+ definition: fromRectsDefinition,
771
+ loadExecute: async () => executeFromRects
772
+ };
773
+ var geoNodeDefinitions = Object.freeze([
774
+ ["cascade.geo.Rectangle", rectangleDefinition],
775
+ ["cascade.geo.Circle", circleDefinition],
776
+ ["cascade.geo.Transform", transformDefinition],
777
+ ["cascade.geo.Merge", mergeDefinition],
778
+ ["cascade.geo.CopyToPoints", copyToPointsDefinition],
779
+ ["cascade.geo.SvgExport", svgExportDefinition],
780
+ ["cascade.geo.Render", renderDefinition],
781
+ ["cascade.geo.FromRects", fromRectsDefinition]
782
+ ]);
783
+
784
+ // packages/runtime/src/builtins/geo/namespace.ts
785
+ var GEO_NAMESPACE = "cascade.geo";
786
+ function geoModuleId(name) {
787
+ return `${GEO_NAMESPACE}.${name}`;
788
+ }
789
+
790
+ // packages/runtime/src/builtins/geo/copyToPoints.ts
791
+ function executeCopyToPoints(context) {
792
+ const source = context.inputs.source;
793
+ const target = context.inputs.target;
794
+ if (source === void 0 || target === void 0) {
795
+ context.outputs.geometry.set(emptyGeometry(2));
796
+ return;
797
+ }
798
+ context.outputs.geometry.set(
799
+ copyToPoints(source, target, {
800
+ targetGroup: context.props.targetGroup,
801
+ useTargetOrientations: context.props.useTargetOrientations
802
+ })
803
+ );
804
+ }
805
+ var copyToPointsRegistration = {
806
+ kind: "definition-v1",
807
+ moduleId: geoModuleId("CopyToPoints"),
808
+ definition: copyToPointsDefinition,
809
+ loadExecute: async () => executeCopyToPoints
810
+ };
811
+
812
+ // packages/runtime/src/geometry/generate.ts
813
+ function finitePair(value, code) {
814
+ if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]))
815
+ geometryError(code, "both components must be finite");
816
+ }
817
+ function rectangleGeometry(options = {}) {
818
+ const [width, height] = options.size ?? [1, 1];
819
+ const [cx, cy] = options.center ?? [0, 0];
820
+ finitePair([width, height], "rectangle-size");
821
+ finitePair([cx, cy], "rectangle-center");
822
+ if (width < 0 || height < 0)
823
+ geometryError("rectangle-size", "rectangle size must not be negative");
824
+ const x0 = cx - width / 2;
825
+ const x1 = cx + width / 2;
826
+ const y0 = cy - height / 2;
827
+ const y1 = cy + height / 2;
828
+ const builder = new GeometryBuilder();
829
+ builder.addPolygon([x0, y0, x1, y0, x1, y1, x0, y1], { closed: true });
830
+ return builder.build();
831
+ }
832
+ function circleGeometry(options = {}) {
833
+ const [cx, cy] = options.center ?? [0, 0];
834
+ const [rx, ry] = options.radius ?? [1, 1];
835
+ finitePair([cx, cy], "circle-center");
836
+ finitePair([rx, ry], "circle-radius");
837
+ if (rx < 0 || ry < 0)
838
+ geometryError("circle-radius", "circle radius must not be negative");
839
+ const builder = new GeometryBuilder();
840
+ if ((options.type ?? "bezier") === "poly") {
841
+ const divisions = options.divisions ?? 32;
842
+ if (!Number.isSafeInteger(divisions) || divisions < 3)
843
+ geometryError(
844
+ "circle-divisions",
845
+ "a polygonal circle needs an integer division count of at least 3"
846
+ );
847
+ const flat2 = [];
848
+ for (let index = 0; index < divisions; index += 1) {
849
+ const angle = 2 * Math.PI * index / divisions;
850
+ flat2.push(cx + rx * Math.cos(angle), cy + ry * Math.sin(angle));
851
+ }
852
+ builder.addPolygon(flat2, { closed: true });
853
+ return builder.build();
854
+ }
855
+ const k = 4 / 3 * Math.tan(Math.PI / 8);
856
+ const kx = rx * k;
857
+ const ky = ry * k;
858
+ const flat = [
859
+ cx + rx,
860
+ cy,
861
+ cx + rx,
862
+ cy + ky,
863
+ cx + kx,
864
+ cy + ry,
865
+ cx,
866
+ cy + ry,
867
+ cx - kx,
868
+ cy + ry,
869
+ cx - rx,
870
+ cy + ky,
871
+ cx - rx,
872
+ cy,
873
+ cx - rx,
874
+ cy - ky,
875
+ cx - kx,
876
+ cy - ry,
877
+ cx,
878
+ cy - ry,
879
+ cx + kx,
880
+ cy - ry,
881
+ cx + rx,
882
+ cy - ky
883
+ ];
884
+ builder.addPolygon(flat, { closed: true, kind: "bezier" });
885
+ return builder.build();
886
+ }
887
+
888
+ // packages/runtime/src/builtins/geo/generators.ts
889
+ function executeRectangle(context) {
890
+ context.outputs.geometry.set(
891
+ rectangleGeometry({
892
+ size: context.inputs.size,
893
+ center: context.inputs.center
894
+ })
895
+ );
896
+ }
897
+ var rectangleRegistration = {
898
+ kind: "definition-v1",
899
+ moduleId: geoModuleId("Rectangle"),
900
+ definition: rectangleDefinition,
901
+ loadExecute: async () => executeRectangle
902
+ };
903
+ function executeCircle(context) {
904
+ context.outputs.geometry.set(
905
+ circleGeometry({
906
+ center: context.inputs.center,
907
+ radius: context.inputs.radius,
908
+ divisions: context.inputs.divisions,
909
+ type: context.props.type === "poly" ? "poly" : "bezier"
910
+ })
911
+ );
912
+ }
913
+ var circleRegistration = {
914
+ kind: "definition-v1",
915
+ moduleId: geoModuleId("Circle"),
916
+ definition: circleDefinition,
917
+ loadExecute: async () => executeCircle
918
+ };
919
+
920
+ // packages/runtime/src/builtins/geo/merge.ts
921
+ function executeMerge(context) {
922
+ context.outputs.geometry.set(
923
+ mergeGeometries(context.inputs.inputs.filter((input) => input !== void 0))
924
+ );
925
+ }
926
+ var mergeRegistration = {
927
+ kind: "definition-v1",
928
+ moduleId: geoModuleId("Merge"),
929
+ definition: mergeDefinition,
930
+ loadExecute: async () => executeMerge
931
+ };
932
+
933
+ // packages/runtime/src/geometry/render.ts
934
+ function css(colour) {
935
+ const [r, g, b, a = 1] = colour;
936
+ return `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}, ${a})`;
937
+ }
938
+ function multiply(matrix, x, y, z) {
939
+ return [0, 1, 2, 3].map(
940
+ (row) => matrix[row] * x + matrix[4 + row] * y + matrix[8 + row] * z + matrix[12 + row]
941
+ );
942
+ }
943
+ function renderScene(geometry, options) {
944
+ const [width, height] = options.size;
945
+ const canvas = options.surface(
946
+ Math.max(1, Math.round(width)),
947
+ Math.max(1, Math.round(height))
948
+ );
949
+ const context = canvas.getContext("2d");
950
+ if (!context) throw new Error("no 2D context on this surface");
951
+ if (options.background && options.background[3] > 0) {
952
+ context.fillStyle = css(options.background);
953
+ context.fillRect(0, 0, canvas.width, canvas.height);
954
+ }
955
+ for (const one of geometry) drawGeometry(context, canvas, one, options);
956
+ return canvas;
957
+ }
958
+ function drawGeometry(context, canvas, geometry, options) {
959
+ const view = viewMatrix(options.camera);
960
+ const projection = projectionMatrix(options.camera);
961
+ const position = geometry.point[POSITION_ATTRIBUTE];
962
+ if (!position || position.storage === "string") return;
963
+ const size = position.size;
964
+ const points = position.data;
965
+ const projected = [];
966
+ for (let index = 0; index < geometry.pointCount; index += 1) {
967
+ const world = multiply(
968
+ view,
969
+ Number(points[index * size]),
970
+ Number(points[index * size + 1]),
971
+ size >= 3 ? Number(points[index * size + 2]) : 0
972
+ );
973
+ const clip = multiply(projection, world[0], world[1], world[2]);
974
+ const w = clip[3];
975
+ projected.push({
976
+ x: (clip[0] / w + 1) * 0.5 * canvas.width,
977
+ y: (1 - (clip[1] / w + 1) * 0.5) * canvas.height,
978
+ w
979
+ });
980
+ }
981
+ const colours = numericAttribute(geometry.primitive.Cd, "primitive.Cd", [4]);
982
+ const widths = numericAttribute(geometry.primitive.width, "primitive.width", [1]);
983
+ context.lineCap = "round";
984
+ context.lineJoin = "round";
985
+ context.globalAlpha = options.opacity;
986
+ const { vertexPoints, offsets, closed } = geometry.topology;
987
+ const drawn = /* @__PURE__ */ new Set();
988
+ for (let primitive = 0; primitive < geometry.primitiveCount; primitive += 1) {
989
+ const from = offsets[primitive];
990
+ const to = offsets[primitive + 1];
991
+ if (to - from < 2) continue;
992
+ context.strokeStyle = colours ? css(readElement(colours, primitive)) : css(options.stroke);
993
+ context.lineWidth = widths ? readElement(widths, primitive)[0] : options.strokeWidth;
994
+ context.beginPath();
995
+ let open = false;
996
+ for (let vertex = from; vertex < to; vertex += 1) {
997
+ const point = vertexPoints[vertex];
998
+ drawn.add(point);
999
+ const at = projected[point];
1000
+ if (!at || at.w <= 0) {
1001
+ open = false;
1002
+ continue;
1003
+ }
1004
+ if (open) context.lineTo(at.x, at.y);
1005
+ else {
1006
+ context.moveTo(at.x, at.y);
1007
+ open = true;
1008
+ }
1009
+ }
1010
+ if (closed[primitive] === 1 && open) {
1011
+ const first = projected[vertexPoints[from]];
1012
+ if (first && first.w > 0) context.lineTo(first.x, first.y);
1013
+ }
1014
+ context.stroke();
1015
+ }
1016
+ if (options.drawPoints && options.pointRadius > 0) {
1017
+ context.fillStyle = css(options.stroke);
1018
+ for (let index = 0; index < projected.length; index += 1) {
1019
+ if (drawn.has(index)) continue;
1020
+ const at = projected[index];
1021
+ if (at.w <= 0) continue;
1022
+ context.beginPath();
1023
+ context.arc(at.x, at.y, options.pointRadius, 0, Math.PI * 2);
1024
+ context.fill();
1025
+ }
1026
+ }
1027
+ }
1028
+ async function renderToPng(surface) {
1029
+ const blob = await surface.convertToBlob({ type: "image/png" });
1030
+ return new Uint8Array(await blob.arrayBuffer());
1031
+ }
1032
+
1033
+ // packages/runtime/src/builtins/geo/render.ts
1034
+ function hostSurface(width, height) {
1035
+ const factory = globalThis.OffscreenCanvas;
1036
+ if (!factory) {
1037
+ throw new Error(
1038
+ "cascade.geo.Render needs OffscreenCanvas, which this host does not provide"
1039
+ );
1040
+ }
1041
+ return new factory(width, height);
1042
+ }
1043
+ async function executeRender(context) {
1044
+ const { props } = context;
1045
+ const scene = asScene(context.inputs.scene);
1046
+ const camera = context.inputs.camera ?? scene.camera;
1047
+ if (!camera) {
1048
+ throw new Error(
1049
+ "cascade.geo.Render has no camera \u2014 wire a cascade.core.Camera to its camera input, or into the scene"
1050
+ );
1051
+ }
1052
+ const surface = renderScene(scene.geometry, {
1053
+ camera,
1054
+ size: [props.size[0], props.size[1]],
1055
+ ...props.background[3] > 0 ? { background: props.background } : {},
1056
+ stroke: props.stroke,
1057
+ strokeWidth: props.strokeWidth,
1058
+ opacity: props.opacity,
1059
+ pointRadius: props.pointRadius,
1060
+ drawPoints: props.drawPoints,
1061
+ surface: hostSurface
1062
+ });
1063
+ const bytes = await renderToPng(surface);
1064
+ const asset = await context.capabilities.assets.write(
1065
+ bytes,
1066
+ { mediaType: "image/png", suggestedName: props.filename },
1067
+ { signal: context.signal }
1068
+ );
1069
+ context.outputs.asset.set(asset);
1070
+ context.outputs.image.set({
1071
+ path: asset.path ?? props.filename,
1072
+ size: [surface.width, surface.height],
1073
+ channels: "rgba",
1074
+ depth: "u8",
1075
+ space: "srgb"
1076
+ });
1077
+ }
1078
+ var renderRegistration = {
1079
+ kind: "definition-v1",
1080
+ moduleId: geoModuleId("Render"),
1081
+ definition: renderDefinition,
1082
+ loadExecute: async () => executeRender
1083
+ };
1084
+
1085
+ // packages/runtime/src/geometry/primitives.ts
1086
+ function primitiveKind(geometry, primitive) {
1087
+ return PRIMITIVE_KINDS[geometry.topology.kinds[primitive]] ?? "poly";
1088
+ }
1089
+ function isClosed(geometry, primitive) {
1090
+ return geometry.topology.closed[primitive] === 1;
1091
+ }
1092
+ function isBezier(geometry, primitive) {
1093
+ return primitiveKind(geometry, primitive) === "bezier";
1094
+ }
1095
+ function bezierSegments(geometry, primitive) {
1096
+ const points = primitivePoints(geometry, primitive);
1097
+ const closed = isClosed(geometry, primitive);
1098
+ const count = closed ? points.length / BEZIER_STRIDE : (points.length - 1) / BEZIER_STRIDE;
1099
+ const segments = [];
1100
+ for (let index = 0; index < count; index += 1) {
1101
+ const base = index * BEZIER_STRIDE;
1102
+ segments.push([
1103
+ points[base],
1104
+ points[base + 1],
1105
+ points[base + 2],
1106
+ points[(base + BEZIER_STRIDE) % points.length]
1107
+ ]);
1108
+ }
1109
+ return segments;
1110
+ }
1111
+ function vertexRange(geometry, primitive) {
1112
+ return [
1113
+ geometry.topology.offsets[primitive],
1114
+ geometry.topology.offsets[primitive + 1]
1115
+ ];
1116
+ }
1117
+ function primitivePoints(geometry, primitive) {
1118
+ const [start, end] = vertexRange(geometry, primitive);
1119
+ const points = new Array(end - start);
1120
+ for (let vertex = start; vertex < end; vertex += 1)
1121
+ points[vertex - start] = geometry.topology.vertexPoints[vertex];
1122
+ return points;
1123
+ }
1124
+ function loosePointMask(geometry) {
1125
+ const used = new Uint8Array(geometry.pointCount);
1126
+ for (const point of geometry.topology.vertexPoints) used[point] = 1;
1127
+ const loose = new Uint8Array(geometry.pointCount);
1128
+ for (let index = 0; index < loose.length; index += 1)
1129
+ loose[index] = used[index] ? 0 : 1;
1130
+ return loose;
1131
+ }
1132
+ function geometryBounds(geometry) {
1133
+ if (geometry.pointCount === 0) return void 0;
1134
+ const position = positionAttribute(geometry);
1135
+ const size = positionSize(geometry);
1136
+ const min = new Array(size).fill(Number.POSITIVE_INFINITY);
1137
+ const max = new Array(size).fill(Number.NEGATIVE_INFINITY);
1138
+ for (let point = 0; point < geometry.pointCount; point += 1)
1139
+ for (let component = 0; component < size; component += 1) {
1140
+ const value = position.data[point * size + component];
1141
+ if (value < min[component]) min[component] = value;
1142
+ if (value > max[component]) max[component] = value;
1143
+ }
1144
+ return { min, max };
1145
+ }
1146
+
1147
+ // packages/runtime/src/geometry/svg.ts
1148
+ var UNGROUPED = "ungrouped";
1149
+ var BLACK = [0, 0, 0, 1];
1150
+ function geometryToSvg(geometry, options = {}) {
1151
+ validateOptions(options);
1152
+ const margin = options.margin ?? 0;
1153
+ const explicit = options.bounds;
1154
+ const bounds = explicit ? void 0 : geometryBounds(geometry);
1155
+ const minX = explicit ? explicit[0] - margin : bounds === void 0 ? 0 : bounds.min[0] - margin;
1156
+ const minY = explicit ? explicit[1] - margin : bounds === void 0 ? 0 : bounds.min[1] - margin;
1157
+ const maxX = explicit ? explicit[2] + margin : bounds === void 0 ? 1 : bounds.max[0] + margin;
1158
+ const maxY = explicit ? explicit[3] + margin : bounds === void 0 ? 1 : bounds.max[1] + margin;
1159
+ const spanX = Math.max(maxX - minX, Number.EPSILON);
1160
+ const spanY = Math.max(maxY - minY, Number.EPSILON);
1161
+ const width = options.width && options.width > 0 ? options.width : spanX;
1162
+ const height = options.height && options.height > 0 ? options.height : spanY;
1163
+ const precision = options.precision ?? 3;
1164
+ const number = (value) => format(value, precision);
1165
+ const stroke = options.stroke ?? BLACK;
1166
+ const strokeWidth = options.strokeWidth ?? 1;
1167
+ const fill = options.fill ?? BLACK;
1168
+ const filled = (options.fillMode ?? "none") === "solid";
1169
+ const colour = geometry.primitive.Cd;
1170
+ const strokeWidths = numericAttribute(geometry.primitive.width, "primitive.width", [1]);
1171
+ const opacities = numericAttribute(geometry.primitive.opacity, "primitive.opacity", [1]);
1172
+ const pointScale = numericAttribute(geometry.point.pscale, "point.pscale", [1]);
1173
+ const pointColour = geometry.point.Cd;
1174
+ validateRange(strokeWidths, "primitive.width", (value) => value >= 0, "non-negative");
1175
+ validateRange(opacities, "primitive.opacity", (value) => value >= 0 && value <= 1, "between 0 and 1");
1176
+ validateRange(pointScale, "point.pscale", (value) => value >= 0, "non-negative");
1177
+ const names = groupNames(geometry, "primitive");
1178
+ const assigned = new Int32Array(geometry.primitiveCount).fill(-1);
1179
+ for (let index = 0; index < names.length; index += 1) {
1180
+ const mask = geometry.primitiveGroups[names[index]];
1181
+ for (let primitive = 0; primitive < geometry.primitiveCount; primitive += 1)
1182
+ if (mask[primitive] === 1 && assigned[primitive] === -1)
1183
+ assigned[primitive] = index;
1184
+ }
1185
+ const path = (primitive) => {
1186
+ const attributes = [`d="${pathData(geometry, primitive, number)}"`];
1187
+ const paint = paintOf(colour, primitive, "primitive");
1188
+ if (paint !== void 0) {
1189
+ attributes.push(`stroke="${Color.toHex(paint)}"`);
1190
+ if (Color.alpha(paint) < 1)
1191
+ attributes.push(`stroke-opacity="${number(Color.alpha(paint))}"`);
1192
+ }
1193
+ if (strokeWidths !== void 0)
1194
+ attributes.push(
1195
+ `stroke-width="${number(readComponent(strokeWidths, primitive))}"`
1196
+ );
1197
+ if (opacities !== void 0)
1198
+ attributes.push(`opacity="${number(readComponent(opacities, primitive))}"`);
1199
+ return `<path ${attributes.join(" ")} />`;
1200
+ };
1201
+ const body = [];
1202
+ if (names.length === 0) {
1203
+ for (let primitive = 0; primitive < geometry.primitiveCount; primitive += 1)
1204
+ body.push(indent(path(primitive), 2));
1205
+ } else {
1206
+ for (let index = 0; index < names.length; index += 1) {
1207
+ const members = [];
1208
+ for (let primitive = 0; primitive < geometry.primitiveCount; primitive += 1)
1209
+ if (assigned[primitive] === index)
1210
+ members.push(indent(path(primitive), 3));
1211
+ body.push(
1212
+ indent(`<g id="${escapeXml(names[index])}">`, 2),
1213
+ ...members,
1214
+ indent("</g>", 2)
1215
+ );
1216
+ }
1217
+ const rest = [];
1218
+ for (let primitive = 0; primitive < geometry.primitiveCount; primitive += 1)
1219
+ if (assigned[primitive] === -1) rest.push(indent(path(primitive), 3));
1220
+ if (rest.length > 0)
1221
+ body.push(
1222
+ indent(`<g id="${UNGROUPED}">`, 2),
1223
+ ...rest,
1224
+ indent("</g>", 2)
1225
+ );
1226
+ }
1227
+ const loose = loosePointMask(geometry);
1228
+ const position = positionAttribute(geometry);
1229
+ for (let point = 0; point < geometry.pointCount; point += 1) {
1230
+ if (loose[point] !== 1) continue;
1231
+ const radius = pointScale === void 0 ? options.pointRadius ?? 0.5 : readComponent(pointScale, point);
1232
+ const paint = paintOf(pointColour, point, "point") ?? stroke;
1233
+ const transparent = Color.alpha(paint) < 1;
1234
+ body.push(
1235
+ indent(
1236
+ `<circle cx="${number(readComponent(position, point, 0))}" cy="${number(
1237
+ readComponent(position, point, 1)
1238
+ )}" r="${number(radius)}" fill="${Color.toHex(paint)}"${transparent ? ` fill-opacity="${number(Color.alpha(paint))}"` : ""} stroke="none" />`,
1239
+ 2
1240
+ )
1241
+ );
1242
+ }
1243
+ const root = [
1244
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${number(width)}" height="${number(height)}" viewBox="${number(minX)} ${number(-maxY)} ${number(spanX)} ${number(spanY)}">`,
1245
+ ...options.background ? [indent(
1246
+ `<rect x="${number(minX)}" y="${number(-maxY)}" width="${number(spanX)}" height="${number(spanY)}" fill="${Color.toHex(options.background)}" />`,
1247
+ 1
1248
+ )] : [],
1249
+ indent("<!-- Geometry is +Y up; SVG is +Y down. This is the one flip. -->", 1),
1250
+ indent(
1251
+ `<g transform="scale(1,-1)" fill="${filled ? Color.toHex(fill) : "none"}"${filled && Color.alpha(fill) < 1 ? ` fill-opacity="${number(Color.alpha(fill))}"` : ""} stroke="${Color.toHex(stroke)}"${Color.alpha(stroke) < 1 ? ` stroke-opacity="${number(Color.alpha(stroke))}"` : ""} stroke-width="${number(strokeWidth)}"${options.opacity !== void 0 && options.opacity !== 1 ? ` opacity="${number(options.opacity)}"` : ""}>`,
1252
+ 1
1253
+ ),
1254
+ ...body,
1255
+ indent("</g>", 1),
1256
+ "</svg>"
1257
+ ];
1258
+ return `${root.join("\n")}
1259
+ `;
1260
+ }
1261
+ function pathData(geometry, primitive, number) {
1262
+ const position = positionAttribute(geometry);
1263
+ const at = (point) => `${number(readComponent(position, point, 0))} ${number(
1264
+ readComponent(position, point, 1)
1265
+ )}`;
1266
+ const closed = isClosed(geometry, primitive);
1267
+ if (isBezier(geometry, primitive)) {
1268
+ const segments = bezierSegments(geometry, primitive);
1269
+ if (segments.length === 0) return "";
1270
+ const commands2 = [`M ${at(segments[0][0])}`];
1271
+ for (const [, first, second, end] of segments)
1272
+ commands2.push(`C ${at(first)} ${at(second)} ${at(end)}`);
1273
+ if (closed) commands2.push("Z");
1274
+ return commands2.join(" ");
1275
+ }
1276
+ const points = primitivePoints(geometry, primitive);
1277
+ if (points.length === 0) return "";
1278
+ const commands = [`M ${at(points[0])}`];
1279
+ for (let index = 1; index < points.length; index += 1)
1280
+ commands.push(`L ${at(points[index])}`);
1281
+ if (closed) commands.push("Z");
1282
+ return commands.join(" ");
1283
+ }
1284
+ function paintOf(attribute, index, level) {
1285
+ if (attribute === void 0) return void 0;
1286
+ if (isStringAttribute(attribute))
1287
+ geometryError(
1288
+ "colour-storage",
1289
+ `${level}.Cd is a string attribute; colour is a vec4. Convert with Color.fromHex at the boundary.`
1290
+ );
1291
+ if (attribute.size !== 4)
1292
+ geometryError(
1293
+ "colour-size",
1294
+ `${level}.Cd has size ${attribute.size}; colour is a vec4`
1295
+ );
1296
+ return Color.fromComponents(attribute.data, index * 4, 4);
1297
+ }
1298
+ function format(value, precision) {
1299
+ if (!Number.isFinite(value))
1300
+ geometryError("svg-number", `cannot format non-finite value ${String(value)}`);
1301
+ const rounded = Number.parseFloat(value.toFixed(precision));
1302
+ return Object.is(rounded, -0) ? "0" : String(rounded);
1303
+ }
1304
+ function validateOptions(options) {
1305
+ for (const [name, value] of [
1306
+ ["width", options.width],
1307
+ ["height", options.height],
1308
+ ["margin", options.margin],
1309
+ ["strokeWidth", options.strokeWidth],
1310
+ ["opacity", options.opacity],
1311
+ ["pointRadius", options.pointRadius]
1312
+ ]) {
1313
+ if (value !== void 0 && !Number.isFinite(value))
1314
+ geometryError("svg-option", `${name} must be finite`);
1315
+ }
1316
+ if ((options.width ?? 0) < 0 || (options.height ?? 0) < 0)
1317
+ geometryError("svg-option", "width and height must not be negative");
1318
+ if ((options.strokeWidth ?? 0) < 0 || (options.pointRadius ?? 0) < 0)
1319
+ geometryError("svg-option", "strokeWidth and pointRadius must not be negative");
1320
+ if (options.opacity !== void 0 && (options.opacity < 0 || options.opacity > 1))
1321
+ geometryError("svg-option", "opacity must be between 0 and 1");
1322
+ const precision = options.precision ?? 3;
1323
+ if (!Number.isSafeInteger(precision) || precision < 0 || precision > 15)
1324
+ geometryError("svg-precision", "precision must be an integer from 0 to 15");
1325
+ }
1326
+ function validateRange(attribute, path, accepts, expectation) {
1327
+ if (attribute === void 0) return;
1328
+ for (const value of attribute.data)
1329
+ if (!accepts(value))
1330
+ geometryError("attribute-value", `${path} values must be ${expectation}`);
1331
+ }
1332
+ function indent(line, depth) {
1333
+ return `${" ".repeat(depth)}${line}`;
1334
+ }
1335
+ function escapeXml(value) {
1336
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1337
+ }
1338
+ function utf8Bytes(value) {
1339
+ const bytes = [];
1340
+ for (const character of value) {
1341
+ let code = character.codePointAt(0);
1342
+ if (code < 128) bytes.push(code);
1343
+ else if (code < 2048) bytes.push(192 | code >> 6, 128 | code & 63);
1344
+ else if (code < 65536)
1345
+ bytes.push(
1346
+ 224 | code >> 12,
1347
+ 128 | code >> 6 & 63,
1348
+ 128 | code & 63
1349
+ );
1350
+ else
1351
+ bytes.push(
1352
+ 240 | code >> 18,
1353
+ 128 | code >> 12 & 63,
1354
+ 128 | code >> 6 & 63,
1355
+ 128 | code & 63
1356
+ );
1357
+ code = 0;
1358
+ }
1359
+ return Uint8Array.from(bytes);
1360
+ }
1361
+
1362
+ // packages/runtime/src/builtins/geo/svgExport.ts
1363
+ async function executeSvgExport(context) {
1364
+ const { props } = context;
1365
+ const svg = geometryToSvg(context.inputs.geometry ?? emptyGeometry(2), {
1366
+ width: props.width,
1367
+ height: props.height,
1368
+ margin: props.margin,
1369
+ // All-zero means "derive", because a zero-area rectangle is not a frame
1370
+ // anyone would ask for and a separate boolean for it would be a second
1371
+ // way to say the same thing.
1372
+ ...props.bounds.some((value) => value !== 0) ? { bounds: [props.bounds[0], props.bounds[1], props.bounds[2], props.bounds[3]] } : {},
1373
+ // Only when it would show: a fully transparent ground and no ground at all
1374
+ // are the same document, and emitting a rect for it would put a shape in
1375
+ // the file that a plotter would try to draw.
1376
+ ...props.background[3] > 0 ? { background: props.background } : {},
1377
+ stroke: props.stroke,
1378
+ strokeWidth: props.strokeWidth,
1379
+ opacity: props.opacity,
1380
+ fill: props.fill,
1381
+ fillMode: props.fillMode === "solid" ? "solid" : "none",
1382
+ pointRadius: props.pointRadius,
1383
+ precision: props.precision
1384
+ });
1385
+ context.outputs.svg.set(svg);
1386
+ const asset = await context.capabilities.assets.write(
1387
+ utf8Bytes(svg),
1388
+ { mediaType: "image/svg+xml", suggestedName: props.filename },
1389
+ { signal: context.signal }
1390
+ );
1391
+ context.outputs.asset.set(asset);
1392
+ }
1393
+ var svgExportRegistration = {
1394
+ kind: "definition-v1",
1395
+ moduleId: geoModuleId("SvgExport"),
1396
+ definition: svgExportDefinition,
1397
+ loadExecute: async () => executeSvgExport
1398
+ };
1399
+
1400
+ // packages/runtime/src/builtins/geo/transform.ts
1401
+ function executeTransform(context) {
1402
+ const geometry = context.inputs.geometry ?? emptyGeometry(2);
1403
+ context.outputs.geometry.set(
1404
+ transformGeometry(
1405
+ geometry,
1406
+ transformMatrix({
1407
+ translate: context.inputs.translate,
1408
+ rotate: context.inputs.rotate,
1409
+ scale: context.inputs.scale,
1410
+ pivot: context.inputs.pivot
1411
+ })
1412
+ )
1413
+ );
1414
+ }
1415
+ var transformRegistration = {
1416
+ kind: "definition-v1",
1417
+ moduleId: geoModuleId("Transform"),
1418
+ definition: transformDefinition,
1419
+ loadExecute: async () => executeTransform
1420
+ };
1421
+
1422
+ // packages/runtime/src/builtins/geo/index.ts
1423
+ var geoNodeRegistrations = Object.freeze([
1424
+ rectangleRegistration,
1425
+ circleRegistration,
1426
+ transformRegistration,
1427
+ mergeRegistration,
1428
+ copyToPointsRegistration,
1429
+ renderRegistration,
1430
+ svgExportRegistration,
1431
+ fromRectsRegistration
1432
+ ]);
1433
+ var registrationsById = new Map(
1434
+ geoNodeRegistrations.map((registration) => [
1435
+ registration.moduleId,
1436
+ registration
1437
+ ])
1438
+ );
1439
+
1440
+ export {
1441
+ LIGHT_DEFAULTS,
1442
+ isLight,
1443
+ isScene,
1444
+ geoNodeDefinitions,
1445
+ geoNodeRegistrations
1446
+ };
1447
+ //# sourceMappingURL=chunk-35D343DC.js.map