@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.
- package/AGENTS.md +8 -5
- package/ARCHITECTURE.md +5 -4
- package/CHANGELOG.md +245 -0
- package/README.md +1 -1
- package/dist/assets/AgentPanel-1JugFjE4.css +1 -0
- package/dist/assets/AgentPanel-BDL5MddA.js +5 -0
- package/dist/assets/{CodeEditor-4O7FQ__R.js → CodeEditor-C-xYG-Wn.js} +120 -120
- package/dist/assets/CodePanel-CtXd50m6.js +2 -0
- package/dist/assets/CookInfoPanel-B3NMhKJF.css +1 -0
- package/dist/assets/CookInfoPanel-DG4_TRsX.js +2 -0
- package/dist/assets/CustomNodeDialog-CHfZUs-r.js +1 -0
- package/dist/assets/DefinitionNode-CaRTXQ_b.js +1 -0
- package/dist/assets/DefinitionPanel-C3CxZY9r.js +1 -0
- package/dist/assets/DefinitionPanel-VNLnAiQD.css +1 -0
- package/dist/assets/OpenGraphDialog-BLdr6k_q.js +1 -0
- package/dist/assets/ProjectSettings-ZnvmIAir.js +2 -0
- package/dist/assets/{SettingsDialog-Bz8JxISH.js → SettingsDialog-CBX3Wa87.js} +1 -1
- package/dist/assets/TimelinePanel-CDeR0arY.css +1 -0
- package/dist/assets/TimelinePanel-CWgnFH1e.js +1 -0
- package/dist/assets/{VersionHistoryDialog-D5Z0nJ5-.js → VersionHistoryDialog-BOSoW1R-.js} +1 -1
- package/dist/assets/assert-BO2_erPD.js +1 -0
- package/dist/assets/assert-hvykGCnP.js +1 -0
- package/dist/assets/builder-BmBKcDix.js +1 -0
- package/dist/assets/camera-DFF7YA3F.js +1 -0
- package/dist/assets/checks-DAvyO9Gf.js +1 -0
- package/dist/assets/contactSheet-N1NcaWxw.js +2 -0
- package/dist/assets/{core-Yqi0Ni61.js → core-DB-MkGjj.js} +3 -4
- package/dist/assets/coreTypes-DtnDATZ7.js +1 -0
- package/dist/assets/{cssMode-CxUzc4vp.js → cssMode-DPVckSOc.js} +1 -1
- package/dist/assets/definitions-B227e7f_.js +1 -0
- package/dist/assets/definitions-D0WOUJd7.js +1 -0
- package/dist/assets/{editor.api-BNFb25CX.js → editor.api-Dkutg_Z1.js} +1 -1
- package/dist/assets/expressions-X-Z-GrgW.js +5 -0
- package/dist/assets/file-DAL6IfjV.js +1 -0
- package/dist/assets/filters-D3k1LRMa.js +1 -0
- package/dist/assets/{freemarker2-0ZDnWxeM.js → freemarker2-CLiLKk3B.js} +1 -1
- package/dist/assets/generators-BhDxRAFC.js +1 -0
- package/dist/assets/geo-BsMImJIJ.js +1 -0
- package/dist/assets/geo-C4YngDob.js +2 -0
- package/dist/assets/{handlebars-Cy18MoaD.js → handlebars-CcD3aMeL.js} +1 -1
- package/dist/assets/{html-B-2Dx7nq.js → html-DvZWGfqb.js} +1 -1
- package/dist/assets/{htmlMode-DUAc9B_d.js → htmlMode-DUD3VaoN.js} +1 -1
- package/dist/assets/image-0SfH-snh.js +1 -0
- package/dist/assets/image-Ck-RdGU4.js +2 -0
- package/dist/assets/index-BWnrmIB9.js +137 -0
- package/dist/assets/index-DIaxEONI.css +1 -0
- package/dist/assets/io-BGmt2g0D.js +1 -0
- package/dist/assets/{javascript-DeQsDqP5.js → javascript-Cap2ahgh.js} +1 -1
- package/dist/assets/{jsonMode-BiFwJVSk.js → jsonMode-B0uLjp9C.js} +1 -1
- package/dist/assets/{liquid-BLoHH68a.js → liquid-CnxwhfLn.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-CNFgm1Kv.js → lspLanguageFeatures-Dn9hlKX6.js} +1 -1
- package/dist/assets/math-GeULS-sk.js +1 -0
- package/dist/assets/{mdx-CFXQVNfs.js → mdx-BV3jbrVa.js} +1 -1
- package/dist/assets/pop-KMF_qloi.js +1 -0
- package/dist/assets/preload-helper-Czpn1I53.js +1 -0
- package/dist/assets/{python-Cf7S7p2E.js → python-CGyFk4XK.js} +1 -1
- package/dist/assets/{razor-CMHBcG-g.js → razor-CQfzX3-N.js} +1 -1
- package/dist/assets/remap-5SjXQU-q.js +1 -0
- package/dist/assets/renderMarkdown-ork0Oir7.js +67 -0
- package/dist/assets/studioImages-CAyH-0Pl.js +1 -0
- package/dist/assets/text-ZpX7qNkx.js +2 -0
- package/dist/assets/time-P_OiBhcf.js +1 -0
- package/dist/assets/{tsMode-BEih0GDj.js → tsMode-BOeXzO9H.js} +1 -1
- package/dist/assets/{typescript-COEKYD_a.js → typescript-J1SnE_Tu.js} +1 -1
- package/dist/assets/{workers-BNhAhMNn.js → workers-CYwwTiVF.js} +1 -1
- package/dist/assets/{xml-CPiMpnPl.js → xml-DnPRNzK9.js} +1 -1
- package/dist/assets/{yaml-Bgp1ITLR.js → yaml-BmmYBNa9.js} +1 -1
- package/dist/cli/DefinitionNode-P7AA4TAU.js +15 -0
- package/dist/cli/{Graph-ZJJTRQTM.js → Graph-OB6EWSGL.js} +12 -12
- package/dist/cli/assert-GROGQNCQ.js +25 -0
- package/dist/cli/checks-3WB44XBL.js +189 -0
- package/dist/cli/{chunk-3JTJHF7V.js → chunk-2L34ERS2.js} +4 -4
- package/dist/cli/chunk-53LGFPWX.js +138 -0
- package/dist/cli/chunk-5ORUQ6ES.js +267 -0
- package/dist/cli/{chunk-YAT5Y25U.js → chunk-7QE2ZNSN.js} +3 -3
- package/dist/cli/{chunk-SGJAET2L.js → chunk-BPODRH52.js} +8 -4
- package/dist/cli/{chunk-XWDSKG7N.js → chunk-BZ63XQ3I.js} +2 -2
- package/dist/cli/{chunk-FCDXJ6EK.js → chunk-CTPLRSWF.js} +2 -13
- package/dist/cli/{chunk-WIH4TEIS.js → chunk-EZRWJ4G2.js} +1 -1
- package/dist/cli/{chunk-BIJO5OFS.js → chunk-FWMSGKEH.js} +1 -1
- package/dist/cli/{chunk-POMMNKZZ.js → chunk-G23A2BZZ.js} +38 -5
- package/dist/cli/{chunk-35D343DC.js → chunk-GRIN2DDP.js} +19 -1
- package/dist/cli/{chunk-WKURDVYP.js → chunk-JEW5MPGI.js} +28 -331
- package/dist/cli/chunk-JVOCK5W4.js +83 -0
- package/dist/cli/chunk-KBGUO27Q.js +15 -0
- package/dist/cli/{chunk-GFS6W3C5.js → chunk-L6B6FUQP.js} +54 -32
- package/dist/cli/chunk-LTXWBXOM.js +438 -0
- package/dist/cli/chunk-MI5JLQYA.js +220 -0
- package/dist/cli/{chunk-A4OAMOZH.js → chunk-NJFMDXHR.js} +13 -3
- package/dist/cli/{chunk-DUIUFVU2.js → chunk-OBZAP3GG.js} +49 -23
- package/dist/cli/chunk-QAGATJGE.js +336 -0
- package/dist/cli/{chunk-EUIAC3U3.js → chunk-QKL7CCRS.js} +966 -132
- package/dist/cli/{chunk-RLCAJBVR.js → chunk-TJW67QSI.js} +8 -7
- package/dist/cli/{chunk-RQZ4SX5R.js → chunk-W5PJ5RMP.js} +156 -84
- package/dist/cli/chunk-W7OUAT7J.js +122 -0
- package/dist/cli/chunk-WGKPJZDY.js +364 -0
- package/dist/cli/{chunk-D6BUOHUG.js → chunk-X4WKAOQP.js} +4 -4
- package/dist/cli/{chunk-VVGSLVSF.js → chunk-X566FWBV.js} +6 -1
- package/dist/cli/chunk-YH637PDL.js +317 -0
- package/dist/cli/chunk-ZSCUZE6I.js +112 -0
- package/dist/cli/{cliCommands-UQSFGEHF.js → cliCommands-XVFRD7CF.js} +23 -17
- package/dist/cli/{commands-Q23M26QM.js → commands-M2XLTPTZ.js} +129 -47
- package/dist/cli/contactSheet-2PQVRUFW.js +11 -0
- package/dist/cli/{definitions-HF2FTHYU.js → definitions-BPOPW52L.js} +7 -7
- package/dist/cli/{doctorCommand-B7BPQMN6.js → doctorCommand-RAEI7GVY.js} +36 -6
- package/dist/cli/{edit-JHCPTQFT.js → edit-S5KEQ5RA.js} +3 -3
- package/dist/cli/editorCommands-5BBG4S6V.js +41 -0
- package/dist/cli/{encode-HAUZTYPT.js → encode-ICPEPMH5.js} +2 -2
- package/dist/cli/file-3HTQHPYQ.js +41 -0
- package/dist/cli/filters-6ODFWYK4.js +379 -0
- package/dist/cli/generators-YU7GB4MD.js +210 -0
- package/dist/cli/{geo-4RNK3QVN.js → geo-6ZPGKZRU.js} +7 -7
- package/dist/cli/{graphCommands-6BMAKGQE.js → graphCommands-MULQ7VD2.js} +4 -4
- package/dist/cli/{graphFile-VPSQ6OF7.js → graphFile-CITF36ZM.js} +25 -19
- package/dist/cli/{headlessCanvas-5XLDZT7M.js → headlessCanvas-ENRHC7GS.js} +3 -2
- package/dist/cli/{helpCommand-3VRE3KVO.js → helpCommand-X2QDANM3.js} +9 -7
- package/dist/cli/image-TXELZZK5.js +55 -0
- package/dist/cli/index.js +14 -14
- package/dist/cli/{math-ZRMLKU2D.js → math-RSYRQ3C6.js} +5 -5
- package/dist/cli/{mcp-ZLEEMYGF.js → mcp-YRIGRLYQ.js} +4 -4
- package/dist/cli/{parity-KINARCP2.js → parity-3N3HTKWX.js} +42 -36
- package/dist/cli/{pop-USDFXB7H.js → pop-B7SIIYKN.js} +5 -5
- package/dist/cli/project-SJF5ZF3F.js +12 -0
- package/dist/cli/{projectDefinition-DMTZN2NE.js → projectDefinition-F3D7UIHJ.js} +8 -8
- package/dist/cli/{projectRuntime-Z5PJOH47.js → projectRuntime-NY4XMDFH.js} +19 -12
- package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
- package/dist/cli/{render-YURAPPUI.js → render-Y23W6MY6.js} +29 -22
- package/dist/cli/{runCommand-PI764LEG.js → runCommand-XOG7AVC5.js} +3 -3
- package/dist/cli/runner-DNJO5WO2.js +43 -0
- package/dist/cli/runtime/image.ts +27 -2
- package/dist/cli/runtime/io.ts +78 -0
- package/dist/cli/share-B7HTCOJX.js +166 -0
- package/dist/cli/stageCache-TY2BFZJR.js +15 -0
- package/dist/cli/{stageRunner-KUNIMQKJ.js → stageRunner-KY2EJP7X.js} +2 -2
- package/dist/cli/text-MB3EKR4H.js +126 -0
- package/dist/cli/{verbs-RCE3K4VQ.js → verbs-PI2JH5FI.js} +2 -2
- package/dist/cli/webBuild-2K4COCGY.js +44 -0
- package/dist/contracts/capabilities.d.ts +39 -1
- package/dist/contracts/color.d.ts +8 -0
- package/dist/contracts/color.js +23 -0
- package/dist/contracts/definition.d.ts +10 -0
- package/dist/contracts/schema.d.ts +1 -1
- package/dist/contracts/schema.js +1 -0
- package/dist/contracts/validate.js +11 -1
- package/dist/contracts/values.d.ts +24 -0
- package/dist/image.d.ts +15 -0
- package/dist/image.js +36 -2
- package/dist/index.html +8 -2
- package/dist/io.d.ts +29 -0
- package/dist/io.js +40 -0
- package/dist/player/app.js +11 -7
- package/dist/player/editor.js +3 -2
- package/dist/player/editorEngine.js +3 -2
- package/dist/player/embed.js +2 -0
- package/dist/player/types.d.ts +6 -0
- package/dist/runtime/builtins/assert/checks.d.ts +18 -0
- package/dist/runtime/builtins/assert/checks.js +207 -0
- package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
- package/dist/runtime/builtins/assert/definitions.js +54 -0
- package/dist/runtime/builtins/assert/index.d.ts +152 -0
- package/dist/runtime/builtins/assert/index.js +36 -0
- package/dist/runtime/builtins/image/contactSheet.d.ts +3 -165
- package/dist/runtime/builtins/image/contactSheet.js +0 -49
- package/dist/runtime/builtins/image/definitions.d.ts +950 -0
- package/dist/runtime/builtins/image/definitions.js +343 -0
- package/dist/runtime/builtins/image/file.d.ts +8 -0
- package/dist/runtime/builtins/image/file.js +37 -0
- package/dist/runtime/builtins/image/filters.d.ts +8 -0
- package/dist/runtime/builtins/image/filters.js +394 -0
- package/dist/runtime/builtins/image/generators.d.ts +7 -0
- package/dist/runtime/builtins/image/generators.js +144 -0
- package/dist/runtime/builtins/image/index.d.ts +947 -4
- package/dist/runtime/builtins/image/index.js +41 -6
- package/dist/runtime/builtins/image/io.d.ts +100 -0
- package/dist/runtime/builtins/image/io.js +142 -0
- package/dist/runtime/builtins/image/planar.d.ts +55 -0
- package/dist/runtime/builtins/image/planar.js +274 -0
- package/dist/runtime/builtins/image/simplex.d.ts +22 -0
- package/dist/runtime/builtins/image/simplex.js +92 -0
- package/dist/runtime/builtins/image/text.d.ts +4 -0
- package/dist/runtime/builtins/image/text.js +141 -0
- package/dist/runtime/builtins/index.d.ts +3 -0
- package/dist/runtime/builtins/index.js +7 -0
- package/dist/runtime/definition/architecture.js +89 -0
- package/dist/runtime/gpu.d.ts +25 -0
- package/dist/runtime/gpu.js +40 -0
- package/dist/runtime/runtime.d.ts +2 -0
- package/dist/runtime/runtime.js +268 -44
- package/dist/runtime/types.d.ts +30 -0
- package/doc/CLI_REFERENCE.md +21 -3
- package/doc/EDITOR_EXPORT.md +26 -0
- package/doc/MCP.md +2 -1
- package/doc/NODE_REFERENCE.md +286 -14
- package/doc/PROJECT_AUTHORING.md +158 -4
- package/doc/WEB_PLAYER.md +9 -1
- package/package.json +4 -1
- package/dist/assets/CodePanel-BU-autmq.js +0 -2
- package/dist/assets/CustomNodeDialog-BRpDfOV5.js +0 -1
- package/dist/assets/OpenGraphDialog-BwbRlC0X.js +0 -1
- package/dist/assets/ProjectSettings-DLOXLxcA.js +0 -2
- package/dist/assets/definitions-GreEMXLV.js +0 -1
- package/dist/assets/geo-u0n_jOsD.js +0 -1
- package/dist/assets/image-cAa-Nu9O.js +0 -2062
- package/dist/assets/index-5ZtDAuy2.js +0 -220
- package/dist/assets/index-7vae6YFC.css +0 -1
- package/dist/assets/math-DioIQOPJ.js +0 -1
- package/dist/assets/pop-DC_TQG9t.js +0 -1
- package/dist/cli/chunk-36P5K3SD.js +0 -631
- package/dist/cli/chunk-4UDFYF5G.js +0 -132
- package/dist/cli/chunk-53NMBJAO.js +0 -35
- package/dist/cli/chunk-MIIEFUGG.js +0 -623
- package/dist/cli/chunk-QBBKRNGY.js +0 -90
- package/dist/cli/chunk-WPUDGEFY.js +0 -20
- package/dist/cli/editorCommands-UIF5YLUZ.js +0 -33
- package/dist/cli/project-K3QIPGBB.js +0 -12
- package/dist/cli/registerStandardNodes-S2VLMF6J.js +0 -3081
- package/dist/cli/runner-RA6UKEPD.js +0 -37
- package/dist/cli/webBuild-GT4PZT5D.js +0 -37
- package/dist/runtime/builtins/headless.d.ts +0 -19
- package/dist/runtime/builtins/headless.js +0 -25
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 2D simplex noise: `createNoise2D` and `buildPermutationTable` from
|
|
3
|
+
* simplex-noise 4.0.3 by Jonas Wagner (MIT), copied rather than depended on.
|
|
4
|
+
*
|
|
5
|
+
* The class `Noise` node loaded simplex-noise@latest from esm.sh at cook time,
|
|
6
|
+
* so it needed a network and could not run headless. The runtime depends on
|
|
7
|
+
* contracts alone, so the two functions it used live here, unchanged, and a
|
|
8
|
+
* seeded noise image is the same pixels it always was.
|
|
9
|
+
*
|
|
10
|
+
* Copyright (c) 2024 Jonas Wagner. Permission is hereby granted, free of
|
|
11
|
+
* charge, to any person obtaining a copy of this software and associated
|
|
12
|
+
* documentation files (the "Software"), to deal in the Software without
|
|
13
|
+
* restriction, including without limitation the rights to use, copy, modify,
|
|
14
|
+
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
|
15
|
+
* and to permit persons to whom the Software is furnished to do so, subject to
|
|
16
|
+
* the following conditions: The above copyright notice and this permission
|
|
17
|
+
* notice shall be included in all copies or substantial portions of the
|
|
18
|
+
* Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
|
19
|
+
*/
|
|
20
|
+
const SQRT3 = Math.sqrt(3.0);
|
|
21
|
+
const F2 = 0.5 * (SQRT3 - 1.0);
|
|
22
|
+
const G2 = (3.0 - SQRT3) / 6.0;
|
|
23
|
+
const fastFloor = (x) => Math.floor(x) | 0;
|
|
24
|
+
const grad2 = new Float64Array([1, 1, -1, 1, 1, -1, -1, -1, 1, 0, -1, 0, 1, 0, -1, 0, 0, 1, 0, -1, 0, 1, 0, -1]);
|
|
25
|
+
function buildPermutationTable(random) {
|
|
26
|
+
const tableSize = 512;
|
|
27
|
+
const p = new Uint8Array(tableSize);
|
|
28
|
+
for (let i = 0; i < tableSize / 2; i++)
|
|
29
|
+
p[i] = i;
|
|
30
|
+
for (let i = 0; i < tableSize / 2 - 1; i++) {
|
|
31
|
+
const r = i + ~~(random() * (256 - i));
|
|
32
|
+
const aux = p[i];
|
|
33
|
+
p[i] = p[r];
|
|
34
|
+
p[r] = aux;
|
|
35
|
+
}
|
|
36
|
+
for (let i = 256; i < tableSize; i++)
|
|
37
|
+
p[i] = p[i - 256];
|
|
38
|
+
return p;
|
|
39
|
+
}
|
|
40
|
+
export function createNoise2D(random) {
|
|
41
|
+
const perm = buildPermutationTable(random);
|
|
42
|
+
const permGrad2x = new Float64Array(perm).map((v) => grad2[(v % 12) * 2]);
|
|
43
|
+
const permGrad2y = new Float64Array(perm).map((v) => grad2[(v % 12) * 2 + 1]);
|
|
44
|
+
return function noise2D(x, y) {
|
|
45
|
+
let n0 = 0;
|
|
46
|
+
let n1 = 0;
|
|
47
|
+
let n2 = 0;
|
|
48
|
+
const s = (x + y) * F2;
|
|
49
|
+
const i = fastFloor(x + s);
|
|
50
|
+
const j = fastFloor(y + s);
|
|
51
|
+
const t = (i + j) * G2;
|
|
52
|
+
const X0 = i - t;
|
|
53
|
+
const Y0 = j - t;
|
|
54
|
+
const x0 = x - X0;
|
|
55
|
+
const y0 = y - Y0;
|
|
56
|
+
let i1, j1;
|
|
57
|
+
if (x0 > y0) {
|
|
58
|
+
i1 = 1;
|
|
59
|
+
j1 = 0;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
i1 = 0;
|
|
63
|
+
j1 = 1;
|
|
64
|
+
}
|
|
65
|
+
const x1 = x0 - i1 + G2;
|
|
66
|
+
const y1 = y0 - j1 + G2;
|
|
67
|
+
const x2 = x0 - 1.0 + 2.0 * G2;
|
|
68
|
+
const y2 = y0 - 1.0 + 2.0 * G2;
|
|
69
|
+
const ii = i & 255;
|
|
70
|
+
const jj = j & 255;
|
|
71
|
+
let t0 = 0.5 - x0 * x0 - y0 * y0;
|
|
72
|
+
if (t0 >= 0) {
|
|
73
|
+
const gi0 = ii + perm[jj];
|
|
74
|
+
t0 *= t0;
|
|
75
|
+
n0 = t0 * t0 * (permGrad2x[gi0] * x0 + permGrad2y[gi0] * y0);
|
|
76
|
+
}
|
|
77
|
+
let t1 = 0.5 - x1 * x1 - y1 * y1;
|
|
78
|
+
if (t1 >= 0) {
|
|
79
|
+
const gi1 = ii + i1 + perm[jj + j1];
|
|
80
|
+
t1 *= t1;
|
|
81
|
+
n1 = t1 * t1 * (permGrad2x[gi1] * x1 + permGrad2y[gi1] * y1);
|
|
82
|
+
}
|
|
83
|
+
let t2 = 0.5 - x2 * x2 - y2 * y2;
|
|
84
|
+
if (t2 >= 0) {
|
|
85
|
+
const gi2 = ii + 1 + perm[jj + 1];
|
|
86
|
+
t2 *= t2;
|
|
87
|
+
n2 = t2 * t2 * (permGrad2x[gi2] * x2 + permGrad2y[gi2] * y2);
|
|
88
|
+
}
|
|
89
|
+
return 70.0 * (n0 + n1 + n2);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=simplex.js.map
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { colorToCss, normalizeColor, readPixelsFromCanvas, surface, writePixels } from "./io.js";
|
|
2
|
+
/**
|
|
3
|
+
* `cascade.image.Text`: lines of text on a canvas, aligned to an anchor, with
|
|
4
|
+
* tracking, rotation and opacity.
|
|
5
|
+
*
|
|
6
|
+
* Fonts follow `cascade/image`'s rules. `font` is a CSS family list and draws
|
|
7
|
+
* with whatever faces the realm has (the page's or worker's in a browser,
|
|
8
|
+
* Skia's system fonts headless), so the same family only matches across hosts
|
|
9
|
+
* when both have the same face. A project font file (`.otf`, `.ttf`, `.woff`,
|
|
10
|
+
* `.woff2`) is read through `assets` and registered with the realm, which
|
|
11
|
+
* makes it match everywhere; a file that is not there falls back to
|
|
12
|
+
* `sans-serif` rather than failing, since a licensed face an export does not
|
|
13
|
+
* ship should still draw.
|
|
14
|
+
*/
|
|
15
|
+
const FONT_FILE = /\.(otf|ttf|woff2?)$/i;
|
|
16
|
+
const HOST_KEY = "__cascadeImageHost";
|
|
17
|
+
// One registration per realm and font file contents; see `registerFont` in
|
|
18
|
+
// cascade/image, which this mirrors for a runtime that cannot import it.
|
|
19
|
+
const registered = new Set();
|
|
20
|
+
function fingerprint(bytes) {
|
|
21
|
+
let hash = 0x811c9dc5;
|
|
22
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
23
|
+
hash ^= bytes[index];
|
|
24
|
+
hash = Math.imul(hash, 0x01000193);
|
|
25
|
+
}
|
|
26
|
+
return `${bytes.length}-${(hash >>> 0).toString(16)}`;
|
|
27
|
+
}
|
|
28
|
+
/** The family to draw with: `font` itself, or a registered face for a font file. */
|
|
29
|
+
async function fontFamily(context, font) {
|
|
30
|
+
const path = font.trim();
|
|
31
|
+
if (!FONT_FILE.test(path))
|
|
32
|
+
return font;
|
|
33
|
+
let bytes;
|
|
34
|
+
try {
|
|
35
|
+
const assets = context.capabilities.assets;
|
|
36
|
+
if (!assets)
|
|
37
|
+
throw new Error("no assets");
|
|
38
|
+
bytes = await assets.read({ path }, { signal: context.signal });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return "sans-serif";
|
|
42
|
+
}
|
|
43
|
+
const family = `cascade-font-${fingerprint(bytes)}`;
|
|
44
|
+
const globals = globalThis;
|
|
45
|
+
const host = globals[HOST_KEY];
|
|
46
|
+
const fonts = host ? null : globals.fonts ?? globals.document?.fonts;
|
|
47
|
+
const key = `${host ? `headless:${host.renderer}` : "browser"}:${family}`;
|
|
48
|
+
if (registered.has(key))
|
|
49
|
+
return family;
|
|
50
|
+
if (host && typeof host.registerFont === "function") {
|
|
51
|
+
await host.registerFont(family, bytes, {});
|
|
52
|
+
}
|
|
53
|
+
else if (typeof globals.FontFace === "function" && fonts) {
|
|
54
|
+
const face = new globals.FontFace(family, bytes.slice().buffer);
|
|
55
|
+
fonts.add(await face.load());
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
return "sans-serif";
|
|
59
|
+
}
|
|
60
|
+
registered.add(key);
|
|
61
|
+
return family;
|
|
62
|
+
}
|
|
63
|
+
export async function executeText(context) {
|
|
64
|
+
const { props } = context;
|
|
65
|
+
const [width, height] = [props.resolution[0], props.resolution[1]];
|
|
66
|
+
const color = normalizeColor(props.color);
|
|
67
|
+
const background = normalizeColor(props.backgroundColor);
|
|
68
|
+
const [offsetX, offsetY] = props.offset;
|
|
69
|
+
const { fontSize, alignH, alignV, rotation, letterSpacing } = props;
|
|
70
|
+
const family = await fontFamily(context, props.font);
|
|
71
|
+
const { canvas, context: ctx } = surface(width, height);
|
|
72
|
+
if (background.a > 0) {
|
|
73
|
+
ctx.fillStyle = colorToCss(background);
|
|
74
|
+
ctx.fillRect(0, 0, width, height);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
ctx.clearRect(0, 0, width, height);
|
|
78
|
+
}
|
|
79
|
+
ctx.save();
|
|
80
|
+
ctx.globalAlpha = props.opacity;
|
|
81
|
+
ctx.font = `${props.fontStyle} ${props.fontWeight} ${fontSize}px ${family}`;
|
|
82
|
+
ctx.fillStyle = colorToCss(color);
|
|
83
|
+
const lines = props.text.split("\n");
|
|
84
|
+
const lineHeightPx = fontSize * props.lineHeight;
|
|
85
|
+
const totalTextHeight = lines.length * lineHeightPx;
|
|
86
|
+
let anchorX;
|
|
87
|
+
let anchorY;
|
|
88
|
+
switch (alignH) {
|
|
89
|
+
case "left":
|
|
90
|
+
ctx.textAlign = "left";
|
|
91
|
+
anchorX = 0;
|
|
92
|
+
break;
|
|
93
|
+
case "right":
|
|
94
|
+
ctx.textAlign = "right";
|
|
95
|
+
anchorX = width;
|
|
96
|
+
break;
|
|
97
|
+
default:
|
|
98
|
+
ctx.textAlign = "center";
|
|
99
|
+
anchorX = width / 2;
|
|
100
|
+
}
|
|
101
|
+
switch (alignV) {
|
|
102
|
+
case "top":
|
|
103
|
+
anchorY = fontSize;
|
|
104
|
+
break;
|
|
105
|
+
case "bottom":
|
|
106
|
+
anchorY = height - totalTextHeight + fontSize;
|
|
107
|
+
break;
|
|
108
|
+
default:
|
|
109
|
+
anchorY = (height - totalTextHeight) / 2 + fontSize;
|
|
110
|
+
}
|
|
111
|
+
anchorX += offsetX;
|
|
112
|
+
anchorY += offsetY;
|
|
113
|
+
if (rotation !== 0) {
|
|
114
|
+
const pivotY = anchorY - fontSize / 2 + totalTextHeight / 2;
|
|
115
|
+
ctx.translate(anchorX, pivotY);
|
|
116
|
+
ctx.rotate((rotation * Math.PI) / 180);
|
|
117
|
+
ctx.translate(-anchorX, -pivotY);
|
|
118
|
+
}
|
|
119
|
+
if (letterSpacing !== 0) {
|
|
120
|
+
// Tracking: one glyph at a time.
|
|
121
|
+
ctx.textAlign = "left";
|
|
122
|
+
lines.forEach((line, index) => {
|
|
123
|
+
const y = anchorY + index * lineHeightPx;
|
|
124
|
+
let lineWidth = 0;
|
|
125
|
+
for (const char of line)
|
|
126
|
+
lineWidth += ctx.measureText(char).width + letterSpacing;
|
|
127
|
+
lineWidth -= letterSpacing;
|
|
128
|
+
let x = alignH === "left" ? anchorX : alignH === "right" ? anchorX - lineWidth : anchorX - lineWidth / 2;
|
|
129
|
+
for (const char of line) {
|
|
130
|
+
ctx.fillText(char, x, y);
|
|
131
|
+
x += ctx.measureText(char).width + letterSpacing;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
lines.forEach((line, index) => ctx.fillText(line, anchorX, anchorY + index * lineHeightPx));
|
|
137
|
+
}
|
|
138
|
+
ctx.restore();
|
|
139
|
+
context.outputs.image.set(await writePixels(context, readPixelsFromCanvas(canvas)));
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -6,6 +6,9 @@ import type { DefinitionNodeRegistration } from "../types.js";
|
|
|
6
6
|
* set — which speaks the same `geometry` type as `geo`, so any of its outputs
|
|
7
7
|
* fits any geometry node's input. `cascade.scene.*` is the assembly: geometry
|
|
8
8
|
* plus a viewpoint plus lights, which is what the Viewer's 3D mode renders.
|
|
9
|
+
* `cascade.image.*` makes and filters images, and `cascade.assert.*` fails a
|
|
10
|
+
* run whose result is wrong. Those two load their bodies lazily: this list is
|
|
11
|
+
* in Studio's first-paint bundle.
|
|
9
12
|
*/
|
|
10
13
|
export declare const builtinNodeRegistrations: readonly DefinitionNodeRegistration[];
|
|
11
14
|
export declare function builtinNodeRegistration(moduleId: string): DefinitionNodeRegistration | undefined;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { assertNodeRegistrations } from "./assert/index.js";
|
|
1
2
|
import { coreNodeRegistrations } from "./core/index.js";
|
|
2
3
|
import { geoNodeRegistrations } from "./geo/index.js";
|
|
4
|
+
import { imageNodeRegistrations } from "./image/index.js";
|
|
3
5
|
import { mathNodeRegistrations } from "./math/index.js";
|
|
4
6
|
import { popNodeRegistrations } from "./pop/index.js";
|
|
5
7
|
import { sceneNodeRegistrations } from "./scene/index.js";
|
|
@@ -10,6 +12,9 @@ import { sceneNodeRegistrations } from "./scene/index.js";
|
|
|
10
12
|
* set — which speaks the same `geometry` type as `geo`, so any of its outputs
|
|
11
13
|
* fits any geometry node's input. `cascade.scene.*` is the assembly: geometry
|
|
12
14
|
* plus a viewpoint plus lights, which is what the Viewer's 3D mode renders.
|
|
15
|
+
* `cascade.image.*` makes and filters images, and `cascade.assert.*` fails a
|
|
16
|
+
* run whose result is wrong. Those two load their bodies lazily: this list is
|
|
17
|
+
* in Studio's first-paint bundle.
|
|
13
18
|
*/
|
|
14
19
|
export const builtinNodeRegistrations = Object.freeze([
|
|
15
20
|
...coreNodeRegistrations,
|
|
@@ -17,6 +22,8 @@ export const builtinNodeRegistrations = Object.freeze([
|
|
|
17
22
|
...mathNodeRegistrations,
|
|
18
23
|
...popNodeRegistrations,
|
|
19
24
|
...sceneNodeRegistrations,
|
|
25
|
+
...imageNodeRegistrations,
|
|
26
|
+
...assertNodeRegistrations,
|
|
20
27
|
]);
|
|
21
28
|
const registrationsById = new Map(builtinNodeRegistrations.map((registration) => [
|
|
22
29
|
registration.moduleId,
|
|
@@ -18,6 +18,15 @@ export function validateNodeModuleArchitecture(source, fileName, ts) {
|
|
|
18
18
|
const diagnostics = [];
|
|
19
19
|
const exportsDefinition = file.statements.some((statement) => ts.isVariableStatement(statement) && isDefinition(statement));
|
|
20
20
|
for (const statement of file.statements) {
|
|
21
|
+
if (ts.isVariableStatement(statement) &&
|
|
22
|
+
!isDefinition(statement) &&
|
|
23
|
+
!supersededByDefinition(statement) &&
|
|
24
|
+
isPureConstant(statement)) {
|
|
25
|
+
// A helper: `const TAU = Math.PI * 2`, `const PALETTE = [...] as const`,
|
|
26
|
+
// `const lerp = (a, b, t) => a + (b - a) * t`. Immutable, so it cannot
|
|
27
|
+
// carry anything from one cook to the next.
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
21
30
|
if (ts.isVariableStatement(statement) && !isDefinition(statement)) {
|
|
22
31
|
// `icon` and `runsOn` get their own sentence, because the general
|
|
23
32
|
// message sends you looking for the wrong thing. A dynamic node declares
|
|
@@ -97,6 +106,86 @@ export function validateNodeModuleArchitecture(source, fileName, ts) {
|
|
|
97
106
|
? declaration.name.text
|
|
98
107
|
: null;
|
|
99
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Whether a module-level declaration is an immutable helper rather than
|
|
111
|
+
* state: `const` only, and every initializer pure (see `isPure`).
|
|
112
|
+
*/
|
|
113
|
+
function isPureConstant(statement) {
|
|
114
|
+
if (!(statement.declarationList.flags & ts.NodeFlags.Const))
|
|
115
|
+
return false;
|
|
116
|
+
return statement.declarationList.declarations.every((declaration) => ts.isIdentifier(declaration.name) &&
|
|
117
|
+
declaration.initializer !== undefined &&
|
|
118
|
+
isPure(declaration.initializer, false));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* An expression that creates no mutable value: literals, arithmetic and
|
|
122
|
+
* logic over them, references and property reads (`Math.PI`), functions
|
|
123
|
+
* (they close over nothing mutable, since nothing at module level is), and
|
|
124
|
+
* array/object literals only when frozen — `as const` or `Object.freeze(…)`.
|
|
125
|
+
* Calls, `new`, regular expressions (`lastIndex` is state) and a bare
|
|
126
|
+
* array or object are not.
|
|
127
|
+
*/
|
|
128
|
+
function isPure(node, frozen) {
|
|
129
|
+
if (ts.isNumericLiteral(node) || ts.isStringLiteral(node) || ts.isBigIntLiteral(node) ||
|
|
130
|
+
ts.isNoSubstitutionTemplateLiteral(node) ||
|
|
131
|
+
node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword ||
|
|
132
|
+
node.kind === ts.SyntaxKind.NullKeyword)
|
|
133
|
+
return true;
|
|
134
|
+
if (ts.isIdentifier(node))
|
|
135
|
+
return true;
|
|
136
|
+
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node))
|
|
137
|
+
return true;
|
|
138
|
+
if (ts.isParenthesizedExpression(node))
|
|
139
|
+
return isPure(node.expression, frozen);
|
|
140
|
+
if (ts.isAsExpression(node))
|
|
141
|
+
return isPure(node.expression, frozen || isConstAssertion(node.type));
|
|
142
|
+
if (ts.isSatisfiesExpression(node) || ts.isNonNullExpression(node) || ts.isTypeAssertionExpression(node))
|
|
143
|
+
return isPure(node.expression, frozen);
|
|
144
|
+
if (ts.isPrefixUnaryExpression(node))
|
|
145
|
+
return node.operator !== ts.SyntaxKind.PlusPlusToken &&
|
|
146
|
+
node.operator !== ts.SyntaxKind.MinusMinusToken &&
|
|
147
|
+
isPure(node.operand, false);
|
|
148
|
+
if (ts.isBinaryExpression(node))
|
|
149
|
+
return !isAssignment(node.operatorToken.kind) &&
|
|
150
|
+
isPure(node.left, false) && isPure(node.right, false);
|
|
151
|
+
if (ts.isConditionalExpression(node))
|
|
152
|
+
return isPure(node.condition, false) && isPure(node.whenTrue, frozen) && isPure(node.whenFalse, frozen);
|
|
153
|
+
if (ts.isTemplateExpression(node))
|
|
154
|
+
return node.templateSpans.every((span) => isPure(span.expression, false));
|
|
155
|
+
if (ts.isPropertyAccessExpression(node))
|
|
156
|
+
return isPure(node.expression, false);
|
|
157
|
+
if (ts.isElementAccessExpression(node))
|
|
158
|
+
return isPure(node.expression, false) && isPure(node.argumentExpression, false);
|
|
159
|
+
if (ts.isArrayLiteralExpression(node))
|
|
160
|
+
return frozen && node.elements.every((element) => !ts.isSpreadElement(element) && isPure(element, true));
|
|
161
|
+
if (ts.isObjectLiteralExpression(node))
|
|
162
|
+
return frozen && node.properties.every((property) => ts.isPropertyAssignment(property)
|
|
163
|
+
? !ts.isComputedPropertyName(property.name) && isPure(property.initializer, true)
|
|
164
|
+
: ts.isShorthandPropertyAssignment(property));
|
|
165
|
+
if (ts.isCallExpression(node) &&
|
|
166
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
167
|
+
ts.isIdentifier(node.expression.expression) &&
|
|
168
|
+
node.expression.expression.text === "Object" &&
|
|
169
|
+
node.expression.name.text === "freeze" &&
|
|
170
|
+
node.arguments.length === 1) {
|
|
171
|
+
const [argument] = node.arguments;
|
|
172
|
+
// Object.freeze is shallow: a nested array or object needs its own freeze.
|
|
173
|
+
return ts.isArrayLiteralExpression(argument)
|
|
174
|
+
? argument.elements.every((element) => !ts.isSpreadElement(element) && isPure(element, false))
|
|
175
|
+
: ts.isObjectLiteralExpression(argument)
|
|
176
|
+
? argument.properties.every((property) => ts.isPropertyAssignment(property) &&
|
|
177
|
+
!ts.isComputedPropertyName(property.name) &&
|
|
178
|
+
isPure(property.initializer, false))
|
|
179
|
+
: false;
|
|
180
|
+
}
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
function isConstAssertion(type) {
|
|
184
|
+
return ts.isTypeReferenceNode(type) && ts.isIdentifier(type.typeName) && type.typeName.text === "const";
|
|
185
|
+
}
|
|
186
|
+
function isAssignment(kind) {
|
|
187
|
+
return kind >= ts.SyntaxKind.FirstAssignment && kind <= ts.SyntaxKind.LastAssignment;
|
|
188
|
+
}
|
|
100
189
|
function isDefinition(statement) {
|
|
101
190
|
return Boolean(statement.declarationList.flags & ts.NodeFlags.Const) &&
|
|
102
191
|
Boolean(statement.modifiers?.some((item) => item.kind === ts.SyntaxKind.ExportKeyword)) &&
|
package/dist/runtime/gpu.d.ts
CHANGED
|
@@ -28,6 +28,31 @@ export interface GpuHost extends GpuCapability {
|
|
|
28
28
|
}
|
|
29
29
|
export declare function isGpuHost(value: unknown): value is GpuHost;
|
|
30
30
|
export declare function createGpuHost(options: GpuHostOptions): GpuHost;
|
|
31
|
+
interface LimitedAdapter {
|
|
32
|
+
readonly limits: unknown;
|
|
33
|
+
requestDevice(descriptor?: {
|
|
34
|
+
requiredLimits?: Record<string, number>;
|
|
35
|
+
}): Promise<GpuCapability["device"]>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The host's one device, asked for with everything the adapter allows.
|
|
39
|
+
*
|
|
40
|
+
* A bare `requestDevice()` gets WebGPU's defaults (128 MiB per storage binding,
|
|
41
|
+
* 256 invocations, 16 KiB workgroup storage, 8 storage buffers per stage)
|
|
42
|
+
* whatever the hardware is, and the device is shared, so a node cannot ask for
|
|
43
|
+
* more later. Brand + Design chunked a ribbon into four accumulator buffers on
|
|
44
|
+
* an M3 Max for that reason (3 Nov 2026). Should an adapter refuse its own
|
|
45
|
+
* ceiling, the defaults are better than no device; a node that needs more
|
|
46
|
+
* says so with `gpuLimits` and gets {@link gpuLimitShortfall}'s message.
|
|
47
|
+
*/
|
|
48
|
+
export declare function requestDeviceWithAdapterLimits(adapter: LimitedAdapter): Promise<GpuCapability["device"]>;
|
|
49
|
+
/**
|
|
50
|
+
* What a node's declared `gpuLimits` asks for that this device does not give,
|
|
51
|
+
* as one sentence, or null when it all fits. `max*` limits must reach the
|
|
52
|
+
* value; `min*Alignment` limits must be at most it (smaller is better there).
|
|
53
|
+
*/
|
|
54
|
+
export declare function gpuLimitShortfall(label: string, required: Readonly<Record<string, number>> | undefined, gpu: Pick<GpuCapability, "limits" | "adapterInfo">): string | null;
|
|
31
55
|
/** Getter-backed supported-limits objects cannot be normalized with spread. */
|
|
32
56
|
export declare function readGpuLimits(source: unknown): Record<string, number>;
|
|
57
|
+
export {};
|
|
33
58
|
//# sourceMappingURL=gpu.d.ts.map
|
package/dist/runtime/gpu.js
CHANGED
|
@@ -196,6 +196,46 @@ export function createGpuHost(options) {
|
|
|
196
196
|
}
|
|
197
197
|
return host;
|
|
198
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* The host's one device, asked for with everything the adapter allows.
|
|
201
|
+
*
|
|
202
|
+
* A bare `requestDevice()` gets WebGPU's defaults (128 MiB per storage binding,
|
|
203
|
+
* 256 invocations, 16 KiB workgroup storage, 8 storage buffers per stage)
|
|
204
|
+
* whatever the hardware is, and the device is shared, so a node cannot ask for
|
|
205
|
+
* more later. Brand + Design chunked a ribbon into four accumulator buffers on
|
|
206
|
+
* an M3 Max for that reason (3 Nov 2026). Should an adapter refuse its own
|
|
207
|
+
* ceiling, the defaults are better than no device; a node that needs more
|
|
208
|
+
* says so with `gpuLimits` and gets {@link gpuLimitShortfall}'s message.
|
|
209
|
+
*/
|
|
210
|
+
export async function requestDeviceWithAdapterLimits(adapter) {
|
|
211
|
+
const requiredLimits = readGpuLimits(adapter.limits);
|
|
212
|
+
try {
|
|
213
|
+
return await adapter.requestDevice({ requiredLimits });
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return adapter.requestDevice();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* What a node's declared `gpuLimits` asks for that this device does not give,
|
|
221
|
+
* as one sentence, or null when it all fits. `max*` limits must reach the
|
|
222
|
+
* value; `min*Alignment` limits must be at most it (smaller is better there).
|
|
223
|
+
*/
|
|
224
|
+
export function gpuLimitShortfall(label, required, gpu) {
|
|
225
|
+
if (!required)
|
|
226
|
+
return null;
|
|
227
|
+
const short = [];
|
|
228
|
+
for (const [name, wanted] of Object.entries(required)) {
|
|
229
|
+
const have = gpu.limits[name];
|
|
230
|
+
const fits = have !== undefined && (name.startsWith("min") ? have <= wanted : have >= wanted);
|
|
231
|
+
if (!fits)
|
|
232
|
+
short.push(`${name} ${wanted} (this GPU: ${have ?? "not reported"})`);
|
|
233
|
+
}
|
|
234
|
+
if (!short.length)
|
|
235
|
+
return null;
|
|
236
|
+
const adapter = gpu.adapterInfo.description || gpu.adapterInfo.device || gpu.adapterInfo.vendor || "this adapter";
|
|
237
|
+
return `${label} needs GPU limits ${adapter} does not allow: ${short.join(", ")}.`;
|
|
238
|
+
}
|
|
199
239
|
/** Getter-backed supported-limits objects cannot be normalized with spread. */
|
|
200
240
|
export function readGpuLimits(source) {
|
|
201
241
|
if (!source || typeof source !== "object")
|
|
@@ -42,4 +42,6 @@ import type { CascadeRuntime, CreateRuntimeOptions } from "./types.js";
|
|
|
42
42
|
*/
|
|
43
43
|
export declare const PREFLIGHT_WARNING_CODES: ReadonlySet<string>;
|
|
44
44
|
export declare function createRuntime(options: CreateRuntimeOptions): CascadeRuntime;
|
|
45
|
+
/** How many plain arrays/objects the runtime has copied in this process. */
|
|
46
|
+
export declare function runtimeValueCopies(): number;
|
|
45
47
|
//# sourceMappingURL=runtime.d.ts.map
|