@field/cascade 0.11.0 → 0.13.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 (220) hide show
  1. package/AGENTS.md +8 -5
  2. package/ARCHITECTURE.md +5 -4
  3. package/CHANGELOG.md +245 -0
  4. package/README.md +1 -1
  5. package/dist/assets/AgentPanel-1JugFjE4.css +1 -0
  6. package/dist/assets/AgentPanel-BDL5MddA.js +5 -0
  7. package/dist/assets/{CodeEditor-4O7FQ__R.js → CodeEditor-C-xYG-Wn.js} +120 -120
  8. package/dist/assets/CodePanel-CtXd50m6.js +2 -0
  9. package/dist/assets/CookInfoPanel-B3NMhKJF.css +1 -0
  10. package/dist/assets/CookInfoPanel-DG4_TRsX.js +2 -0
  11. package/dist/assets/CustomNodeDialog-CHfZUs-r.js +1 -0
  12. package/dist/assets/DefinitionNode-CaRTXQ_b.js +1 -0
  13. package/dist/assets/DefinitionPanel-C3CxZY9r.js +1 -0
  14. package/dist/assets/DefinitionPanel-VNLnAiQD.css +1 -0
  15. package/dist/assets/OpenGraphDialog-BLdr6k_q.js +1 -0
  16. package/dist/assets/ProjectSettings-ZnvmIAir.js +2 -0
  17. package/dist/assets/{SettingsDialog-Bz8JxISH.js → SettingsDialog-CBX3Wa87.js} +1 -1
  18. package/dist/assets/TimelinePanel-CDeR0arY.css +1 -0
  19. package/dist/assets/TimelinePanel-CWgnFH1e.js +1 -0
  20. package/dist/assets/{VersionHistoryDialog-D5Z0nJ5-.js → VersionHistoryDialog-BOSoW1R-.js} +1 -1
  21. package/dist/assets/assert-BO2_erPD.js +1 -0
  22. package/dist/assets/assert-hvykGCnP.js +1 -0
  23. package/dist/assets/builder-BmBKcDix.js +1 -0
  24. package/dist/assets/camera-DFF7YA3F.js +1 -0
  25. package/dist/assets/checks-DAvyO9Gf.js +1 -0
  26. package/dist/assets/contactSheet-N1NcaWxw.js +2 -0
  27. package/dist/assets/{core-Yqi0Ni61.js → core-DB-MkGjj.js} +3 -4
  28. package/dist/assets/coreTypes-DtnDATZ7.js +1 -0
  29. package/dist/assets/{cssMode-CxUzc4vp.js → cssMode-DPVckSOc.js} +1 -1
  30. package/dist/assets/definitions-B227e7f_.js +1 -0
  31. package/dist/assets/definitions-D0WOUJd7.js +1 -0
  32. package/dist/assets/{editor.api-BNFb25CX.js → editor.api-Dkutg_Z1.js} +1 -1
  33. package/dist/assets/expressions-X-Z-GrgW.js +5 -0
  34. package/dist/assets/file-DAL6IfjV.js +1 -0
  35. package/dist/assets/filters-D3k1LRMa.js +1 -0
  36. package/dist/assets/{freemarker2-0ZDnWxeM.js → freemarker2-CLiLKk3B.js} +1 -1
  37. package/dist/assets/generators-BhDxRAFC.js +1 -0
  38. package/dist/assets/geo-BsMImJIJ.js +1 -0
  39. package/dist/assets/geo-C4YngDob.js +2 -0
  40. package/dist/assets/{handlebars-Cy18MoaD.js → handlebars-CcD3aMeL.js} +1 -1
  41. package/dist/assets/{html-B-2Dx7nq.js → html-DvZWGfqb.js} +1 -1
  42. package/dist/assets/{htmlMode-DUAc9B_d.js → htmlMode-DUD3VaoN.js} +1 -1
  43. package/dist/assets/image-0SfH-snh.js +1 -0
  44. package/dist/assets/image-Ck-RdGU4.js +2 -0
  45. package/dist/assets/index-BWnrmIB9.js +137 -0
  46. package/dist/assets/index-DIaxEONI.css +1 -0
  47. package/dist/assets/io-BGmt2g0D.js +1 -0
  48. package/dist/assets/{javascript-DeQsDqP5.js → javascript-Cap2ahgh.js} +1 -1
  49. package/dist/assets/{jsonMode-BiFwJVSk.js → jsonMode-B0uLjp9C.js} +1 -1
  50. package/dist/assets/{liquid-BLoHH68a.js → liquid-CnxwhfLn.js} +1 -1
  51. package/dist/assets/{lspLanguageFeatures-CNFgm1Kv.js → lspLanguageFeatures-Dn9hlKX6.js} +1 -1
  52. package/dist/assets/math-GeULS-sk.js +1 -0
  53. package/dist/assets/{mdx-CFXQVNfs.js → mdx-BV3jbrVa.js} +1 -1
  54. package/dist/assets/pop-KMF_qloi.js +1 -0
  55. package/dist/assets/preload-helper-Czpn1I53.js +1 -0
  56. package/dist/assets/{python-Cf7S7p2E.js → python-CGyFk4XK.js} +1 -1
  57. package/dist/assets/{razor-CMHBcG-g.js → razor-CQfzX3-N.js} +1 -1
  58. package/dist/assets/remap-5SjXQU-q.js +1 -0
  59. package/dist/assets/renderMarkdown-ork0Oir7.js +67 -0
  60. package/dist/assets/studioImages-CAyH-0Pl.js +1 -0
  61. package/dist/assets/text-ZpX7qNkx.js +2 -0
  62. package/dist/assets/time-P_OiBhcf.js +1 -0
  63. package/dist/assets/{tsMode-BEih0GDj.js → tsMode-BOeXzO9H.js} +1 -1
  64. package/dist/assets/{typescript-COEKYD_a.js → typescript-J1SnE_Tu.js} +1 -1
  65. package/dist/assets/{workers-BNhAhMNn.js → workers-CYwwTiVF.js} +1 -1
  66. package/dist/assets/{xml-CPiMpnPl.js → xml-DnPRNzK9.js} +1 -1
  67. package/dist/assets/{yaml-Bgp1ITLR.js → yaml-BmmYBNa9.js} +1 -1
  68. package/dist/cli/DefinitionNode-P7AA4TAU.js +15 -0
  69. package/dist/cli/{Graph-ZJJTRQTM.js → Graph-OB6EWSGL.js} +12 -12
  70. package/dist/cli/assert-GROGQNCQ.js +25 -0
  71. package/dist/cli/checks-3WB44XBL.js +189 -0
  72. package/dist/cli/{chunk-3JTJHF7V.js → chunk-2L34ERS2.js} +4 -4
  73. package/dist/cli/chunk-53LGFPWX.js +138 -0
  74. package/dist/cli/chunk-5ORUQ6ES.js +267 -0
  75. package/dist/cli/{chunk-YAT5Y25U.js → chunk-7QE2ZNSN.js} +3 -3
  76. package/dist/cli/{chunk-SGJAET2L.js → chunk-BPODRH52.js} +8 -4
  77. package/dist/cli/{chunk-XWDSKG7N.js → chunk-BZ63XQ3I.js} +2 -2
  78. package/dist/cli/{chunk-FCDXJ6EK.js → chunk-CTPLRSWF.js} +2 -13
  79. package/dist/cli/{chunk-WIH4TEIS.js → chunk-EZRWJ4G2.js} +1 -1
  80. package/dist/cli/{chunk-BIJO5OFS.js → chunk-FWMSGKEH.js} +1 -1
  81. package/dist/cli/{chunk-POMMNKZZ.js → chunk-G23A2BZZ.js} +38 -5
  82. package/dist/cli/{chunk-35D343DC.js → chunk-GRIN2DDP.js} +19 -1
  83. package/dist/cli/{chunk-WKURDVYP.js → chunk-JEW5MPGI.js} +28 -331
  84. package/dist/cli/chunk-JVOCK5W4.js +83 -0
  85. package/dist/cli/chunk-KBGUO27Q.js +15 -0
  86. package/dist/cli/{chunk-GFS6W3C5.js → chunk-L6B6FUQP.js} +54 -32
  87. package/dist/cli/chunk-LTXWBXOM.js +438 -0
  88. package/dist/cli/chunk-MI5JLQYA.js +220 -0
  89. package/dist/cli/{chunk-A4OAMOZH.js → chunk-NJFMDXHR.js} +13 -3
  90. package/dist/cli/{chunk-DUIUFVU2.js → chunk-OBZAP3GG.js} +49 -23
  91. package/dist/cli/chunk-QAGATJGE.js +336 -0
  92. package/dist/cli/{chunk-EUIAC3U3.js → chunk-QKL7CCRS.js} +966 -132
  93. package/dist/cli/{chunk-RLCAJBVR.js → chunk-TJW67QSI.js} +8 -7
  94. package/dist/cli/{chunk-RQZ4SX5R.js → chunk-W5PJ5RMP.js} +156 -84
  95. package/dist/cli/chunk-W7OUAT7J.js +122 -0
  96. package/dist/cli/chunk-WGKPJZDY.js +364 -0
  97. package/dist/cli/{chunk-D6BUOHUG.js → chunk-X4WKAOQP.js} +4 -4
  98. package/dist/cli/{chunk-VVGSLVSF.js → chunk-X566FWBV.js} +6 -1
  99. package/dist/cli/chunk-YH637PDL.js +317 -0
  100. package/dist/cli/chunk-ZSCUZE6I.js +112 -0
  101. package/dist/cli/{cliCommands-UQSFGEHF.js → cliCommands-XVFRD7CF.js} +23 -17
  102. package/dist/cli/{commands-Q23M26QM.js → commands-M2XLTPTZ.js} +129 -47
  103. package/dist/cli/contactSheet-2PQVRUFW.js +11 -0
  104. package/dist/cli/{definitions-HF2FTHYU.js → definitions-BPOPW52L.js} +7 -7
  105. package/dist/cli/{doctorCommand-B7BPQMN6.js → doctorCommand-RAEI7GVY.js} +36 -6
  106. package/dist/cli/{edit-JHCPTQFT.js → edit-S5KEQ5RA.js} +3 -3
  107. package/dist/cli/editorCommands-5BBG4S6V.js +41 -0
  108. package/dist/cli/{encode-HAUZTYPT.js → encode-ICPEPMH5.js} +2 -2
  109. package/dist/cli/file-3HTQHPYQ.js +41 -0
  110. package/dist/cli/filters-6ODFWYK4.js +379 -0
  111. package/dist/cli/generators-YU7GB4MD.js +210 -0
  112. package/dist/cli/{geo-4RNK3QVN.js → geo-6ZPGKZRU.js} +7 -7
  113. package/dist/cli/{graphCommands-6BMAKGQE.js → graphCommands-MULQ7VD2.js} +4 -4
  114. package/dist/cli/{graphFile-VPSQ6OF7.js → graphFile-CITF36ZM.js} +25 -19
  115. package/dist/cli/{headlessCanvas-5XLDZT7M.js → headlessCanvas-ENRHC7GS.js} +3 -2
  116. package/dist/cli/{helpCommand-3VRE3KVO.js → helpCommand-X2QDANM3.js} +9 -7
  117. package/dist/cli/image-TXELZZK5.js +55 -0
  118. package/dist/cli/index.js +14 -14
  119. package/dist/cli/{math-ZRMLKU2D.js → math-RSYRQ3C6.js} +5 -5
  120. package/dist/cli/{mcp-ZLEEMYGF.js → mcp-YRIGRLYQ.js} +4 -4
  121. package/dist/cli/{parity-KINARCP2.js → parity-3N3HTKWX.js} +42 -36
  122. package/dist/cli/{pop-USDFXB7H.js → pop-B7SIIYKN.js} +5 -5
  123. package/dist/cli/project-SJF5ZF3F.js +12 -0
  124. package/dist/cli/{projectDefinition-DMTZN2NE.js → projectDefinition-F3D7UIHJ.js} +8 -8
  125. package/dist/cli/{projectRuntime-Z5PJOH47.js → projectRuntime-NY4XMDFH.js} +19 -12
  126. package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
  127. package/dist/cli/{render-YURAPPUI.js → render-Y23W6MY6.js} +29 -22
  128. package/dist/cli/{runCommand-PI764LEG.js → runCommand-XOG7AVC5.js} +3 -3
  129. package/dist/cli/runner-DNJO5WO2.js +43 -0
  130. package/dist/cli/runtime/image.ts +27 -2
  131. package/dist/cli/runtime/io.ts +78 -0
  132. package/dist/cli/share-B7HTCOJX.js +166 -0
  133. package/dist/cli/stageCache-TY2BFZJR.js +15 -0
  134. package/dist/cli/{stageRunner-KUNIMQKJ.js → stageRunner-KY2EJP7X.js} +2 -2
  135. package/dist/cli/text-MB3EKR4H.js +126 -0
  136. package/dist/cli/{verbs-RCE3K4VQ.js → verbs-PI2JH5FI.js} +2 -2
  137. package/dist/cli/webBuild-2K4COCGY.js +44 -0
  138. package/dist/contracts/capabilities.d.ts +39 -1
  139. package/dist/contracts/color.d.ts +8 -0
  140. package/dist/contracts/color.js +23 -0
  141. package/dist/contracts/definition.d.ts +10 -0
  142. package/dist/contracts/schema.d.ts +1 -1
  143. package/dist/contracts/schema.js +1 -0
  144. package/dist/contracts/validate.js +11 -1
  145. package/dist/contracts/values.d.ts +24 -0
  146. package/dist/image.d.ts +15 -0
  147. package/dist/image.js +36 -2
  148. package/dist/index.html +8 -2
  149. package/dist/io.d.ts +29 -0
  150. package/dist/io.js +40 -0
  151. package/dist/player/app.js +11 -7
  152. package/dist/player/editor.js +3 -2
  153. package/dist/player/editorEngine.js +3 -2
  154. package/dist/player/embed.js +2 -0
  155. package/dist/player/types.d.ts +6 -0
  156. package/dist/runtime/builtins/assert/checks.d.ts +18 -0
  157. package/dist/runtime/builtins/assert/checks.js +207 -0
  158. package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
  159. package/dist/runtime/builtins/assert/definitions.js +54 -0
  160. package/dist/runtime/builtins/assert/index.d.ts +152 -0
  161. package/dist/runtime/builtins/assert/index.js +36 -0
  162. package/dist/runtime/builtins/image/contactSheet.d.ts +3 -165
  163. package/dist/runtime/builtins/image/contactSheet.js +0 -49
  164. package/dist/runtime/builtins/image/definitions.d.ts +950 -0
  165. package/dist/runtime/builtins/image/definitions.js +343 -0
  166. package/dist/runtime/builtins/image/file.d.ts +8 -0
  167. package/dist/runtime/builtins/image/file.js +37 -0
  168. package/dist/runtime/builtins/image/filters.d.ts +8 -0
  169. package/dist/runtime/builtins/image/filters.js +394 -0
  170. package/dist/runtime/builtins/image/generators.d.ts +7 -0
  171. package/dist/runtime/builtins/image/generators.js +144 -0
  172. package/dist/runtime/builtins/image/index.d.ts +947 -4
  173. package/dist/runtime/builtins/image/index.js +41 -6
  174. package/dist/runtime/builtins/image/io.d.ts +100 -0
  175. package/dist/runtime/builtins/image/io.js +142 -0
  176. package/dist/runtime/builtins/image/planar.d.ts +55 -0
  177. package/dist/runtime/builtins/image/planar.js +274 -0
  178. package/dist/runtime/builtins/image/simplex.d.ts +22 -0
  179. package/dist/runtime/builtins/image/simplex.js +92 -0
  180. package/dist/runtime/builtins/image/text.d.ts +4 -0
  181. package/dist/runtime/builtins/image/text.js +141 -0
  182. package/dist/runtime/builtins/index.d.ts +3 -0
  183. package/dist/runtime/builtins/index.js +7 -0
  184. package/dist/runtime/definition/architecture.js +89 -0
  185. package/dist/runtime/gpu.d.ts +25 -0
  186. package/dist/runtime/gpu.js +40 -0
  187. package/dist/runtime/runtime.d.ts +2 -0
  188. package/dist/runtime/runtime.js +268 -44
  189. package/dist/runtime/types.d.ts +30 -0
  190. package/doc/CLI_REFERENCE.md +21 -3
  191. package/doc/EDITOR_EXPORT.md +26 -0
  192. package/doc/MCP.md +2 -1
  193. package/doc/NODE_REFERENCE.md +286 -14
  194. package/doc/PROJECT_AUTHORING.md +158 -4
  195. package/doc/WEB_PLAYER.md +9 -1
  196. package/package.json +4 -1
  197. package/dist/assets/CodePanel-BU-autmq.js +0 -2
  198. package/dist/assets/CustomNodeDialog-BRpDfOV5.js +0 -1
  199. package/dist/assets/OpenGraphDialog-BwbRlC0X.js +0 -1
  200. package/dist/assets/ProjectSettings-DLOXLxcA.js +0 -2
  201. package/dist/assets/definitions-GreEMXLV.js +0 -1
  202. package/dist/assets/geo-u0n_jOsD.js +0 -1
  203. package/dist/assets/image-cAa-Nu9O.js +0 -2062
  204. package/dist/assets/index-5ZtDAuy2.js +0 -220
  205. package/dist/assets/index-7vae6YFC.css +0 -1
  206. package/dist/assets/math-DioIQOPJ.js +0 -1
  207. package/dist/assets/pop-DC_TQG9t.js +0 -1
  208. package/dist/cli/chunk-36P5K3SD.js +0 -631
  209. package/dist/cli/chunk-4UDFYF5G.js +0 -132
  210. package/dist/cli/chunk-53NMBJAO.js +0 -35
  211. package/dist/cli/chunk-MIIEFUGG.js +0 -623
  212. package/dist/cli/chunk-QBBKRNGY.js +0 -90
  213. package/dist/cli/chunk-WPUDGEFY.js +0 -20
  214. package/dist/cli/editorCommands-UIF5YLUZ.js +0 -33
  215. package/dist/cli/project-K3QIPGBB.js +0 -12
  216. package/dist/cli/registerStandardNodes-S2VLMF6J.js +0 -3081
  217. package/dist/cli/runner-RA6UKEPD.js +0 -37
  218. package/dist/cli/webBuild-GT4PZT5D.js +0 -37
  219. package/dist/runtime/builtins/headless.d.ts +0 -19
  220. package/dist/runtime/builtins/headless.js +0 -25
@@ -0,0 +1,394 @@
1
+ import { output, readPlanar } from "./io.js";
2
+ import { Planar, resample, resizeToFit, resolveOutputSize } from "./planar.js";
3
+ /**
4
+ * `cascade.image.Blur`, `NormalMap`, `Resize`, `Transform` and `Composite`:
5
+ * the class library's pixel loops, unchanged. An unwired input leaves the
6
+ * output unset, as the class nodes did.
7
+ */
8
+ // ---------------------------------------------------------------- Blur
9
+ /** Box radii for an n-pass approximation of a Gaussian of this sigma. */
10
+ function boxesForGauss(sigma, n) {
11
+ const wIdeal = Math.sqrt((12 * sigma * sigma) / n + 1);
12
+ let wl = Math.floor(wIdeal);
13
+ if (wl % 2 === 0)
14
+ wl--;
15
+ const wu = wl + 2;
16
+ const mIdeal = (12 * sigma * sigma - n * wl * wl - 4 * n * wl - 3 * n) / (-4 * wl - 4);
17
+ const m = Math.round(mIdeal);
18
+ const sizes = [];
19
+ for (let i = 0; i < n; i++)
20
+ sizes.push(i < m ? wl : wu);
21
+ return sizes.map((s) => (s - 1) / 2);
22
+ }
23
+ /** One box pass, horizontal then vertical, sliding sums: O(1) a pixel. */
24
+ function boxBlurPass(data, w, h, r, wrap) {
25
+ if (r < 1)
26
+ return;
27
+ const temp = new Float32Array(w * h);
28
+ const ri = Math.floor(r);
29
+ const invDiameter = 1 / (ri * 2 + 1);
30
+ for (let y = 0; y < h; y++) {
31
+ const row = y * w;
32
+ let sum = 0;
33
+ for (let i = -ri; i <= ri; i++) {
34
+ const px = wrap ? ((i % w) + w) % w : Math.max(0, Math.min(w - 1, i));
35
+ sum += data[row + px];
36
+ }
37
+ temp[row] = sum * invDiameter;
38
+ for (let x = 1; x < w; x++) {
39
+ const removeX = wrap ? (((x - ri - 1) % w) + w) % w : Math.max(0, x - ri - 1);
40
+ const addX = wrap ? (((x + ri) % w) + w) % w : Math.min(w - 1, x + ri);
41
+ sum -= data[row + removeX];
42
+ sum += data[row + addX];
43
+ temp[row + x] = sum * invDiameter;
44
+ }
45
+ }
46
+ for (let x = 0; x < w; x++) {
47
+ let sum = 0;
48
+ for (let i = -ri; i <= ri; i++) {
49
+ const py = wrap ? ((i % h) + h) % h : Math.max(0, Math.min(h - 1, i));
50
+ sum += temp[py * w + x];
51
+ }
52
+ data[x] = sum * invDiameter;
53
+ for (let y = 1; y < h; y++) {
54
+ const removeY = wrap ? (((y - ri - 1) % h) + h) % h : Math.max(0, y - ri - 1);
55
+ const addY = wrap ? (((y + ri) % h) + h) % h : Math.min(h - 1, y + ri);
56
+ sum -= temp[removeY * w + x];
57
+ sum += temp[addY * w + x];
58
+ data[y * w + x] = sum * invDiameter;
59
+ }
60
+ }
61
+ }
62
+ function boxBlur(source, radius, wrapEdges) {
63
+ if (radius <= 0)
64
+ return source.clone();
65
+ const boxes = boxesForGauss(radius, 3);
66
+ const result = source.emptyClone();
67
+ for (let c = 0; c < source.channelCount; c++) {
68
+ const channel = new Float32Array(source.channels[c]);
69
+ for (const box of boxes)
70
+ boxBlurPass(channel, source.width, source.height, box, wrapEdges);
71
+ result.channels[c].set(channel);
72
+ }
73
+ return result;
74
+ }
75
+ function gaussianBlur(source, radius, wrapEdges) {
76
+ if (radius <= 0)
77
+ return source.clone();
78
+ const { width, height } = source;
79
+ const result = source.emptyClone();
80
+ const kernelSize = Math.ceil(radius * 3) * 2 + 1;
81
+ const kernel = new Float32Array(kernelSize);
82
+ const sigma = radius / 3;
83
+ const twoSigmaSq = 2 * sigma * sigma;
84
+ const half = (kernelSize - 1) >> 1;
85
+ let sum = 0;
86
+ for (let i = 0; i < kernelSize; i++) {
87
+ const x = i - half;
88
+ const value = Math.exp(-(x * x) / twoSigmaSq);
89
+ kernel[i] = value;
90
+ sum += value;
91
+ }
92
+ const invSum = 1 / sum;
93
+ for (let i = 0; i < kernelSize; i++)
94
+ kernel[i] *= invSum;
95
+ const wrapX = wrapEdges
96
+ ? (coord) => ((coord % width) + width) % width
97
+ : (coord) => (coord < 0 ? 0 : coord >= width ? width - 1 : coord);
98
+ const wrapY = wrapEdges
99
+ ? (coord) => ((coord % height) + height) % height
100
+ : (coord) => (coord < 0 ? 0 : coord >= height ? height - 1 : coord);
101
+ for (let c = 0; c < source.channelCount; c++) {
102
+ const src = source.channels[c];
103
+ const dst = result.channels[c];
104
+ const temp = new Float32Array(width * height);
105
+ for (let y = 0; y < height; y++) {
106
+ const row = y * width;
107
+ for (let x = 0; x < width; x++) {
108
+ let acc = 0;
109
+ for (let k = 0; k < kernelSize; k++)
110
+ acc += src[row + wrapX(x + k - half)] * kernel[k];
111
+ temp[row + x] = acc;
112
+ }
113
+ }
114
+ for (let x = 0; x < width; x++) {
115
+ for (let y = 0; y < height; y++) {
116
+ let acc = 0;
117
+ for (let k = 0; k < kernelSize; k++)
118
+ acc += temp[wrapY(y + k - half) * width + x] * kernel[k];
119
+ dst[y * width + x] = acc;
120
+ }
121
+ }
122
+ }
123
+ return result;
124
+ }
125
+ export async function executeBlur(context) {
126
+ const source = await readPlanar(context, context.inputs.image);
127
+ if (!source)
128
+ return;
129
+ const { method, radius, wrapEdges } = context.props;
130
+ await output(context, method === "gaussian" ? gaussianBlur(source, radius, wrapEdges) : boxBlur(source, radius, wrapEdges));
131
+ }
132
+ // ---------------------------------------------------------------- NormalMap
133
+ export async function executeNormalMap(context) {
134
+ const source = await readPlanar(context, context.inputs.image);
135
+ if (!source)
136
+ return;
137
+ const { scale, flipX, flipY } = context.props;
138
+ const { width, height } = source;
139
+ const result = Planar.rgba(width, height);
140
+ const heightMap = source.channels[0];
141
+ const [rOut, gOut, bOut, aOut] = result.channels;
142
+ for (let y = 0; y < height; y++) {
143
+ for (let x = 0; x < width; x++) {
144
+ const idx = y * width + x;
145
+ const h00 = heightMap[idx];
146
+ const h10 = heightMap[Math.min(x + 1, width - 1) + y * width];
147
+ const h01 = heightMap[x + Math.min(y + 1, height - 1) * width];
148
+ const dx = (h10 - h00) * scale;
149
+ const dy = (h01 - h00) * scale;
150
+ // normalize(-dx, -dy, 1), mapped from [-1, 1] to [0, 1].
151
+ const nx = -(flipX ? -dx : dx);
152
+ const ny = -(flipY ? -dy : dy);
153
+ const nz = 1.0;
154
+ const length = Math.sqrt(nx * nx + ny * ny + nz * nz);
155
+ rOut[idx] = (nx / length) * 0.5 + 0.5;
156
+ gOut[idx] = (ny / length) * 0.5 + 0.5;
157
+ bOut[idx] = (nz / length) * 0.5 + 0.5;
158
+ aOut[idx] = 1.0;
159
+ }
160
+ }
161
+ await output(context, result);
162
+ }
163
+ // ---------------------------------------------------------------- Resize
164
+ export async function executeResize(context) {
165
+ const source = await readPlanar(context, context.inputs.image);
166
+ if (!source)
167
+ return;
168
+ const { props } = context;
169
+ const scaled = props.mode === "scale";
170
+ const width = scaled ? Math.round(source.width * props.scale) : props.width;
171
+ const height = scaled ? Math.round(source.height * props.scale) : props.height;
172
+ await output(context, resample(source, width, height));
173
+ }
174
+ // ---------------------------------------------------------------- Transform
175
+ function pivotPoint(pivot, custom, width, height) {
176
+ switch (pivot) {
177
+ case "top-left": return [0, 0];
178
+ case "top-right": return [width, 0];
179
+ case "bottom-left": return [0, height];
180
+ case "bottom-right": return [width, height];
181
+ case "custom": return [custom[0] * width, custom[1] * height];
182
+ default: return [width / 2, height / 2];
183
+ }
184
+ }
185
+ function wrapCoord(coord, size, mode) {
186
+ if (mode === "clamp")
187
+ return Math.max(0, Math.min(size - 1, coord));
188
+ if (mode === "repeat") {
189
+ coord = coord % size;
190
+ if (coord < 0)
191
+ coord += size;
192
+ return coord;
193
+ }
194
+ if (mode === "mirror") {
195
+ const period = size * 2;
196
+ coord = Math.abs(coord % period);
197
+ if (coord >= size)
198
+ coord = period - coord - 1;
199
+ return Math.max(0, Math.min(size - 1, coord));
200
+ }
201
+ return coord; // transparent: checked before sampling
202
+ }
203
+ export async function executeTransform(context) {
204
+ const input = await readPlanar(context, context.inputs.image);
205
+ if (!input)
206
+ return;
207
+ const { props } = context;
208
+ const { width, height } = input;
209
+ const [tx, ty] = props.translate;
210
+ const rotation = (props.rotation * Math.PI) / 180;
211
+ const sx = props.scale[0];
212
+ const sy = props.uniformScale ? sx : props.scale[1];
213
+ const [pivotX, pivotY] = pivotPoint(props.pivot, props.customPivot, width, height);
214
+ const cosR = Math.cos(-rotation);
215
+ const sinR = Math.sin(-rotation);
216
+ const invSx = 1 / sx;
217
+ const invSy = 1 / sy;
218
+ const buffer = Planar.rgba(width, height);
219
+ const channelCount = Math.min(input.channelCount, 4);
220
+ const nearest = props.filter === "nearest";
221
+ const wrap = props.wrap;
222
+ for (let y = 0; y < height; y++) {
223
+ for (let x = 0; x < width; x++) {
224
+ const idx = y * width + x;
225
+ // Destination to source: about the pivot, rotated and scaled back.
226
+ const dx = x - pivotX - tx;
227
+ const dy = y - pivotY - ty;
228
+ const srcX = (dx * cosR - dy * sinR) * invSx + pivotX;
229
+ const srcY = (dx * sinR + dy * cosR) * invSy + pivotY;
230
+ if (wrap === "transparent" && (srcX < 0 || srcX >= width || srcY < 0 || srcY >= height)) {
231
+ for (let c = 0; c < 4; c++)
232
+ buffer.channels[c][idx] = 0;
233
+ continue;
234
+ }
235
+ if (nearest) {
236
+ const srcIdx = wrapCoord(Math.round(srcY), height, wrap) * width + wrapCoord(Math.round(srcX), width, wrap);
237
+ for (let c = 0; c < channelCount; c++)
238
+ buffer.channels[c][idx] = input.channels[c][srcIdx];
239
+ }
240
+ else {
241
+ const x0 = Math.floor(srcX);
242
+ const y0 = Math.floor(srcY);
243
+ const fx = srcX - x0;
244
+ const fy = srcY - y0;
245
+ const wx0 = wrapCoord(x0, width, wrap);
246
+ const wx1 = wrapCoord(x0 + 1, width, wrap);
247
+ const wy0 = wrapCoord(y0, height, wrap);
248
+ const wy1 = wrapCoord(y0 + 1, height, wrap);
249
+ for (let c = 0; c < channelCount; c++) {
250
+ const channel = input.channels[c];
251
+ const v00 = channel[wy0 * width + wx0];
252
+ const v10 = channel[wy0 * width + wx1];
253
+ const v01 = channel[wy1 * width + wx0];
254
+ const v11 = channel[wy1 * width + wx1];
255
+ const v0 = v00 + (v10 - v00) * fx;
256
+ const v1 = v01 + (v11 - v01) * fx;
257
+ buffer.channels[c][idx] = v0 + (v1 - v0) * fy;
258
+ }
259
+ }
260
+ if (channelCount < 4)
261
+ buffer.channels[3][idx] = 1;
262
+ }
263
+ }
264
+ await output(context, buffer);
265
+ }
266
+ // ---------------------------------------------------------------- Composite
267
+ function blend(base, layer, mode, opacity) {
268
+ const { width, height } = base;
269
+ const result = Planar.rgba(width, height);
270
+ const [r1, g1, b1, a1] = (base.channelCount >= 4 ? base : base.toRGBA()).channels;
271
+ const [r2, g2, b2, a2] = (layer.channelCount >= 4 ? layer : layer.toRGBA()).channels;
272
+ const [rOut, gOut, bOut, aOut] = result.channels;
273
+ for (let i = 0; i < width * height; i++) {
274
+ const baseR = r1[i], baseG = g1[i], baseB = b1[i], baseA = a1[i];
275
+ const blendR = r2[i], blendG = g2[i], blendB = b2[i], blendA = a2[i];
276
+ let r, g, b;
277
+ switch (mode) {
278
+ case "normal":
279
+ r = blendR;
280
+ g = blendG;
281
+ b = blendB;
282
+ break;
283
+ case "multiply":
284
+ r = baseR * blendR;
285
+ g = baseG * blendG;
286
+ b = baseB * blendB;
287
+ break;
288
+ case "screen":
289
+ r = 1 - (1 - baseR) * (1 - blendR);
290
+ g = 1 - (1 - baseG) * (1 - blendG);
291
+ b = 1 - (1 - baseB) * (1 - blendB);
292
+ break;
293
+ case "overlay":
294
+ r = baseR < 0.5 ? 2 * baseR * blendR : 1 - 2 * (1 - baseR) * (1 - blendR);
295
+ g = baseG < 0.5 ? 2 * baseG * blendG : 1 - 2 * (1 - baseG) * (1 - blendG);
296
+ b = baseB < 0.5 ? 2 * baseB * blendB : 1 - 2 * (1 - baseB) * (1 - blendB);
297
+ break;
298
+ case "darken":
299
+ r = Math.min(baseR, blendR);
300
+ g = Math.min(baseG, blendG);
301
+ b = Math.min(baseB, blendB);
302
+ break;
303
+ case "lighten":
304
+ r = Math.max(baseR, blendR);
305
+ g = Math.max(baseG, blendG);
306
+ b = Math.max(baseB, blendB);
307
+ break;
308
+ case "color-dodge":
309
+ r = blendR === 1 ? 1 : Math.min(1, baseR / (1 - blendR));
310
+ g = blendG === 1 ? 1 : Math.min(1, baseG / (1 - blendG));
311
+ b = blendB === 1 ? 1 : Math.min(1, baseB / (1 - blendB));
312
+ break;
313
+ case "color-burn":
314
+ r = blendR === 0 ? 0 : Math.max(0, 1 - (1 - baseR) / blendR);
315
+ g = blendG === 0 ? 0 : Math.max(0, 1 - (1 - baseG) / blendG);
316
+ b = blendB === 0 ? 0 : Math.max(0, 1 - (1 - baseB) / blendB);
317
+ break;
318
+ case "hard-light":
319
+ r = blendR < 0.5 ? 2 * baseR * blendR : 1 - 2 * (1 - baseR) * (1 - blendR);
320
+ g = blendG < 0.5 ? 2 * baseG * blendG : 1 - 2 * (1 - baseG) * (1 - blendG);
321
+ b = blendB < 0.5 ? 2 * baseB * blendB : 1 - 2 * (1 - baseB) * (1 - blendB);
322
+ break;
323
+ case "soft-light":
324
+ r = blendR < 0.5 ? baseR - (1 - 2 * blendR) * baseR * (1 - baseR) : baseR + (2 * blendR - 1) * (Math.sqrt(baseR) - baseR);
325
+ g = blendG < 0.5 ? baseG - (1 - 2 * blendG) * baseG * (1 - baseG) : baseG + (2 * blendG - 1) * (Math.sqrt(baseG) - baseG);
326
+ b = blendB < 0.5 ? baseB - (1 - 2 * blendB) * baseB * (1 - baseB) : baseB + (2 * blendB - 1) * (Math.sqrt(baseB) - baseB);
327
+ break;
328
+ case "difference":
329
+ r = Math.abs(baseR - blendR);
330
+ g = Math.abs(baseG - blendG);
331
+ b = Math.abs(baseB - blendB);
332
+ break;
333
+ case "exclusion":
334
+ r = baseR + blendR - 2 * baseR * blendR;
335
+ g = baseG + blendG - 2 * baseG * blendG;
336
+ b = baseB + blendB - 2 * baseB * blendB;
337
+ break;
338
+ case "add":
339
+ case "linear-dodge":
340
+ r = Math.min(1, baseR + blendR);
341
+ g = Math.min(1, baseG + blendG);
342
+ b = Math.min(1, baseB + blendB);
343
+ break;
344
+ case "subtract":
345
+ r = Math.max(0, baseR - blendR);
346
+ g = Math.max(0, baseG - blendG);
347
+ b = Math.max(0, baseB - blendB);
348
+ break;
349
+ case "divide":
350
+ r = blendR === 0 ? 1 : Math.min(1, baseR / blendR);
351
+ g = blendG === 0 ? 1 : Math.min(1, baseG / blendG);
352
+ b = blendB === 0 ? 1 : Math.min(1, baseB / blendB);
353
+ break;
354
+ case "pin-light":
355
+ r = blendR < 0.5 ? Math.min(baseR, 2 * blendR) : Math.max(baseR, 2 * (blendR - 0.5));
356
+ g = blendG < 0.5 ? Math.min(baseG, 2 * blendG) : Math.max(baseG, 2 * (blendG - 0.5));
357
+ b = blendB < 0.5 ? Math.min(baseB, 2 * blendB) : Math.max(baseB, 2 * (blendB - 0.5));
358
+ break;
359
+ case "vivid-light":
360
+ r = blendR < 0.5 ? (blendR === 0 ? 0 : 1 - (1 - baseR) / (2 * blendR)) : (blendR === 1 ? 1 : baseR / (2 * (1 - blendR)));
361
+ g = blendG < 0.5 ? (blendG === 0 ? 0 : 1 - (1 - baseG) / (2 * blendG)) : (blendG === 1 ? 1 : baseG / (2 * (1 - blendG)));
362
+ b = blendB < 0.5 ? (blendB === 0 ? 0 : 1 - (1 - baseB) / (2 * blendB)) : (blendB === 1 ? 1 : baseB / (2 * (1 - blendB)));
363
+ break;
364
+ case "linear-burn":
365
+ r = Math.max(0, baseR + blendR - 1);
366
+ g = Math.max(0, baseG + blendG - 1);
367
+ b = Math.max(0, baseB + blendB - 1);
368
+ break;
369
+ default:
370
+ r = blendR;
371
+ g = blendG;
372
+ b = blendB;
373
+ }
374
+ rOut[i] = baseR + (r - baseR) * opacity * blendA;
375
+ gOut[i] = baseG + (g - baseG) * opacity * blendA;
376
+ bOut[i] = baseB + (b - baseB) * opacity * blendA;
377
+ aOut[i] = baseA + (blendA - baseA) * opacity;
378
+ }
379
+ return result;
380
+ }
381
+ export async function executeComposite(context) {
382
+ const { props } = context;
383
+ const inputs = [await readPlanar(context, context.inputs.image1), await readPlanar(context, context.inputs.image2)];
384
+ const { width, height } = resolveOutputSize(props.outputResolution, inputs, props.customResolution);
385
+ const [base, layer] = inputs.map((buffer) => {
386
+ if (!buffer || (buffer.width === width && buffer.height === height))
387
+ return buffer;
388
+ return resizeToFit(buffer, width, height, props.fitMode);
389
+ });
390
+ if (!base || !layer)
391
+ return;
392
+ await output(context, blend(base, layer, props.blendMode, props.opacity));
393
+ }
394
+ //# sourceMappingURL=filters.js.map
@@ -0,0 +1,7 @@
1
+ import type { NodeExecutionContext } from "@field/cascade/contracts";
2
+ import type { checkersDefinition, colorDefinition, noiseDefinition, rampDefinition } from "./definitions.js";
3
+ export declare function executeColor(context: NodeExecutionContext<typeof colorDefinition>): Promise<void>;
4
+ export declare function executeCheckers(context: NodeExecutionContext<typeof checkersDefinition>): Promise<void>;
5
+ export declare function executeNoise(context: NodeExecutionContext<typeof noiseDefinition>): Promise<void>;
6
+ export declare function executeRamp(context: NodeExecutionContext<typeof rampDefinition>): Promise<void>;
7
+ //# sourceMappingURL=generators.d.ts.map
@@ -0,0 +1,144 @@
1
+ import { normalizeColor, output } from "./io.js";
2
+ import { Planar } from "./planar.js";
3
+ import { createNoise2D } from "./simplex.js";
4
+ /** `cascade.image.Color`, `Checkers`, `Noise` and `Ramp`: images from props alone. */
5
+ function size(resolution) {
6
+ return [resolution[0], resolution[1]];
7
+ }
8
+ export async function executeColor(context) {
9
+ const [width, height] = size(context.props.resolution);
10
+ await output(context, Planar.solid(width, height, normalizeColor(context.props.color)));
11
+ }
12
+ export async function executeCheckers(context) {
13
+ const { props } = context;
14
+ const [width, height] = size(props.resolution);
15
+ const color1 = normalizeColor(props.color1);
16
+ const color2 = normalizeColor(props.color2);
17
+ const checkerSize = props.mode === "size" ? props.size : width / props.divisions;
18
+ let offsetX = 0;
19
+ let offsetY = 0;
20
+ if (props.centered) {
21
+ // The centre of the image is the centre of a cell.
22
+ offsetX = (width / 2) % checkerSize - checkerSize / 2;
23
+ offsetY = (height / 2) % checkerSize - checkerSize / 2;
24
+ }
25
+ const buffer = Planar.rgba(width, height);
26
+ const [r, g, b, a] = buffer.channels;
27
+ for (let y = 0; y < height; y++) {
28
+ for (let x = 0; x < width; x++) {
29
+ const idx = y * width + x;
30
+ const even = (Math.floor((x - offsetX) / checkerSize) + Math.floor((y - offsetY) / checkerSize)) % 2 === 0;
31
+ const color = even ? color1 : color2;
32
+ r[idx] = color.r;
33
+ g[idx] = color.g;
34
+ b[idx] = color.b;
35
+ a[idx] = color.a;
36
+ }
37
+ }
38
+ await output(context, buffer);
39
+ }
40
+ export async function executeNoise(context) {
41
+ const { props } = context;
42
+ const [width, height] = size(props.resolution);
43
+ // The class node's LCG, so a seed draws the noise it always drew.
44
+ let rng = props.seed;
45
+ const noise2D = createNoise2D(() => {
46
+ rng = (rng * 9301 + 49297) % 233280;
47
+ return rng / 233280;
48
+ });
49
+ const buffer = Planar.grayscale(width, height);
50
+ const gray = buffer.channels[0];
51
+ for (let y = 0; y < height; y++) {
52
+ for (let x = 0; x < width; x++) {
53
+ let value = 0;
54
+ let amplitude = 1;
55
+ let frequency = props.scale;
56
+ let maxValue = 0;
57
+ for (let i = 0; i < props.iterations; i++) {
58
+ value += noise2D(x * frequency, y * frequency) * amplitude;
59
+ maxValue += amplitude;
60
+ amplitude *= 0.5;
61
+ frequency *= 2;
62
+ }
63
+ gray[y * width + x] = (value / maxValue + 1) * 0.5;
64
+ }
65
+ }
66
+ await output(context, buffer);
67
+ }
68
+ function interpolateColor(color1, color2, t, interpolation) {
69
+ if (interpolation === "constant")
70
+ return t < 0.5 ? color1 : color2;
71
+ if (interpolation === "smooth")
72
+ t = t * t * (3 - 2 * t);
73
+ return {
74
+ r: color1.r + (color2.r - color1.r) * t,
75
+ g: color1.g + (color2.g - color1.g) * t,
76
+ b: color1.b + (color2.b - color1.b) * t,
77
+ a: (color1.a ?? 1) + ((color2.a ?? 1) - (color1.a ?? 1)) * t,
78
+ };
79
+ }
80
+ function colorAt(points, position) {
81
+ const sorted = [...points].sort((a, b) => a.position - b.position);
82
+ position = Math.max(0, Math.min(1, position));
83
+ let before = null;
84
+ let after = null;
85
+ for (const point of sorted) {
86
+ if (point.position <= position)
87
+ before = point;
88
+ if (point.position >= position && !after) {
89
+ after = point;
90
+ break;
91
+ }
92
+ }
93
+ if (!before && after)
94
+ return normalizeColor(after.color);
95
+ if (before && !after)
96
+ return normalizeColor(before.color);
97
+ if (!before || !after)
98
+ return { r: 0, g: 0, b: 0, a: 1 };
99
+ if (before.position === position)
100
+ return normalizeColor(before.color);
101
+ if (after.position === position)
102
+ return normalizeColor(after.color);
103
+ const t = (position - before.position) / (after.position - before.position);
104
+ return interpolateColor(normalizeColor(before.color), normalizeColor(after.color), t, after.interpolation || "linear");
105
+ }
106
+ export async function executeRamp(context) {
107
+ const { props } = context;
108
+ const [width, height] = size(props.resolution);
109
+ const points = (props.points ?? []);
110
+ if (points.length === 0)
111
+ return;
112
+ const buffer = Planar.rgba(width, height);
113
+ const [r, g, b, a] = buffer.channels;
114
+ const centerX = width / 2;
115
+ const centerY = height / 2;
116
+ const maxDist = Math.sqrt(centerX * centerX + centerY * centerY);
117
+ for (let y = 0; y < height; y++) {
118
+ for (let x = 0; x < width; x++) {
119
+ let position = 0;
120
+ if (props.type === "horizontal") {
121
+ position = x / width;
122
+ }
123
+ else if (props.type === "vertical") {
124
+ position = y / height;
125
+ }
126
+ else if (props.type === "radial") {
127
+ position = (Math.atan2(y - centerY, x - centerX) + Math.PI) / (2 * Math.PI);
128
+ }
129
+ else if (props.type === "concentric") {
130
+ const dx = x - centerX;
131
+ const dy = y - centerY;
132
+ position = Math.sqrt(dx * dx + dy * dy) / maxDist;
133
+ }
134
+ const color = colorAt(points, position);
135
+ const idx = y * width + x;
136
+ r[idx] = color.r;
137
+ g[idx] = color.g;
138
+ b[idx] = color.b;
139
+ a[idx] = color.a ?? 1;
140
+ }
141
+ }
142
+ await output(context, buffer);
143
+ }
144
+ //# sourceMappingURL=generators.js.map