@mkbabb/glass-ui 8.0.0 → 10.0.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 (264) hide show
  1. package/MIGRATION.md +4768 -0
  2. package/README.md +57 -13
  3. package/dist/DialogContent-CwIBvou_.js +157 -0
  4. package/dist/{DropdownMenuTrigger-8UZsBtZ8.js → DropdownMenuTrigger-CKRnhDSQ.js} +1 -1
  5. package/dist/{Surface-i1tWC-Ga.js → Surface-1nPoabc3.js} +1 -1
  6. package/dist/{accent-tone-solve-Cw7WkRD9.js → accent-tone-solve-5H6FY2QO.js} +1 -1
  7. package/dist/aurora.js +386 -355
  8. package/dist/{badge-owUOaaba.js → badge-BO6BC4G5.js} +1 -1
  9. package/dist/badge.js +1 -1
  10. package/dist/blob.js +44 -44
  11. package/dist/{button-yvGPb0Bx.js → button-BOFTUPyU.js} +1 -1
  12. package/dist/button.js +1 -1
  13. package/dist/{card-3HFFpC7_.js → card-ByyqRBvK.js} +2 -2
  14. package/dist/card.js +1 -1
  15. package/dist/carousel.js +3 -3
  16. package/dist/{checkbox-BnXuGgJ3.js → checkbox-B1qn65se.js} +1 -1
  17. package/dist/checkbox.js +1 -1
  18. package/dist/chip.js +8 -3
  19. package/dist/{class-names-q-UGOkHH.js → class-names-FlXcvRRT.js} +65 -18
  20. package/dist/{collapsible-B7O6m-Ch.js → collapsible-DFzK29Oh.js} +5 -2
  21. package/dist/collapsible.js +1 -1
  22. package/dist/color.glsl-DTJhdekG.js +34 -0
  23. package/dist/color.js +11 -11
  24. package/dist/color.wgsl-BTszLY39.js +398 -0
  25. package/dist/{command-BuBnYpxF.js → command-C-5xSEJ2.js} +8 -8
  26. package/dist/command.js +1 -1
  27. package/dist/component-styles.css +1 -0
  28. package/dist/components/_shared/class-names.d.ts +10 -1
  29. package/dist/components/_shared/control.d.ts +0 -1
  30. package/dist/components/_shared/index.d.ts +0 -1
  31. package/dist/components/_shared/menu/menu.css +1 -1
  32. package/dist/components/_shared/overlay/index.d.ts +1 -0
  33. package/dist/components/_shared/overlay/shortcuts.d.ts +12 -0
  34. package/dist/components/aurora/Aurora.vue.d.ts +1 -0
  35. package/dist/components/aurora/composables/atoms.d.ts +22 -6
  36. package/dist/components/aurora/composables/frameLoop.d.ts +6 -5
  37. package/dist/components/aurora/composables/runtime.d.ts +28 -1
  38. package/dist/components/aurora/composables/useAurora.d.ts +28 -0
  39. package/dist/components/aurora/composables/wgpuSetup.d.ts +1 -1
  40. package/dist/components/aurora/constants/presets.d.ts +62 -5
  41. package/dist/components/aurora/index.d.ts +1 -1
  42. package/dist/components/blob/Blob.vue.d.ts +3 -3
  43. package/dist/components/card/Card.vue.d.ts +8 -2
  44. package/dist/components/chip/Chip.vue.d.ts +1 -7
  45. package/dist/components/chip/chipVariants.d.ts +1 -0
  46. package/dist/components/collapsible/Collapsible.vue.d.ts +1 -0
  47. package/dist/components/command/CommandDialog.vue.d.ts +1 -1
  48. package/dist/components/configurator/Configurator.vue.d.ts +25 -2
  49. package/dist/components/configurator/ConfiguratorRow.vue.d.ts +4 -2
  50. package/dist/components/configurator/styles.css +1 -1
  51. package/dist/components/dark-mode-toggle/dark-mode-toggle.css +1 -1
  52. package/dist/components/deck/styles/capture.css +1 -1
  53. package/dist/components/dialog/styles.css +1 -1
  54. package/dist/components/dock/DockSeparator.vue.d.ts +2 -3
  55. package/dist/components/dock/GlassDock.vue.d.ts +1 -0
  56. package/dist/components/dock/composables/dockContext.d.ts +1 -8
  57. package/dist/components/dock/composables/dockCrossfadeContext.d.ts +0 -2
  58. package/dist/components/dock/composables/index.d.ts +1 -1
  59. package/dist/components/dock/composables/useDockHold.d.ts +16 -12
  60. package/dist/components/dock/composables/useDockRun.d.ts +20 -0
  61. package/dist/components/dock/composables/useDockSearch.d.ts +1 -1
  62. package/dist/components/dock/composables/useDockShellProps.d.ts +18 -101
  63. package/dist/components/dock/composables/useDockState.d.ts +2 -9
  64. package/dist/components/dock/constants.d.ts +15 -0
  65. package/dist/components/dock/index.d.ts +2 -2
  66. package/dist/components/dock/styles/controls/icon-button.css +1 -1
  67. package/dist/components/dock/styles/controls/tab-button.css +1 -1
  68. package/dist/components/dock/styles/controls/triggers.css +1 -1
  69. package/dist/components/dock/styles/controls.css +1 -1
  70. package/dist/components/dock/styles/density.css +1 -1
  71. package/dist/components/dock/styles/index.css +1 -1
  72. package/dist/components/dock/styles/layer-group.css +1 -1
  73. package/dist/components/dock/styles/layers.css +1 -1
  74. package/dist/components/dock/styles/morph.css +1 -1
  75. package/dist/components/dock/styles/run.css +1 -0
  76. package/dist/components/dock/styles/search.css +1 -1
  77. package/dist/components/dock/styles/shape.css +1 -1
  78. package/dist/components/dock/styles/shell-regions.css +1 -1
  79. package/dist/components/dock/styles/shell.css +1 -1
  80. package/dist/components/fourier-field/FourierField.vue.d.ts +29 -26
  81. package/dist/components/fourier-field/clock.d.ts +35 -0
  82. package/dist/components/fourier-field/constants.d.ts +80 -104
  83. package/dist/components/fourier-field/index.d.ts +3 -28
  84. package/dist/components/fourier-field/math.d.ts +69 -77
  85. package/dist/components/fourier-field/renderer/mint.d.ts +50 -0
  86. package/dist/components/fourier-field/renderer/uniforms.d.ts +84 -0
  87. package/dist/components/fourier-field/renderer/wgpu.d.ts +28 -0
  88. package/dist/components/fourier-field/shaders/compute.wgsl.d.ts +1 -0
  89. package/dist/components/fourier-field/shaders/render.wgsl.d.ts +1 -0
  90. package/dist/components/fourier-field/useFourierField.d.ts +49 -0
  91. package/dist/components/handmark/HandMark.vue.d.ts +18 -14
  92. package/dist/components/handmark/index.d.ts +4 -23
  93. package/dist/components/handmark/stroke.d.ts +92 -0
  94. package/dist/components/labeled-field/LabeledSwitch.vue.d.ts +3 -1
  95. package/dist/components/labeled-field/types.d.ts +1 -1
  96. package/dist/components/menu/DropdownMenu.vue.d.ts +1 -1
  97. package/dist/components/metric/coalesce-metric.d.ts +6 -1
  98. package/dist/components/metric/styles.css +1 -1
  99. package/dist/components/popover/Popover.vue.d.ts +1 -1
  100. package/dist/components/scroll-progress-rim/styles.css +1 -1
  101. package/dist/components/sheet/styles.css +1 -1
  102. package/dist/components/slider/styles.css +1 -1
  103. package/dist/components/tabs/SegmentedTabs.vue.d.ts +25 -112
  104. package/dist/components/tabs/composables/useTabDragMorph.d.ts +1 -1
  105. package/dist/components/tabs/composables/useTabResponsive.d.ts +11 -11
  106. package/dist/components/tabs/index.d.ts +1 -1
  107. package/dist/components/tabs/styles/drag.css +1 -1
  108. package/dist/components/tabs/styles/segmented.css +1 -1
  109. package/dist/components/tabs/types.d.ts +85 -0
  110. package/dist/components/timeline/index.d.ts +2 -0
  111. package/dist/components/toast/Toast.vue.d.ts +2 -2
  112. package/dist/composables/color/index.d.ts +0 -15
  113. package/dist/composables/dark/darkModeSyncScript.d.ts +42 -0
  114. package/dist/composables/dom/useResolveTokenColor.d.ts +1 -1
  115. package/dist/composables/glass/index.d.ts +2 -0
  116. package/dist/composables/keyboard/useKeyboardShortcuts.d.ts +47 -1
  117. package/dist/composables/motion/pointer/pointerFieldMappings.d.ts +0 -41
  118. package/dist/{components/search/composables → composables/search}/index.d.ts +2 -2
  119. package/dist/{components/search/composables/fuzzySearchIndex.d.ts → composables/search/match.d.ts} +8 -3
  120. package/dist/composables/sidebar/types.d.ts +5 -1
  121. package/dist/configurator-DiwVN573.js +339 -0
  122. package/dist/configurator.js +1 -1
  123. package/dist/constellation.js +5 -5
  124. package/dist/{content-CWO-XL7o.js → content-So3j6SOe.js} +1 -1
  125. package/dist/control-DJp15L4U.js +7 -0
  126. package/dist/dark-mode-toggle.js +2 -2
  127. package/dist/{dark-z_P5QwqI.js → dark-pONY3x93.js} +2 -2
  128. package/dist/dark.js +1 -1
  129. package/dist/{data-table-_RrOEdiw.js → data-table-Ba_uTfPc.js} +3 -3
  130. package/dist/data-table.js +1 -1
  131. package/dist/{deck-Ci6YjB5e.js → deck-Cm_VmyNL.js} +1 -1
  132. package/dist/deck.js +1 -1
  133. package/dist/{dialog-CbRnTRtg.js → dialog-BZoFG0j8.js} +1 -1
  134. package/dist/dialog.js +2 -2
  135. package/dist/dock.js +718 -496
  136. package/dist/dom.js +90 -8
  137. package/dist/easing.js +5 -5
  138. package/dist/expandable-container-CZUj9BzY.js +142 -0
  139. package/dist/expandable-container.js +2 -142
  140. package/dist/fourier-field.js +638 -855
  141. package/dist/fourier-math.js +6 -6
  142. package/dist/glass-ui.css +1 -1
  143. package/dist/glass-ui.js +88 -75
  144. package/dist/handmark.js +330 -718
  145. package/dist/index.d.ts +2 -2
  146. package/dist/infinite-scroll.js +16 -17
  147. package/dist/{input-Bls2A4Tt.js → input-eLZ_AU7l.js} +1 -1
  148. package/dist/input.js +1 -1
  149. package/dist/keyboard.js +64 -21
  150. package/dist/{label-BQEZhnK_.js → label-CE03H1KL.js} +2 -2
  151. package/dist/label.js +1 -1
  152. package/dist/labeled-field.js +9 -6
  153. package/dist/{menu-wLu1XGKn.js → menu-BnmCOa3y.js} +93 -90
  154. package/dist/menu.js +2 -2
  155. package/dist/metric.js +12 -7
  156. package/dist/{motion-D4uYE0UM.js → motion-BvV1_24p.js} +1 -1
  157. package/dist/motion-core.js +35 -35
  158. package/dist/{number-field-NFAwQ0HK.js → number-field-RP89046a.js} +2 -2
  159. package/dist/number-field.js +1 -1
  160. package/dist/pager-dots.js +3 -3
  161. package/dist/{participation-B0hdsIRo.js → participation-Di4UHbzk.js} +8 -8
  162. package/dist/{pointerFieldMappings-DP5tla5z.js → pointerFieldMappings-DXwlU5uj.js} +7 -23
  163. package/dist/popover-DA87hFBx.js +184 -0
  164. package/dist/popover.js +1 -1
  165. package/dist/{progress-DF8dWZIX.js → progress-eVa-mc7J.js} +1 -1
  166. package/dist/progress.js +1 -1
  167. package/dist/{radio-group-Ci8o-pDx.js → radio-group-B58Bz16F.js} +2 -2
  168. package/dist/radio-group.js +1 -1
  169. package/dist/{select-CDgwGH_P.js → select-fy3vtArm.js} +13 -13
  170. package/dist/select.js +1 -1
  171. package/dist/{separator-CN11t406.js → separator-D4EZM10u.js} +2 -2
  172. package/dist/separator.js +1 -1
  173. package/dist/{sheet-BEFvnO_u.js → sheet-BQJyER59.js} +120 -115
  174. package/dist/sheet.js +2 -2
  175. package/dist/shortcuts-DGGWNRNe.js +13 -0
  176. package/dist/sidebar.js +4 -4
  177. package/dist/{slider-gsc8jDIo.js → slider-5ChjjWSR.js} +1 -1
  178. package/dist/slider.js +1 -1
  179. package/dist/{status-dot-BpnqOP5N.js → status-dot-W4LFiJps.js} +2 -2
  180. package/dist/status-dot.js +1 -1
  181. package/dist/styles/accessibility.css +1 -1
  182. package/dist/styles/animations.css +1 -1
  183. package/dist/styles/components.css +1 -1
  184. package/dist/styles/glass/a11y-fallback.css +1 -1
  185. package/dist/styles/glass/dissolve.css +1 -1
  186. package/dist/styles/glass/focus-veil.css +1 -1
  187. package/dist/styles/glass/ladder.css +1 -1
  188. package/dist/styles/glass/material.css +1 -1
  189. package/dist/styles/glass/overlay-plate.css +1 -1
  190. package/dist/styles/glass/reveal.css +1 -1
  191. package/dist/styles/glass-specular-track.css +1 -1
  192. package/dist/styles/paper.css +1 -1
  193. package/dist/styles/scroll-choreography.css +1 -1
  194. package/dist/styles/scroll-chrome.css +1 -1
  195. package/dist/styles/scroll-driven.css +1 -1
  196. package/dist/styles/theme/literals.css +1 -1
  197. package/dist/styles/tokens/color-radius.css +1 -1
  198. package/dist/styles/tokens/dark-arm-glass.css +1 -1
  199. package/dist/styles/tokens/light-dark.css +1 -1
  200. package/dist/styles/tokens/offsets.css +1 -1
  201. package/dist/styles/tokens/scheme-motion.css +1 -1
  202. package/dist/styles/tokens/sizing.css +1 -1
  203. package/dist/styles/typography/semantic.css +1 -1
  204. package/dist/styles/typography/utilities.css +1 -1
  205. package/dist/styles/utilities/a11y-overrides.css +1 -1
  206. package/dist/styles/utilities/responsive.css +1 -1
  207. package/dist/styles/view-transition.css +1 -1
  208. package/dist/styles/viz-reveal.css +1 -1
  209. package/dist/surface.js +1 -1
  210. package/dist/{switch-DxZzDrmV.js → switch-BYUZ6g9G.js} +2 -2
  211. package/dist/switch.js +1 -1
  212. package/dist/{tabs-CYscnq5H.js → tabs-rdRXKXxd.js} +4 -3
  213. package/dist/tabs.js +1 -1
  214. package/dist/textarea.js +1 -1
  215. package/dist/timeline.js +44 -42
  216. package/dist/{toast-9c6Kp58L.js → toast-DVOwo6GB.js} +1 -1
  217. package/dist/toast.js +1 -1
  218. package/dist/{toggle-group-11aPGMfE.js → toggle-group-CC5b3o1R.js} +2 -2
  219. package/dist/toggle-group.js +1 -1
  220. package/dist/{tooltip-DqKMZ4dn.js → tooltip-DhTHhtJN.js} +2 -2
  221. package/dist/tooltip.js +1 -1
  222. package/dist/typewriter.js +1 -1
  223. package/dist/{useAccentTone-DFAh1osB.js → useAccentTone-DitmKfGL.js} +1 -1
  224. package/dist/{resolveCanvasColor-6MCrswTB.js → useCanvas2D-CQRElMyE.js} +1 -11
  225. package/dist/{usePointerVelocityField-DsIf7yyq.js → usePointerVelocityField-Lx26_Yap.js} +1 -1
  226. package/dist/useResolveTokenColor-B2FfJnd9.js +24 -0
  227. package/package.json +5 -21
  228. package/dist/DialogContent-4hzLiaCQ.js +0 -152
  229. package/dist/canvas.d.ts +0 -1
  230. package/dist/canvas.js +0 -2
  231. package/dist/color.wgsl-B0N3mKMb.js +0 -412
  232. package/dist/compile-D7VYG3gi.js +0 -19
  233. package/dist/components/dock/composables/useDockOverflowFit.d.ts +0 -20
  234. package/dist/components/dock/composables/useDockTouchGate.d.ts +0 -42
  235. package/dist/components/dock/styles/overflow.css +0 -1
  236. package/dist/components/fourier-field/composables/fourierFieldGLSetup.d.ts +0 -24
  237. package/dist/components/fourier-field/composables/fourierFieldWGPUSetup.d.ts +0 -31
  238. package/dist/components/fourier-field/composables/uniformBridgeWGPU.d.ts +0 -39
  239. package/dist/components/fourier-field/composables/useFourierField.d.ts +0 -56
  240. package/dist/components/fourier-field/shaders/fourier-field.compute.wgsl.d.ts +0 -1
  241. package/dist/components/fourier-field/shaders/fourier-field.glsl.d.ts +0 -4
  242. package/dist/components/fourier-field/shaders/fourier-field.render.wgsl.d.ts +0 -1
  243. package/dist/components/fourier-field/shaders/fourier-field.ribbon.d.ts +0 -51
  244. package/dist/components/handmark/brush.d.ts +0 -269
  245. package/dist/components/handmark/composables/useHandMark.d.ts +0 -99
  246. package/dist/components/handmark/constants.d.ts +0 -69
  247. package/dist/components/handmark/freehand.d.ts +0 -69
  248. package/dist/components/handmark/geometry.d.ts +0 -62
  249. package/dist/components/handmark/ink.d.ts +0 -11
  250. package/dist/components/handmark/noise.d.ts +0 -30
  251. package/dist/components/handmark/texture.d.ts +0 -39
  252. package/dist/components/handmark/types.d.ts +0 -70
  253. package/dist/components/search/SearchBar.vue.d.ts +0 -38
  254. package/dist/components/search/index.d.ts +0 -8
  255. package/dist/components/search/searchVariants.d.ts +0 -16
  256. package/dist/configurator-BoeScnDL.js +0 -311
  257. package/dist/control-Da4XeT43.js +0 -11
  258. package/dist/popover-DBMKTPkL.js +0 -181
  259. package/dist/search.d.ts +0 -1
  260. package/dist/search.js +0 -61
  261. package/dist/useFuzzySearch-CUnZknpm.js +0 -177
  262. package/dist/useResolveTokenColor-CspQv_ik.js +0 -107
  263. /package/dist/{components/search/composables → composables/search}/types.d.ts +0 -0
  264. /package/dist/{components/search/composables → composables/search}/useFuzzySearch.d.ts +0 -0
@@ -1,104 +1,177 @@
1
- import { n as e } from "./useResolveTokenColor-CspQv_ik.js";
2
- import { t } from "./_plugin-vue_export-helper-B3ysoDQm.js";
3
- import { t as n } from "./usePointerVelocityField-DsIf7yyq.js";
4
- import { t as r } from "./useRoutePointer-B8itM7Ka.js";
5
- import { d as i, u as a } from "./pointerFieldMappings-DP5tla5z.js";
6
- import { i as o } from "./dark-z_P5QwqI.js";
7
- import { cssToOklch as s, oklchToLinear as c } from "./color.js";
8
- import { FOURIER_FIGURES as l, FOURIER_FIGURE_KEYS as u, comp as d, dftFromPoints as f, makeEllipticSpectrum as p, makeHarmonicFigure as m, partialSumAt as h, positionsAt as g } from "./fourier-math.js";
9
- import { m as _, n as v, o as y, r as b, s as x, u as S } from "./color.wgsl-B0N3mKMb.js";
10
- import { a as C } from "./rendererStatus-BxUlBLVy.js";
11
- import { n as w, t as T } from "./prng-2uS6J_A1.js";
12
- import { computed as E, createElementBlock as D, createElementVNode as O, defineComponent as k, normalizeClass as A, onMounted as j, onScopeDispose as M, openBlock as N, readonly as P, shallowRef as F, useTemplateRef as I, watch as L } from "vue";
1
+ import { n as e } from "./springPresets-Cbe0Voqg.js";
2
+ import { n as t } from "./useResolveTokenColor-B2FfJnd9.js";
3
+ import { t as n } from "./_plugin-vue_export-helper-B3ysoDQm.js";
4
+ import { t as r } from "./usePointerVelocityField-Lx26_Yap.js";
5
+ import { i } from "./dark-pONY3x93.js";
6
+ import { cssToOklch as a, gamutMapStop as o, oklchToLinear as s } from "./color.js";
7
+ import { FOURIER_FIGURES as c, FOURIER_FIGURE_KEYS as l, comp as u, dftFromPoints as d, makeEllipticSpectrum as f, makeHarmonicFigure as p, partialSumAt as m, positionsAt as h } from "./fourier-math.js";
8
+ import { a as g, n as _, r as v } from "./color.wgsl-BTszLY39.js";
9
+ import { a as y } from "./rendererStatus-BxUlBLVy.js";
10
+ import { n as b, t as x } from "./prng-2uS6J_A1.js";
11
+ import { computed as S, createElementBlock as C, createElementVNode as w, defineComponent as T, normalizeClass as E, onMounted as D, onScopeDispose as O, openBlock as k, readonly as A, shallowRef as j, useTemplateRef as M, watch as N } from "vue";
12
+ var P = .5, F = 720;
13
+ function I(e) {
14
+ let t = Infinity, n = Infinity, r = -Infinity, i = -Infinity;
15
+ for (let a = 0; a < F; a++) {
16
+ let [o, s] = m(e, a / F);
17
+ o < t && (t = o), s < n && (n = s), o > r && (r = o), s > i && (i = s);
18
+ }
19
+ return isFinite(t) ? {
20
+ minX: t,
21
+ minY: n,
22
+ maxX: r,
23
+ maxY: i
24
+ } : {
25
+ minX: 0,
26
+ minY: 0,
27
+ maxX: 0,
28
+ maxY: 0
29
+ };
30
+ }
31
+ function L(e) {
32
+ let t = e.find((e) => e.index === 0), n = t ? [t.coefficient[0], t.coefficient[1]] : [0, 0], r = e.filter((e) => e.index !== 0).slice().sort((e, t) => t.amplitude - e.amplitude);
33
+ if (r.length === 0) return R([u(1, .5, 0)], n);
34
+ let i = I(r), a = Math.max(i.maxX - i.minX, 1e-6), o = Math.max(i.maxY - i.minY, 1e-6), s = 856 / Math.hypot(a, o), c = r.filter((e) => 2 * e.amplitude * s >= P);
35
+ return R(c.length > 0 ? c : [r[0]], n);
36
+ }
37
+ function R(e, t) {
38
+ let n = I(e), r = Math.max(n.maxX - n.minX, 1e-6), i = Math.max(n.maxY - n.minY, 1e-6), a = 0;
39
+ for (let t of e) a += t.amplitude;
40
+ return {
41
+ terms: e,
42
+ dc: t,
43
+ halfSpanX: r / 2,
44
+ halfSpanY: i / 2,
45
+ centerX: (n.minX + n.maxX) / 2,
46
+ centerY: (n.minY + n.maxY) / 2,
47
+ diagonal: Math.hypot(r, i),
48
+ chainReach: a
49
+ };
50
+ }
51
+ //#endregion
13
52
  //#region src/components/fourier-field/constants.ts
14
- var R = 64, z = 384, B = .15, V = 3.6214285714285714, H = .55, ee = .35, U = [{
15
- L: .62,
16
- C: .19,
17
- h: 34
18
- }, {
19
- L: .86,
20
- C: .06,
21
- h: 70
22
- }], te = {
53
+ var z = .1, B = .22, ee = .11, te = .35, ne = .4, re = .5, ie = .15, ae = 1 / 64, V = 1 / 8, H = {
23
54
  source: "elliptic",
24
55
  harmonics: 6,
25
- showEpicycles: !0,
26
- epicycleArms: 6,
56
+ showMachine: !0,
27
57
  rainbowChain: !0,
28
58
  trailArc: .43,
29
- trailWidth: 5,
59
+ markStroke: 8,
60
+ inkOffset: .7,
61
+ squash: .55,
62
+ glow: .14,
30
63
  intensity: 1,
31
- harmonicScale: .24,
64
+ richness: .5,
32
65
  speed: 1,
33
- palette: U,
34
- respectReducedMotion: !0,
35
- interactive: !0,
36
- squashGain: H,
37
- celGain: ee
38
- }, ne = "\nconst MAX_PHASORS: i32 = 64;\nconst TAU: f32 = 6.283185307179586;\n\nstruct Phasor {\n // (re, im, index, _pad)\n data: vec4<f32>,\n};\n\n// ── Compute uniforms (the typed-struct source-of-truth — see uniformBridgeWGPU.ts) ──\nstruct ComputeUniforms {\n // c0: (headT, trailArc, _pad, _pad)\n c0: vec4<f32>,\n // ints: (harmonicN, epicycleArmN, sampleCount, phasorCount)\n ints: vec4<i32>,\n};\n\n@group(0) @binding(0) var<uniform> u: ComputeUniforms;\n@group(0) @binding(1) var<storage, read> phasors: array<Phasor>;\n// curveSamples[i] = (x, y, age, _pad)\n@group(0) @binding(2) var<storage, read_write> curveSamples: array<vec4<f32>>;\n// chainTips[k] = (x, y, _pad, _pad) — the running tip after stacking phasor k\n@group(0) @binding(3) var<storage, read_write> chainTips: array<vec4<f32>>;\n\n// The PARTIAL-SUM curve point at parameter t over the first n phasors — the WGSL\n// transcription of math.ts:78-95 (partialSumAt). EXACTLY:\n// c += vec2(re·cos − im·sin, re·sin + im·cos) with angle = TAU·index·t.\nfn partialSumAt(t: f32, n: i32) -> vec2<f32> {\n var c = vec2<f32>(0.0, 0.0);\n let phasorCount = u.ints.w;\n let lim = min(n, phasorCount);\n for (var i = 0; i < MAX_PHASORS; i = i + 1) {\n if (i >= lim) { break; }\n let p = phasors[i].data;\n let re = p.x;\n let im = p.y;\n let index = p.z;\n let angle = TAU * index * t;\n let cs = cos(angle);\n let sn = sin(angle);\n c = c + vec2<f32>(re * cs - im * sn, re * sn + im * cs);\n }\n return c;\n}\n\n// The epicycle chain tip after stacking the first k phasors at head_t — the WGSL\n// transcription of math.ts:41-60 (positionsAt, read at the running tip). The running tip\n// after phasor (k−1) is exactly the partial sum over the first k terms.\nfn epicycleChainTip(t: f32, k: i32) -> vec2<f32> {\n return partialSumAt(t, k);\n}\n\n@compute @workgroup_size(64)\nfn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = i32(gid.x);\n let headT = u.c0.x;\n let trailArc = u.c0.y;\n let harmonicN = u.ints.x;\n let armN = u.ints.y;\n let sampleCount = u.ints.z;\n\n // ── The comet body samples ──\n if (i < sampleCount) {\n // age: 0 at the oldest tail (i = sampleCount−1), 1 at the head (i = 0).\n let denom = max(f32(sampleCount - 1), 1.0);\n let frac = f32(i) / denom; // 0 = head, 1 = tail\n let age = 1.0 - frac; // 1 = head, 0 = tail\n // sample back along the period from the head by trailArc.\n var t = headT - trailArc * frac;\n t = t - floor(t); // fract → [0,1)\n let pt = partialSumAt(t, harmonicN);\n curveSamples[i] = vec4<f32>(pt.x, pt.y, age, 0.0);\n }\n\n // ── The epicycle chain tips (k = 0..armN, capped to harmonicN) ──\n // chainTips[0] is the origin; chainTips[k] is the tip after phasor (k−1).\n let armLim = min(armN, harmonicN);\n if (i <= armLim && i <= MAX_PHASORS) {\n let tip = epicycleChainTip(headT, i);\n chainTips[i] = vec4<f32>(tip.x, tip.y, 0.0, 0.0);\n }\n}\n", re = `
39
- // The instanced-ribbon LAYER selector (a pipeline-overridable constant — 5 pipelines share the
40
- // ONE module, each specializing this): 0 trail · 1 epicycle · 2 head · 3 cel-rope · 4 cel-arm.
66
+ palette: [{
67
+ L: .62,
68
+ C: .19,
69
+ h: 34
70
+ }, {
71
+ L: .86,
72
+ C: .06,
73
+ h: 70
74
+ }],
75
+ respectReducedMotion: !0
76
+ }, U = e("world"), W = 2 * Math.PI / U.response, oe = U.dampingFraction, se = .22, ce = .06, le = re * W * W;
77
+ function ue() {
78
+ let e = 0, t = 0, n = 0, r = !0;
79
+ function i(e) {
80
+ !Number.isFinite(e) || e === 0 || (n = Math.min(Math.abs(e), le) * Math.sign(e));
81
+ }
82
+ function a(e) {
83
+ if (t === 0 && n === 0) return;
84
+ let r = -W * W * t - 2 * oe * W * n;
85
+ n += r * e, t += n * e, Math.abs(t) < 1e-4 && Math.abs(n) < 1e-4 && (t = 0, n = 0);
86
+ }
87
+ return {
88
+ get headT() {
89
+ return e;
90
+ },
91
+ get momentum() {
92
+ return t;
93
+ },
94
+ step(o) {
95
+ if (o.frozen) return t = 0, n = 0, 0;
96
+ let s = Math.min(Math.max(o.dt, 0), .05), c = o.speed / 16;
97
+ return o.engaged ? (c += o.scrubVelocity * ie, r && o.burst >= se ? (r = !1, i(o.burst * 4 * Math.sign(o.scrubVelocity || 1))) : !r && o.burst <= ce && (r = !0)) : r = !0, c = Math.max(0, c + t), e = (e + c * s) % 1, e < 0 && (e += 1), a(s), c;
98
+ },
99
+ set(t) {
100
+ e = (t % 1 + 1) % 1;
101
+ },
102
+ flick(e) {
103
+ i(e);
104
+ }
105
+ };
106
+ }
107
+ //#endregion
108
+ //#region src/components/fourier-field/shaders/compute.wgsl.ts
109
+ var de = "\nconst TAU: f32 = 6.283185307179586;\n\nstruct Phasor {\n // (re, im, index, _pad)\n data: vec4<f32>,\n};\n\nstruct ComputeUniforms {\n // c0: (headT, trailArc, _pad, _pad)\n c0: vec4<f32>,\n // ints: (harmonicN, sampleCount, termCount, _pad)\n ints: vec4<i32>,\n};\n\n@group(0) @binding(0) var<uniform> u: ComputeUniforms;\n@group(0) @binding(1) var<storage, read> phasors: array<Phasor>;\n// curveSamples[i] = (x, y, age, _pad) in MODEL space\n@group(0) @binding(2) var<storage, read_write> curveSamples: array<vec4<f32>>;\n// chainTips[k] = (x, y, _pad, _pad) in MODEL space\n@group(0) @binding(3) var<storage, read_write> chainTips: array<vec4<f32>>;\n\n// The truncated inverse DFT at parameter t over the first n terms — the transcription of\n// math.ts partialSumAt: c += vec2(re*cos - im*sin, re*sin + im*cos), angle = TAU*index*t.\nfn partialSumAt(t: f32, n: i32) -> vec2<f32> {\n var c = vec2<f32>(0.0, 0.0);\n let lim = min(n, i32(arrayLength(&phasors)));\n for (var i = 0; i < lim; i = i + 1) {\n let p = phasors[i].data;\n let angle = TAU * p.z * t;\n let cs = cos(angle);\n let sn = sin(angle);\n c = c + vec2<f32>(p.x * cs - p.y * sn, p.x * sn + p.y * cs);\n }\n return c;\n}\n\n@compute @workgroup_size(64)\nfn cs_main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = i32(gid.x);\n let headT = u.c0.x;\n let trailArc = u.c0.y;\n let harmonicN = u.ints.x;\n let sampleCount = u.ints.y;\n\n // ── The comet body ──\n if (i < sampleCount) {\n let denom = max(f32(sampleCount - 1), 1.0);\n let frac = f32(i) / denom; // 0 = head, 1 = tail\n let age = 1.0 - frac; // 1 = head, 0 = tail\n var t = headT - trailArc * frac;\n t = t - floor(t); // fract -> [0,1)\n let pt = partialSumAt(t, harmonicN);\n curveSamples[i] = vec4<f32>(pt.x, pt.y, age, 0.0);\n }\n\n // ── The chain tips (k = 0..N). chainTips[0] is the origin. ──\n if (i <= harmonicN && i < i32(arrayLength(&chainTips))) {\n let tip = partialSumAt(headT, i);\n chainTips[i] = vec4<f32>(tip.x, tip.y, 0.0, 0.0);\n }\n}\n", fe = `
110
+ // 0 chain · 1 trail · 2 head — a pipeline-overridable constant, three pipelines, one module.
41
111
  override LAYER: i32 = 0;
42
112
 
113
+ // THE SPLICE CONTRACT, honoured. OKLCH_MATRICES_WGSL states it at color.wgsl.ts:52-53 —
114
+ // "PI must be in scope (the consumer defines it first, as the GLSL chunk requires)" — and
115
+ // spends it at :105 folding the OKLCh hue to [0, 2pi). This module splices that chunk
116
+ // below and defined PI nowhere, so every CreateShaderModule on this source failed
117
+ // validation with "unresolved value 'PI'" and the stage painted NOTHING. The two other
118
+ // WGSL primaries carry the same line for the same reason (aurora.wgsl.ts:49,
119
+ // metaball.wgsl.ts:53); this is that house idiom, not a local patch. The CLASS is gated
120
+ // by tests/components/fourier-field/wgsl-splice-contract.test.ts, which resolves every
121
+ // assembled module against the chunks it splices.
43
122
  const PI: f32 = 3.141592653589793;
44
- // The degenerate-tangent guard must equal FOURIER_TANGENT_EPS in
45
- // constants.ts and the GL CPU bead path so a cusp resolves identically across both engines.
123
+
46
124
  const TANGENT_EPS: f32 = 1e-4;
47
125
  const SPEED_REF_HALFWIDTHS: f32 = 6.0;
48
- // The thick luminous ribbon mirrors RIBBON_TAIL_FRAC verbatim
49
- // from constants.ts because the WGSL twin cannot import a TS const; parity coverage verifies the
50
- // two copies are equal so the mid-body floor (RIBBON_HEAD_FLOOR_PX) can never drift off it.
51
- const RIBBON_TAIL_FRAC: f32 = 0.4;
52
- const RIBBON_UNDERGLOW_SCALE: f32 = 2.2;
53
- const RIBBON_UNDERGLOW_ALPHA: f32 = 0.16;
126
+ // The trail's taper: the mark keeps this fraction of its width at the oldest tail.
127
+ const TAIL_TAPER: f32 = 0.55;
54
128
 
55
- // ── Render uniforms (the typed-struct source-of-truth — see uniformBridgeWGPU.ts) ──
56
129
  struct RenderUniforms {
57
- // r0: (centerX, centerY, scale, aspect) — model→clip fit transform
130
+ // r0: (centerX, centerY, scale, aspect)
58
131
  r0: vec4<f32>,
59
- // r1: (trailWidth, peakAlpha, headGlowAlpha, trailFadeExp)
132
+ // r1: (markHalf, inkOffsetStrokes, scaffoldFrac, edgeMargin)
60
133
  r1: vec4<f32>,
61
- // r2: (trailFloor, intensity, showEpicycles, rainbowChain)
134
+ // r2: (peakAlpha, glow, squash, trailFadeExp)
62
135
  r2: vec4<f32>,
63
- // r3: (squashGain, celGain, edgeMargin, _pad) — FOURIER-LOOM §2b/§3b + the ribbon bbox slop
136
+ // r3: (trailFloor, hueSweep, ringStrokeModel, showMachine)
64
137
  r3: vec4<f32>,
65
- // ints: (sampleCount, armCount, stopCount, _pad)
138
+ // ink: (linear rgb, rainbowChain)
139
+ ink: vec4<f32>,
140
+ // head: (linear rgb, _pad)
141
+ head: vec4<f32>,
142
+ // ints: (sampleCount, lutSize, chainCount, _pad)
66
143
  ints: vec4<i32>,
67
- // palette stops: linear-sRGB rgb + _pad
68
- palette: array<vec4<f32>, 4>,
144
+ // the CPU-resolved linear-sRGB ramp — the length is the TS constant, not a second copy
145
+ lut: array<vec4<f32>, 16>,
69
146
  };
70
147
 
71
148
  @group(0) @binding(0) var<uniform> u: RenderUniforms;
72
- // curveSamples[i] = (x, y, age, _pad) in MODEL space — the render VERTEX-PULLS these.
73
149
  @group(0) @binding(1) var<storage, read> curveSamples: array<vec4<f32>>;
74
- // chainTips[k] = (x, y, _pad, _pad) in MODEL space.
75
150
  @group(0) @binding(2) var<storage, read> chainTips: array<vec4<f32>>;
76
151
 
152
+ ${_}
77
153
  ${v}
78
- ${b}
79
154
 
80
- fn samplePaletteLin(t: f32) -> vec3<f32> {
81
- let stopCount = u.ints.z;
82
- if (stopCount <= 1) { return u.palette[0].rgb; }
83
- let n = f32(stopCount);
84
- let ft = clamp(t, 0.0, 1.0) * (n - 1.0);
155
+ // Read the CPU-resolved ramp. No colour-space conversion happens here — the LUT already
156
+ // carries the chroma floor and the gamut map.
157
+ fn sampleRamp(t: f32) -> vec3<f32> {
158
+ let n = u.ints.y;
159
+ let ft = clamp(t, 0.0, 1.0) * f32(n - 1);
85
160
  let i0 = i32(floor(ft));
86
- let i1 = min(i0 + 1, stopCount - 1);
87
- let f = ft - f32(i0);
88
- let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i0].rgb);
89
- let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i1].rgb);
90
- return oklabToLinearSrgb(mix(labA, labB, f));
161
+ let i1 = min(i0 + 1, n - 1);
162
+ return mix(u.lut[i0].rgb, u.lut[i1].rgb, ft - f32(i0));
91
163
  }
92
164
 
93
- fn chainColorLin(seg: f32, rainbow: bool) -> vec3<f32> {
94
- let baseLin = u.palette[0].rgb;
95
- if (!rainbow) { return baseLin; }
96
- let lab = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * baseLin);
165
+ // The chain's hue sweep, taken ONCE PER INSTANCE in the vertex stage: the ramp entry for
166
+ // this arm, rotated about the palette anchor by ±hueSweep. Anchored warm, the sweep is
167
+ // bounded by its own arithmetic — it cannot arrive anywhere the palette is not.
168
+ fn chainTint(seg: f32) -> vec3<f32> {
169
+ let base = sampleRamp(seg * 0.6);
170
+ if (u.ink.w < 0.5) { return base; }
171
+ let lab = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * base);
97
172
  let lch = oklabToOklch(lab);
98
- let hueShift = mix(-0.45, 1.15, clamp(seg, 0.0, 1.0));
99
- let h = lch.z + hueShift;
100
- let lifted = vec3<f32>(0.66, max(lch.y, 0.14), h);
101
- return oklabToLinearSrgb(oklchToOklab(lifted));
173
+ let swept = vec3<f32>(lch.x, lch.y, lch.z + mix(-u.r3.y, u.r3.y, clamp(seg, 0.0, 1.0)));
174
+ return oklabToLinearSrgb(oklchToOklab(swept));
102
175
  }
103
176
 
104
177
  fn segDist(p: vec2<f32>, a: vec2<f32>, b: vec2<f32>) -> f32 {
@@ -108,10 +181,10 @@ fn segDist(p: vec2<f32>, a: vec2<f32>, b: vec2<f32>) -> f32 {
108
181
  return length(pa - ba * h);
109
182
  }
110
183
 
111
- // The head travel frame + the volume-preserving anisotropic distance.
184
+ // The head's travel frame + its volume-preserving anisotropic metric.
112
185
  struct HeadFrame { T: vec2<f32>, sHat: f32, };
113
- fn headFrame(head: vec2<f32>, headBack: vec2<f32>, halfW: f32) -> HeadFrame {
114
- let d = head - headBack;
186
+ fn headFrame(head: vec2<f32>, back: vec2<f32>, halfW: f32) -> HeadFrame {
187
+ let d = head - back;
115
188
  let s = length(d);
116
189
  var out: HeadFrame;
117
190
  if (s <= TANGENT_EPS) {
@@ -120,8 +193,7 @@ fn headFrame(head: vec2<f32>, headBack: vec2<f32>, halfW: f32) -> HeadFrame {
120
193
  return out;
121
194
  }
122
195
  out.T = d / s;
123
- let refScale = max(halfW * SPEED_REF_HALFWIDTHS, 1e-6);
124
- out.sHat = clamp(s / refScale, 0.0, 1.0);
196
+ out.sHat = clamp(s / max(halfW * SPEED_REF_HALFWIDTHS, 1e-6), 0.0, 1.0);
125
197
  return out;
126
198
  }
127
199
 
@@ -129,19 +201,9 @@ fn headAniso(p: vec2<f32>, head: vec2<f32>, fr: HeadFrame, k: f32) -> f32 {
129
201
  let g = 1.0 + k * fr.sHat;
130
202
  let n = vec2<f32>(-fr.T.y, fr.T.x);
131
203
  let rel = p - head;
132
- let along = dot(rel, fr.T) / g;
133
- let across = dot(rel, n) * g;
134
- return length(vec2<f32>(along, across));
135
- }
136
-
137
- // The cel ink lags behind the travel — the SAME −T·halfW·1.4 offset both engines apply (the
138
- // head frame is read straight off the compute-filled curveSamples[0]/[1], parity by construction).
139
- fn celOffset(halfW: f32) -> vec2<f32> {
140
- let fr = headFrame(curveSamples[0].xy, curveSamples[1].xy, halfW);
141
- return -fr.T * halfW * 1.4;
204
+ return length(vec2<f32>(dot(rel, fr.T) / g, dot(rel, n) * g));
142
205
  }
143
206
 
144
- // The unit quad as two triangles (6 verts) in [0,1]² — the instanced vertex expands it.
145
207
  fn quadCorner(vi: u32) -> vec2<f32> {
146
208
  var c = array<vec2<f32>, 6>(
147
209
  vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 0.0), vec2<f32>(0.0, 1.0),
@@ -152,10 +214,12 @@ fn quadCorner(vi: u32) -> vec2<f32> {
152
214
 
153
215
  struct VSOut {
154
216
  @builtin(position) pos: vec4<f32>,
155
- @location(0) p: vec2<f32>, // model-space fragment coordinate
217
+ @location(0) p: vec2<f32>,
156
218
  @location(1) a: vec2<f32>,
157
219
  @location(2) b: vec2<f32>,
158
- @location(3) segData: vec3<f32>, // (age, strokeHalf, arg2)
220
+ // (age, strokeHalf, drawRing)
221
+ @location(3) seg: vec3<f32>,
222
+ @location(4) @interpolate(flat) tint: vec3<f32>,
159
223
  };
160
224
 
161
225
  @vertex
@@ -164,54 +228,44 @@ fn vs_main(@builtin(vertex_index) vi: u32, @builtin(instance_index) inst: u32) -
164
228
  let center = u.r0.xy;
165
229
  let scale = max(u.r0.z, 1e-6);
166
230
  let aspect = max(u.r0.w, 1e-4);
167
- let halfW = u.r1.x * 0.5;
168
- let margin = u.r3.z;
169
-
170
- var a: vec2<f32> = vec2<f32>(0.0);
171
- var b: vec2<f32> = vec2<f32>(0.0);
172
- var strokeHalf: f32 = halfW;
173
- var age: f32 = 0.0;
174
- var arg2: f32 = 0.0;
175
- var isCapsule: bool = true;
176
- var ext: f32 = halfW;
231
+ let markHalf = u.r1.x;
232
+ let margin = u.r1.w;
233
+
234
+ var a = vec2<f32>(0.0);
235
+ var b = vec2<f32>(0.0);
236
+ var strokeHalf = markHalf;
237
+ var age = 0.0;
238
+ var drawRing = 0.0;
239
+ var tint = vec3<f32>(0.0);
240
+ var isCapsule = true;
241
+ var ext = markHalf;
177
242
 
178
243
  if (LAYER == 0) {
179
- // TRAIL — the curve capsule; the covered extent is the head-width under-glow.
244
+ // CHAIN — the AABB around this arm's own orbit (radius = |b − a| = |c_k|).
245
+ a = chainTips[inst].xy;
246
+ b = chainTips[inst + 1u].xy;
247
+ let radius = length(b - a);
248
+ // THE RING LAW, asked of the live stage: a circle narrower than its own line is a blob.
249
+ if (2.0 * radius >= u.r3.z) { drawRing = 1.0; }
250
+ strokeHalf = markHalf * u.r1.z;
251
+ tint = chainTint(f32(inst) / max(f32(u.ints.z) - 1.0, 1.0));
252
+ ext = radius + strokeHalf * 2.0;
253
+ isCapsule = false;
254
+ } else if (LAYER == 1) {
255
+ // TRAIL — the curve capsule, padded for the ink's own offset.
180
256
  a = curveSamples[inst].xy;
181
257
  b = curveSamples[inst + 1u].xy;
182
258
  age = max(curveSamples[inst].z, curveSamples[inst + 1u].z);
183
- ext = halfW * RIBBON_UNDERGLOW_SCALE;
259
+ tint = sampleRamp(1.0 - age);
260
+ ext = markHalf * (1.0 + 2.0 * max(u.r1.y, 0.0));
184
261
  isCapsule = true;
185
- } else if (LAYER == 1) {
186
- // EPICYCLE — the chain AABB around a (radius = |b−a|).
187
- a = chainTips[inst].xy;
188
- b = chainTips[inst + 1u].xy;
189
- arg2 = f32(inst) / max(f32(u.ints.y - 1), 1.0);
190
- ext = length(b - a) + halfW * 0.7;
191
- isCapsule = false;
192
- } else if (LAYER == 2) {
193
- // HEAD — the comet head at curveSamples[0], AABB by the halo radius.
262
+ } else {
263
+ // HEAD — centred on the newest curve sample; the AABB is the halo.
194
264
  a = curveSamples[0].xy;
195
265
  b = curveSamples[1].xy;
196
- ext = halfW * 4.5;
266
+ tint = u.head.rgb;
267
+ ext = markHalf * 5.0;
197
268
  isCapsule = false;
198
- } else if (LAYER == 3) {
199
- // CEL-ROPE — the curve capsule, offset opposite travel; full-width ink.
200
- let off = celOffset(halfW);
201
- a = curveSamples[inst].xy + off;
202
- b = curveSamples[inst + 1u].xy + off;
203
- arg2 = 1.0;
204
- ext = halfW;
205
- isCapsule = true;
206
- } else {
207
- // CEL-ARM (LAYER == 4) — the chain capsule, offset; the narrower arm ink (0.55·halfW).
208
- let off = celOffset(halfW);
209
- a = chainTips[inst].xy + off;
210
- b = chainTips[inst + 1u].xy + off;
211
- strokeHalf = halfW * 0.55;
212
- arg2 = 0.7;
213
- ext = halfW * 0.55;
214
- isCapsule = true;
215
269
  }
216
270
  ext = ext + margin;
217
271
 
@@ -222,20 +276,18 @@ fn vs_main(@builtin(vertex_index) vi: u32, @builtin(instance_index) inst: u32) -
222
276
  var dir = vec2<f32>(1.0, 0.0);
223
277
  if (len > 1e-6) { dir = d / len; }
224
278
  let perp = vec2<f32>(-dir.y, dir.x);
225
- let along = mix(a - dir * ext, b + dir * ext, corner.x);
226
- pos = along + perp * (corner.y * 2.0 - 1.0) * ext;
279
+ pos = mix(a - dir * ext, b + dir * ext, corner.x) + perp * (corner.y * 2.0 - 1.0) * ext;
227
280
  } else {
228
281
  pos = a + (corner * 2.0 - vec2<f32>(1.0)) * ext;
229
282
  }
230
283
 
231
- let clipX = (pos.x - center.x) * scale / aspect;
232
- let clipY = (pos.y - center.y) * scale;
233
284
  var out: VSOut;
234
- out.pos = vec4<f32>(clipX, clipY, 0.0, 1.0);
285
+ out.pos = vec4<f32>((pos.x - center.x) * scale / aspect, (pos.y - center.y) * scale, 0.0, 1.0);
235
286
  out.p = pos;
236
287
  out.a = a;
237
288
  out.b = b;
238
- out.segData = vec3<f32>(age, strokeHalf, arg2);
289
+ out.seg = vec3<f32>(age, strokeHalf, drawRing);
290
+ out.tint = tint;
239
291
  return out;
240
292
  }
241
293
 
@@ -243,270 +295,223 @@ fn vs_main(@builtin(vertex_index) vi: u32, @builtin(instance_index) inst: u32) -
243
295
  fn fs_main(in: VSOut) -> @location(0) vec4<f32> {
244
296
  let p = in.p;
245
297
  let aa = max(fwidth(p.x) + fwidth(p.y), 1e-5) * 0.75;
246
- let peak = u.r1.y * u.r2.y;
247
- let headGlow = u.r1.z * u.r2.y;
248
- let fadeExp = u.r1.w;
249
- let trailFloor = u.r2.x;
250
- let rainbow = u.r2.w > 0.5;
251
- let squashGain = u.r3.x;
252
- let celGain = u.r3.y;
253
- let halfW = in.segData.y;
254
- let age = in.segData.x;
255
- let arg2 = in.segData.z;
298
+ let peak = u.r2.x;
299
+ let glow = u.r2.y;
300
+ let squash = u.r2.z;
301
+ let fadeExp = u.r2.w;
302
+ let trailFloor = u.r3.x;
303
+ let markHalf = u.r1.x;
304
+ let strokeHalf = in.seg.y;
305
+ let age = in.seg.x;
256
306
  let a = in.a;
257
307
  let b = in.b;
258
308
 
259
309
  if (LAYER == 0) {
260
- // ── TRAIL — the tapered luminous ribbon over its own soft under-glow ──
261
- let d = segDist(p, a, b);
262
- let ribbonHalf = halfW * (RIBBON_TAIL_FRAC + (1.0 - RIBBON_TAIL_FRAC) * age);
263
- let lin = samplePaletteLin(1.0 - age * 0.4);
264
- let rgb = clamp(linearToSrgb(lin), vec3<f32>(0.0), vec3<f32>(1.0));
265
- let glowHalf = ribbonHalf * RIBBON_UNDERGLOW_SCALE;
266
- let glowCover = 1.0 - smoothstep(glowHalf, glowHalf + aa * 3.0, d);
267
- let ga = peak * RIBBON_UNDERGLOW_ALPHA * pow(age, fadeExp) * glowCover;
268
- let cover = 1.0 - smoothstep(ribbonHalf, ribbonHalf + aa, d);
269
- let ca = max(peak * pow(age, fadeExp), peak * trailFloor) * cover;
270
- let outA = ca + ga * (1.0 - ca);
271
- return vec4<f32>(rgb * outA, outA);
272
- } else if (LAYER == 1) {
273
- // ── EPICYCLE — orbit RING + ARM + joint DOT union (MAX-blended, no join seam) ──
274
- let lin = chainColorLin(arg2, rainbow);
275
- let rgb = clamp(linearToSrgb(lin), vec3<f32>(0.0), vec3<f32>(1.0));
310
+ // ── THE MACHINE — ring (conditionally), arm, joint dot; one scaffold rung ──
276
311
  let radius = length(b - a);
277
- let dRing = abs(length(p - a) - radius);
278
- let ringW = max(halfW * 0.45, aa);
279
- let ringMask = (1.0 - smoothstep(ringW, ringW + aa, dRing)) * 0.5;
280
- let dArm = segDist(p, a, b);
281
- let armMask = 1.0 - smoothstep(halfW * 0.55, halfW * 0.55 + aa, dArm);
282
- let dotR = max(halfW * 0.7, aa * 2.0);
283
- let dDot = length(p - a);
284
- let dotMask = 1.0 - smoothstep(dotR, dotR + aa, dDot);
285
- let m = clamp(max(max(ringMask, armMask), dotMask), 0.0, 1.0) * peak * 0.7;
286
- return vec4<f32>(rgb * m, m);
287
- } else if (LAYER == 2) {
288
- // ── HEAD — squash-and-stretch (§2b): halo + saturated core + white specular ──
289
- let head = a;
290
- let headBack = b;
291
- let fr = headFrame(head, headBack, halfW);
292
- let dHead = headAniso(p, head, fr, squashGain);
293
- let coreR = halfW * 1.5;
294
- let haloR = coreR * 3.0;
295
- let headRgb = clamp(linearToSrgb(samplePaletteLin(0.0)), vec3<f32>(0.0), vec3<f32>(1.0));
296
- let halo = (1.0 - smoothstep(0.0, haloR, dHead)) * headGlow * 0.35;
297
- var accum = vec4<f32>(headRgb * halo, halo);
298
- let core = (1.0 - smoothstep(coreR, coreR + aa, dHead)) * headGlow;
299
- accum = vec4<f32>(headRgb * core, core) + accum * (1.0 - core);
300
- let spec = (1.0 - smoothstep(coreR * 0.4, coreR * 0.4 + aa, dHead)) * headGlow * 0.9;
301
- accum = vec4<f32>(vec3<f32>(1.0) * spec, spec) + accum * (1.0 - spec);
302
- return accum;
312
+ var m = 0.0;
313
+ if (in.seg.z > 0.5) {
314
+ let dRing = abs(length(p - a) - radius);
315
+ m = max(m, 1.0 - smoothstep(strokeHalf, strokeHalf + aa, dRing));
316
+ }
317
+ m = max(m, 1.0 - smoothstep(strokeHalf, strokeHalf + aa, segDist(p, a, b)));
318
+ // The joint dot is the mark's own diameter — the machine's one solid rung.
319
+ m = max(m, 1.0 - smoothstep(markHalf, markHalf + aa, length(p - a)));
320
+ let alpha = clamp(m, 0.0, 1.0) * peak;
321
+ let rgb = clamp(linearToSrgb(in.tint), vec3<f32>(0.0), vec3<f32>(1.0));
322
+ return vec4<f32>(rgb * alpha, alpha);
323
+ } else if (LAYER == 1) {
324
+ // ── THE CURVE — ink under mark, composited at α1, then ONE envelope ──
325
+ let taper = TAIL_TAPER + (1.0 - TAIL_TAPER) * age;
326
+ let half = markHalf * taper;
327
+ // The ink rides THIS segment's own tangent, opposite the travel: the head runs from
328
+ // b toward a, so the ink lands behind at (b − a).
329
+ var dir = vec2<f32>(1.0, 0.0);
330
+ let d = b - a;
331
+ let len = length(d);
332
+ if (len > 1e-6) { dir = d / len; }
333
+ let off = dir * (markHalf * 2.0 * u.r1.y);
334
+
335
+ let aMark = 1.0 - smoothstep(half, half + aa, segDist(p, a, b));
336
+ let aInk = 1.0 - smoothstep(half, half + aa, segDist(p, a + off, b + off));
337
+
338
+ let markRgb = clamp(linearToSrgb(in.tint), vec3<f32>(0.0), vec3<f32>(1.0));
339
+ let inkRgb = clamp(linearToSrgb(u.ink.rgb), vec3<f32>(0.0), vec3<f32>(1.0));
340
+
341
+ // Mark OVER ink, both fully opaque where they cover — the pair, then the envelope.
342
+ let aPair = aMark + aInk * (1.0 - aMark);
343
+ let rgbPair = markRgb * aMark + inkRgb * aInk * (1.0 - aMark);
344
+ let env = peak * max(pow(max(age, 0.0), fadeExp), trailFloor);
345
+ return vec4<f32>(rgbPair * env, aPair * env);
303
346
  } else {
304
- // ── CEL — the technicolor ink shadow (rope LAYER==3, arm LAYER==4) ──
305
- let celA = clamp(celGain, 0.0, 1.0);
306
- let d = segDist(p, a, b);
307
- let cover = 1.0 - smoothstep(halfW, halfW + aa, d);
308
- let m = cover * celA * peak * arg2; // arg2 = celStrength (1.0 rope / 0.7 arm)
309
- return vec4<f32>(0.0, 0.0, 0.0, m);
347
+ // ── THE HEAD — palette-lifted core over its own halo. Light-led, never white. ──
348
+ let fr = headFrame(a, b, markHalf);
349
+ let dHead = headAniso(p, a, fr, squash);
350
+ let coreR = markHalf * 1.5;
351
+ let haloR = coreR * 3.0;
352
+ let rgb = clamp(linearToSrgb(in.tint), vec3<f32>(0.0), vec3<f32>(1.0));
353
+ let halo = (1.0 - smoothstep(0.0, haloR, dHead)) * glow;
354
+ let core = (1.0 - smoothstep(coreR, coreR + aa, dHead)) * peak;
355
+ let alpha = clamp(max(core, halo), 0.0, 1.0);
356
+ return vec4<f32>(rgb * alpha, alpha);
310
357
  }
311
358
  }
312
- `, W = {
313
- trail: 0,
314
- epicycle: 1,
315
- head: 2,
316
- "cel-rope": 3,
317
- "cel-arm": 4
318
- };
319
- function ie(e) {
320
- let t = Math.max(0, Math.floor(e.sampleCount) - 1), n = e.showEpicycles ? Math.max(0, Math.floor(e.armCount)) : 0, r = e.celGain > .001, i = [];
321
- return r && t > 0 && i.push({
322
- kind: "cel-rope",
323
- instanceCount: t,
324
- blend: "over"
325
- }), r && n > 0 && i.push({
326
- kind: "cel-arm",
327
- instanceCount: n,
328
- blend: "over"
329
- }), n > 0 && i.push({
330
- kind: "epicycle",
331
- instanceCount: n,
332
- blend: "max"
333
- }), t > 0 && i.push({
334
- kind: "trail",
335
- instanceCount: t,
336
- blend: "over"
337
- }), i.push({
338
- kind: "head",
339
- instanceCount: 1,
340
- blend: "over"
341
- }), i;
342
- }
343
- var ae = new Float32Array([
344
- 0,
345
- 0,
346
- 1,
347
- 0,
348
- 0,
349
- 1,
350
- 1,
351
- 1
352
- ]), oe = 32, G = {
359
+ `, pe = 32, G = {
353
360
  c0: 0,
354
361
  ints: 4
355
362
  };
356
363
  function K() {
357
- let e = /* @__PURE__ */ new ArrayBuffer(oe);
364
+ let e = /* @__PURE__ */ new ArrayBuffer(pe);
358
365
  return {
359
366
  buffer: e,
360
367
  f32: new Float32Array(e),
361
368
  i32: new Int32Array(e)
362
369
  };
363
370
  }
364
- function se(e, t, n, r, i, a, o) {
365
- let { f32: s, i32: c } = e;
366
- return s[G.c0 + 0] = t, s[G.c0 + 1] = n, s[G.c0 + 2] = 0, s[G.c0 + 3] = 0, c[G.ints + 0] = r, c[G.ints + 1] = i, c[G.ints + 2] = a, c[G.ints + 3] = o, e;
371
+ function me(e, t, n, r, i, a) {
372
+ let { f32: o, i32: s } = e;
373
+ return o[G.c0 + 0] = t, o[G.c0 + 1] = n, o[G.c0 + 2] = 0, o[G.c0 + 3] = 0, s[G.ints + 0] = r, s[G.ints + 1] = i, s[G.ints + 2] = a, s[G.ints + 3] = 0, e;
367
374
  }
368
- var q = 144, J = {
375
+ var he = 368, q = {
369
376
  r0: 0,
370
377
  r1: 4,
371
378
  r2: 8,
372
379
  r3: 12,
373
- ints: 16,
374
- palette: 20
380
+ ink: 16,
381
+ head: 20,
382
+ ints: 24,
383
+ lut: 28
375
384
  };
376
- function Y() {
377
- let e = new ArrayBuffer(q);
385
+ function J() {
386
+ let e = new ArrayBuffer(he);
378
387
  return {
379
388
  buffer: e,
380
389
  f32: new Float32Array(e),
381
390
  i32: new Int32Array(e)
382
391
  };
383
392
  }
384
- function X(e) {
385
- let t = Infinity, n = Infinity, r = -Infinity, i = -Infinity, a = e;
386
- for (let e = 0; e < 720; e++) {
387
- let o = g(a, e / 720), [s, c] = o[o.length - 1];
388
- s < t && (t = s), c < n && (n = c), s > r && (r = s), c > i && (i = c);
389
- }
390
- if (!isFinite(t)) return {
391
- centerX: 0,
392
- centerY: 0,
393
- scale: 1
394
- };
395
- let o = Math.max(.001, r - t), s = Math.max(.001, i - n), c = 2 * .82 / Math.max(o, s);
393
+ var ge = .82;
394
+ function _e(e, t) {
395
+ let n = Math.max(e.halfSpanX, e.halfSpanY, 1e-6), r = t ? e.chainReach : 0, i = Math.max(n, r, 1e-6);
396
396
  return {
397
- centerX: (t + r) / 2,
398
- centerY: (n + i) / 2,
399
- scale: c
397
+ centerX: e.centerX,
398
+ centerY: e.centerY,
399
+ scale: ge / i
400
400
  };
401
401
  }
402
- function ce(e, t, n) {
403
- let r = Math.max(e, V), i = Math.max(n, 1) / 2;
404
- return r / Math.max(t, 1e-6) / i;
402
+ function Y(e, t, n) {
403
+ let r = Math.max(n, 1) / 2;
404
+ return e / Math.max(t, 1e-6) / r;
405
+ }
406
+ function ve(e, t, n) {
407
+ let r = t.h - e.h;
408
+ for (; r > 180;) r -= 360;
409
+ for (; r < -180;) r += 360;
410
+ return {
411
+ L: e.L + (t.L - e.L) * n,
412
+ C: e.C + (t.C - e.C) * n,
413
+ h: e.h + r * n
414
+ };
405
415
  }
406
- function Z(e, t, n, r, i, a, o, s, l, u, d, f, p, m, h, g, _) {
407
- let { f32: v, i32: y } = e;
408
- v[J.r0 + 0] = t.centerX, v[J.r0 + 1] = t.centerY, v[J.r0 + 2] = t.scale, v[J.r0 + 3] = n, v[J.r1 + 0] = r, v[J.r1 + 1] = i, v[J.r1 + 2] = a, v[J.r1 + 3] = o, v[J.r2 + 0] = s, v[J.r2 + 1] = l, v[J.r2 + 2] = +!!u, v[J.r2 + 3] = +!!d, v[J.r3 + 0] = f, v[J.r3 + 1] = p, v[J.r3 + 2] = m, v[J.r3 + 3] = 0;
409
- let b = Math.min(_.length, 4);
410
- y[J.ints + 0] = h, y[J.ints + 1] = g, y[J.ints + 2] = b, y[J.ints + 3] = 0;
411
- for (let e = 0; e < 4; e++) {
412
- let t = J.palette + e * 4, n = c(_[Math.min(e, b - 1)] ?? _[0]);
413
- v[t + 0] = n[0], v[t + 1] = n[1], v[t + 2] = n[2], v[t + 3] = 0;
416
+ function X(e) {
417
+ let t = e.length > 0 ? e.slice(0, 4) : [], n = t.length > 0 ? t : [{
418
+ L: .62,
419
+ C: .19,
420
+ h: 34
421
+ }], r = /* @__PURE__ */ new Float32Array(64);
422
+ for (let e = 0; e < 16; e++) {
423
+ let t = e / 15 * (n.length - 1), i = Math.min(Math.floor(t), n.length - 1), a = Math.min(i + 1, n.length - 1), c = ve(n[i], n[a], t - i), l = s(o({
424
+ L: c.L,
425
+ C: Math.max(c.C, z),
426
+ h: c.h
427
+ }));
428
+ r[e * 4 + 0] = l[0], r[e * 4 + 1] = l[1], r[e * 4 + 2] = l[2], r[e * 4 + 3] = 0;
414
429
  }
430
+ let i = n[0];
431
+ return {
432
+ lut: r,
433
+ ink: s(o({
434
+ L: Math.max(0, i.L - B),
435
+ C: i.C,
436
+ h: i.h
437
+ })),
438
+ head: s(o({
439
+ L: Math.min(1, i.L + ee),
440
+ C: i.C,
441
+ h: i.h
442
+ }))
443
+ };
444
+ }
445
+ function ye(e, t) {
446
+ let { f32: n, i32: r } = e;
447
+ n[q.r0 + 0] = t.fit.centerX, n[q.r0 + 1] = t.fit.centerY, n[q.r0 + 2] = t.fit.scale, n[q.r0 + 3] = t.aspect, n[q.r1 + 0] = t.markHalfModel, n[q.r1 + 1] = t.inkOffsetStrokes, n[q.r1 + 2] = t.scaffoldFrac, n[q.r1 + 3] = t.edgeMargin, n[q.r2 + 0] = Math.min(Math.max(t.peakAlpha, 0), 1), n[q.r2 + 1] = t.glow, n[q.r2 + 2] = t.squash, n[q.r2 + 3] = t.trailFadeExp, n[q.r3 + 0] = t.trailFloor, n[q.r3 + 1] = t.hueSweep, n[q.r3 + 2] = t.ringStrokeModel, n[q.r3 + 3] = +!!t.showMachine, n[q.ink + 0] = t.ramp.ink[0], n[q.ink + 1] = t.ramp.ink[1], n[q.ink + 2] = t.ramp.ink[2], n[q.ink + 3] = +!!t.rainbowChain, n[q.head + 0] = t.ramp.head[0], n[q.head + 1] = t.ramp.head[1], n[q.head + 2] = t.ramp.head[2], n[q.head + 3] = 0, r[q.ints + 0] = t.sampleCount, r[q.ints + 1] = 16, r[q.ints + 2] = t.chainCount, r[q.ints + 3] = 0;
448
+ for (let e = 0; e < 64; e++) n[q.lut + e] = t.ramp.lut[e];
415
449
  return e;
416
450
  }
417
- function Q(e) {
418
- let t = Math.min(e.length, 64), n = /* @__PURE__ */ new Float32Array(256);
419
- for (let r = 0; r < t; r++) {
420
- let t = e[r], i = r * 4;
421
- n[i + 0] = t.coefficient[0], n[i + 1] = t.coefficient[1], n[i + 2] = t.index, n[i + 3] = 0;
451
+ function be(e) {
452
+ let t = new Float32Array(Math.max(e.length, 1) * 4);
453
+ for (let n = 0; n < e.length; n++) {
454
+ let r = e[n], i = n * 4;
455
+ t[i + 0] = r.coefficient[0], t[i + 1] = r.coefficient[1], t[i + 2] = r.index, t[i + 3] = 0;
422
456
  }
423
- return n;
457
+ return t;
424
458
  }
425
459
  //#endregion
426
- //#region src/components/fourier-field/composables/fourierFieldWGPUSetup.ts
427
- var le = 128, $ = 4, ue = 64, de = .92, fe = .95, pe = 1.35, me = .34;
428
- function he(e) {
429
- let { canvas: t, config: n, getSpectrum: r, getPalette: i, getHeadT: a, shouldContinue: o, onFrame: s } = e, c = e.getPointerLean;
430
- return function(e, l, u) {
431
- let d = e.createShaderModule({
432
- label: "[FourierField] fourier-field.compute.wgsl",
433
- code: ne
434
- }), f = e.createShaderModule({
435
- label: "[FourierField] fourier-field.render.wgsl",
436
- code: re
437
- }), p = e.createBuffer({
438
- label: "[FourierField] phasors",
439
- size: 1024,
440
- usage: 136
441
- }), m = e.createBuffer({
442
- label: "[FourierField] curve-samples",
443
- size: 384 * 16,
444
- usage: le
445
- }), h = e.createBuffer({
446
- label: "[FourierField] chain-tips",
447
- size: 1040,
448
- usage: le
449
- }), g = e.createBuffer({
460
+ //#region src/components/fourier-field/renderer/wgpu.ts
461
+ var Z = 128, Q = 4, xe = 64, Se = 1.35, Ce = .34, we = .92, $ = {
462
+ chain: 0,
463
+ trail: 1,
464
+ head: 2
465
+ };
466
+ function Te(e) {
467
+ let { canvas: t, config: n, getSpectrum: r, getPalette: i, getHeadT: a, shouldContinue: o, onFrame: s } = e;
468
+ return function(e, c, l) {
469
+ let u = e.createShaderModule({
470
+ label: "[FourierField] compute.wgsl",
471
+ code: de
472
+ }), d = e.createShaderModule({
473
+ label: "[FourierField] render.wgsl",
474
+ code: fe
475
+ }), f = e.createBuffer({
450
476
  label: "[FourierField] compute-uniforms",
451
477
  size: K().buffer.byteLength,
452
478
  usage: 72
453
- }), v = e.createBuffer({
479
+ }), p = e.createBuffer({
454
480
  label: "[FourierField] render-uniforms",
455
- size: Y().buffer.byteLength,
481
+ size: J().buffer.byteLength,
456
482
  usage: 72
457
- }), y = e.createBindGroupLayout({
483
+ }), m = e.createBindGroupLayout({
458
484
  label: "[FourierField] compute-bgl",
459
485
  entries: [
460
486
  {
461
487
  binding: 0,
462
- visibility: $,
488
+ visibility: Q,
463
489
  buffer: { type: "uniform" }
464
490
  },
465
491
  {
466
492
  binding: 1,
467
- visibility: $,
493
+ visibility: Q,
468
494
  buffer: { type: "read-only-storage" }
469
495
  },
470
496
  {
471
497
  binding: 2,
472
- visibility: $,
498
+ visibility: Q,
473
499
  buffer: { type: "storage" }
474
500
  },
475
501
  {
476
502
  binding: 3,
477
- visibility: $,
503
+ visibility: Q,
478
504
  buffer: { type: "storage" }
479
505
  }
480
506
  ]
481
- }), b = e.createComputePipeline({
507
+ }), h = e.createComputePipeline({
482
508
  label: "[FourierField] compute-pipeline",
483
- layout: e.createPipelineLayout({ bindGroupLayouts: [y] }),
509
+ layout: e.createPipelineLayout({ bindGroupLayouts: [m] }),
484
510
  compute: {
485
- module: d,
511
+ module: u,
486
512
  entryPoint: "cs_main"
487
513
  }
488
- }), x = e.createBindGroup({
489
- label: "[FourierField] compute-bg",
490
- layout: y,
491
- entries: [
492
- {
493
- binding: 0,
494
- resource: { buffer: g }
495
- },
496
- {
497
- binding: 1,
498
- resource: { buffer: p }
499
- },
500
- {
501
- binding: 2,
502
- resource: { buffer: m }
503
- },
504
- {
505
- binding: 3,
506
- resource: { buffer: h }
507
- }
508
- ]
509
- }), S = e.createBindGroupLayout({
514
+ }), g = e.createBindGroupLayout({
510
515
  label: "[FourierField] render-bgl",
511
516
  entries: [
512
517
  {
@@ -525,18 +530,7 @@ function he(e) {
525
530
  buffer: { type: "read-only-storage" }
526
531
  }
527
532
  ]
528
- }), C = e.createPipelineLayout({ bindGroupLayouts: [S] }), w = {
529
- color: {
530
- srcFactor: "one",
531
- dstFactor: "one-minus-src-alpha",
532
- operation: "add"
533
- },
534
- alpha: {
535
- srcFactor: "one",
536
- dstFactor: "one-minus-src-alpha",
537
- operation: "add"
538
- }
539
- }, T = {
533
+ }), _ = e.createPipelineLayout({ bindGroupLayouts: [g] }), v = {
540
534
  color: {
541
535
  srcFactor: "one",
542
536
  dstFactor: "one",
@@ -547,74 +541,132 @@ function he(e) {
547
541
  dstFactor: "one",
548
542
  operation: "max"
549
543
  }
550
- }, E = (t, n) => e.createRenderPipeline({
551
- label: `[FourierField] ribbon-pipeline-L${t}`,
552
- layout: C,
544
+ }, y = (t) => e.createRenderPipeline({
545
+ label: `[FourierField] pipeline-L${t}`,
546
+ layout: _,
553
547
  vertex: {
554
- module: f,
548
+ module: d,
555
549
  entryPoint: "vs_main",
556
550
  constants: { LAYER: t }
557
551
  },
558
552
  fragment: {
559
- module: f,
553
+ module: d,
560
554
  entryPoint: "fs_main",
561
555
  constants: { LAYER: t },
562
556
  targets: [{
563
- format: u,
564
- blend: n
557
+ format: l,
558
+ blend: v
565
559
  }]
566
560
  },
567
561
  primitive: { topology: "triangle-list" }
568
- }), D = {
569
- trail: E(W.trail, w),
570
- epicycle: E(W.epicycle, T),
571
- head: E(W.head, w),
572
- "cel-rope": E(W["cel-rope"], w),
573
- "cel-arm": E(W["cel-arm"], w)
574
- }, O = e.createBindGroup({
575
- label: "[FourierField] render-bg",
576
- layout: S,
577
- entries: [
578
- {
579
- binding: 0,
580
- resource: { buffer: v }
581
- },
582
- {
583
- binding: 1,
584
- resource: { buffer: m }
585
- },
586
- {
587
- binding: 2,
588
- resource: { buffer: h }
589
- }
590
- ]
591
- }), k = K(), A = Y(), j = null, M = {
562
+ }), b = {
563
+ chain: y($.chain),
564
+ trail: y($.trail),
565
+ head: y($.head)
566
+ }, x = K(), S = J(), C = null, w = null, T = null, E = null, D = null, O = null, k = {
592
567
  centerX: 0,
593
568
  centerY: 0,
594
569
  scale: 1
595
- }, N = 0;
596
- function P() {
597
- let t = r();
598
- t !== j && (j = t, N = Math.min(t.length, 64), M = X(t), e.queue.writeBuffer(p, 0, Q(t)));
570
+ }, A = n.showMachine;
571
+ function j() {
572
+ w?.destroy(), T?.destroy(), E?.destroy(), w = null, T = null, E = null;
599
573
  }
600
- function F(e) {}
601
- function I(r) {
602
- s?.(r), P();
603
- let o = a(), u = Math.max(1, Math.min(Math.round(n.harmonics), N)), d = n.showEpicycles ? Math.max(1, Math.min(Math.round(n.epicycleArms), u)) : 0;
604
- se(k, o, n.trailArc, u, d, 384, N), e.queue.writeBuffer(g, 0, k.buffer);
605
- let f = Math.min(t.width, t.height) / Math.max(_(), 1), p = t.width / Math.max(t.height, 1), m = ce(n.trailWidth, M.scale, f), h = 12 / (Math.max(M.scale, 1e-6) * Math.max(f, 1)), y = c?.() ?? {
606
- x: 0,
607
- y: 0
608
- }, S = y.x !== 0 || y.y !== 0 ? {
609
- ...M,
610
- centerX: M.centerX - y.x,
611
- centerY: M.centerY - y.y
612
- } : M;
613
- Z(A, S, p, m, de, fe, pe, me, n.intensity, n.showEpicycles, n.rainbowChain, n.squashGain, n.celGain, h, 384, d, i()), e.queue.writeBuffer(v, 0, A.buffer);
614
- let C = e.createCommandEncoder({ label: "[FourierField] frame" }), w = C.beginComputePass();
615
- w.setPipeline(b), w.setBindGroup(0, x), w.dispatchWorkgroups(Math.ceil(384 / ue)), w.end();
616
- let T = l.getCurrentTexture().createView(), E = C.beginRenderPass({ colorAttachments: [{
617
- view: T,
574
+ function M() {
575
+ let t = r(), i = A !== n.showMachine;
576
+ if (!(t === C && !i)) {
577
+ if (t !== C) {
578
+ C = t, j();
579
+ let n = Math.max(t.terms.length, 1);
580
+ w = e.createBuffer({
581
+ label: "[FourierField] phasors",
582
+ size: n * 16,
583
+ usage: 136
584
+ }), T = e.createBuffer({
585
+ label: "[FourierField] curve-samples",
586
+ size: 384 * 16,
587
+ usage: Z
588
+ }), E = e.createBuffer({
589
+ label: "[FourierField] chain-tips",
590
+ size: (n + 1) * 16,
591
+ usage: Z
592
+ }), e.queue.writeBuffer(w, 0, be(t.terms)), D = e.createBindGroup({
593
+ label: "[FourierField] compute-bg",
594
+ layout: m,
595
+ entries: [
596
+ {
597
+ binding: 0,
598
+ resource: { buffer: f }
599
+ },
600
+ {
601
+ binding: 1,
602
+ resource: { buffer: w }
603
+ },
604
+ {
605
+ binding: 2,
606
+ resource: { buffer: T }
607
+ },
608
+ {
609
+ binding: 3,
610
+ resource: { buffer: E }
611
+ }
612
+ ]
613
+ }), O = e.createBindGroup({
614
+ label: "[FourierField] render-bg",
615
+ layout: g,
616
+ entries: [
617
+ {
618
+ binding: 0,
619
+ resource: { buffer: p }
620
+ },
621
+ {
622
+ binding: 1,
623
+ resource: { buffer: T }
624
+ },
625
+ {
626
+ binding: 2,
627
+ resource: { buffer: E }
628
+ }
629
+ ]
630
+ });
631
+ }
632
+ A = n.showMachine, k = _e(C, A);
633
+ }
634
+ }
635
+ let N = X(i()), P = Ee(i());
636
+ function F() {
637
+ let e = i(), t = Ee(e);
638
+ t !== P && (P = t, N = X(e));
639
+ }
640
+ function I(e) {}
641
+ function L(r) {
642
+ if (s?.(r), M(), F(), !C || !D || !O) return;
643
+ let i = C.terms.length, o = Math.max(1, Math.min(Math.round(n.harmonics), i)), l = n.showMachine ? o : 0;
644
+ me(x, a(), n.trailArc, o, 384, i), e.queue.writeBuffer(f, 0, x.buffer);
645
+ let u = Math.min(typeof window > "u" ? 1 : window.devicePixelRatio || 1, 2), d = Math.min(t.width, t.height) / Math.max(u, 1), m = t.width / Math.max(t.height, 1), g = Y(n.markStroke, k.scale, d) * .5, _ = Y(n.markStroke, k.scale, d), v = 12 / (Math.max(k.scale, 1e-6) * Math.max(d, 1));
646
+ ye(S, {
647
+ fit: k,
648
+ aspect: m,
649
+ markHalfModel: g,
650
+ inkOffsetStrokes: n.inkOffset,
651
+ scaffoldFrac: ne,
652
+ edgeMargin: v,
653
+ peakAlpha: we * n.intensity,
654
+ glow: n.glow,
655
+ squash: n.squash,
656
+ trailFadeExp: Se,
657
+ trailFloor: Ce,
658
+ hueSweep: te,
659
+ ringStrokeModel: _,
660
+ showMachine: n.showMachine,
661
+ rainbowChain: n.rainbowChain,
662
+ sampleCount: 384,
663
+ chainCount: l,
664
+ ramp: N
665
+ }), e.queue.writeBuffer(p, 0, S.buffer);
666
+ let y = e.createCommandEncoder({ label: "[FourierField] frame" }), w = y.beginComputePass();
667
+ w.setPipeline(h), w.setBindGroup(0, D), w.dispatchWorkgroups(Math.ceil(384 / xe)), w.end();
668
+ let T = y.beginRenderPass({ colorAttachments: [{
669
+ view: c.getCurrentTexture().createView(),
618
670
  clearValue: {
619
671
  r: 0,
620
672
  g: 0,
@@ -624,484 +676,174 @@ function he(e) {
624
676
  loadOp: "clear",
625
677
  storeOp: "store"
626
678
  }] });
627
- E.setBindGroup(0, O);
628
- let j = ie({
629
- sampleCount: 384,
630
- armCount: d,
631
- showEpicycles: n.showEpicycles,
632
- celGain: n.celGain
633
- });
634
- for (let e of j) E.setPipeline(D[e.kind]), E.draw(6, e.instanceCount, 0, 0);
635
- E.end(), e.queue.submit([C.finish()]);
679
+ T.setBindGroup(0, O), l > 0 && (T.setPipeline(b.chain), T.draw(6, l, 0, 0)), T.setPipeline(b.trail), T.draw(6, 383, 0, 0), T.setPipeline(b.head), T.draw(6, 1, 0, 0), T.end(), e.queue.submit([y.finish()]);
636
680
  }
637
681
  return {
638
- frame: I,
682
+ frame: L,
639
683
  shouldContinue: o,
640
- resize: F,
684
+ resize: I,
641
685
  teardown: () => {
642
- p.destroy(), m.destroy(), h.destroy(), g.destroy(), v.destroy();
686
+ j(), f.destroy(), p.destroy();
643
687
  }
644
688
  };
645
689
  };
646
690
  }
647
- //#endregion
648
- //#region src/components/fourier-field/shaders/fourier-field.glsl.ts
649
- var ge = "#version 300 es\n// Must equal the WGSL twin and FOURIER_TANGENT_EPS in constants.ts.\n#define RIBBON_UNDERGLOW_SCALE 2.2\n\nin vec2 aCorner; // the unit quad corner ∈ [0,1]²\nin vec4 aSeg; // (ax, ay, bx, by) — segment endpoints in MODEL space\nin vec4 aData; // (age, strokeHalf, arg2, _pad) — strokeHalf/age/arg2 per layer\n\nuniform vec4 uFit; // (centerX, centerY, scale, aspect) — model→clip fit\nuniform int uLayer; // 0 trail · 1 epicycle · 2 head · 3 cel-rope · 4 cel-arm\nuniform float uEdgeMargin; // AA-feather slop in MODEL units (a few CSS px), padding the bbox\n\nout vec2 vP; // the MODEL-space fragment coordinate (the fs runs the SDF on it)\nout vec2 vA;\nout vec2 vB;\nout float vAge;\nout float vHalf;\nout float vArg2;\n\nvoid main() {\n vec2 a = aSeg.xy;\n vec2 b = aSeg.zw;\n float strokeHalf = aData.y;\n vA = a; vB = b; vAge = aData.x; vHalf = strokeHalf; vArg2 = aData.z;\n\n vec2 pos;\n if (uLayer == 1 || uLayer == 2) {\n // AABB around a — the epicycle orbit RING (radius = |b-a|) or the head HALO.\n float ext = (uLayer == 1) ? (length(b - a) + strokeHalf * 0.7) : (strokeHalf * 4.5);\n ext += uEdgeMargin;\n pos = a + (aCorner * 2.0 - 1.0) * ext;\n } else {\n // Capsule along a→b padded by the covered extent (trail under-glow, cel stroke).\n float ext = (uLayer == 0) ? (strokeHalf * RIBBON_UNDERGLOW_SCALE) : strokeHalf;\n ext += uEdgeMargin;\n vec2 d = b - a;\n float len = length(d);\n vec2 dir = len > 1e-6 ? d / len : vec2(1.0, 0.0);\n vec2 perp = vec2(-dir.y, dir.x);\n vec2 along = mix(a - dir * ext, b + dir * ext, aCorner.x);\n pos = along + perp * (aCorner.y * 2.0 - 1.0) * ext;\n }\n\n vP = pos;\n vec2 center = uFit.xy;\n float scale = max(uFit.z, 1e-6);\n float aspect = max(uFit.w, 1e-4);\n // model → clip (the inverse of the fs's p = center + vClip·aspect, scale).\n float clipX = (pos.x - center.x) * scale / aspect;\n float clipY = (pos.y - center.y) * scale;\n gl_Position = vec4(clipX, clipY, 0.0, 1.0);\n}\n", _e = `#version 300 es
650
- precision highp float;
651
-
652
- #define MAX_FOURIER_STOPS 4
653
- // Must equal the WGSL twin and FOURIER_TANGENT_EPS in constants.ts.
654
- #define TANGENT_EPS 1e-4
655
- #define SPEED_REF_HALFWIDTHS 6.0
656
- // The thick luminous ribbon mirrors RIBBON_TAIL_FRAC verbatim
657
- // from constants.ts because the GLSL twin cannot import a TS const; parity coverage verifies the
658
- // two copies are equal so the mid-body floor (RIBBON_HEAD_FLOOR_PX) can never drift off it.
659
- #define RIBBON_TAIL_FRAC 0.4
660
- #define RIBBON_UNDERGLOW_SCALE 2.2
661
- #define RIBBON_UNDERGLOW_ALPHA 0.16
662
-
663
- in vec2 vP;
664
- in vec2 vA;
665
- in vec2 vB;
666
- in float vAge;
667
- in float vHalf;
668
- in float vArg2;
669
- out vec4 fragColor;
670
-
671
- uniform vec4 uTrail; // (trailWidth, peakAlpha, headGlowAlpha, trailFadeExp)
672
- uniform vec4 uEnv; // (trailFloor, intensity, showEpicycles, rainbowChain)
673
- uniform vec4 uLoom; // (squashGain, celGain, _pad, _pad) — FOURIER-LOOM §2b/§3b
674
- uniform int uLayer; // 0 trail · 1 epicycle · 2 head · 3 cel-rope · 4 cel-arm
675
- uniform int uStopCount;
676
- uniform vec3 uPalette[MAX_FOURIER_STOPS]; // linear-sRGB stops
677
-
678
- const float PI = 3.141592653589793;
679
-
680
- ${y}
681
- ${x}
682
-
683
- // Negative-safe cube root (the WGSL chunk's cbrt3 twin; the GLSL chunk inlines it).
684
- vec3 cbrt3(vec3 v) { return sign(v) * pow(abs(v), vec3(1.0 / 3.0)); }
685
-
686
- vec3 samplePaletteLin(float t) {
687
- if (uStopCount <= 1) return uPalette[0];
688
- float n = float(uStopCount);
689
- float ft = clamp(t, 0.0, 1.0) * (n - 1.0);
690
- int i0 = int(floor(ft));
691
- int i1 = min(i0 + 1, uStopCount - 1);
692
- float f = ft - float(i0);
693
- vec3 labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * uPalette[i0]);
694
- vec3 labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * uPalette[i1]);
695
- return oklabToLinearSrgb(mix(labA, labB, f));
691
+ function Ee(e) {
692
+ let t = "";
693
+ for (let n of e) t += `${n.L.toFixed(4)},${n.C.toFixed(4)},${n.h.toFixed(2)};`;
694
+ return t;
696
695
  }
697
-
698
- vec3 chainColorLin(float seg, bool rainbow) {
699
- vec3 baseLin = uPalette[0];
700
- if (!rainbow) return baseLin;
701
- vec3 lab = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * baseLin);
702
- vec3 lch = oklabToOklch(lab);
703
- float hueShift = mix(-0.45, 1.15, clamp(seg, 0.0, 1.0));
704
- vec3 lifted = vec3(0.66, max(lch.y, 0.14), lch.z + hueShift);
705
- return oklabToLinearSrgb(oklchToOklab(lifted));
706
- }
707
-
708
- float segDist(vec2 p, vec2 a, vec2 b) {
709
- vec2 pa = p - a;
710
- vec2 ba = b - a;
711
- float h = clamp(dot(pa, ba) / max(dot(ba, ba), 1e-8), 0.0, 1.0);
712
- return length(pa - ba * h);
713
- }
714
-
715
- // The head travel frame (T, ŝ), the GLSL twin of the WGSL headFrame.
716
- struct HeadFrame { vec2 T; float sHat; };
717
- HeadFrame headFrame(vec2 head, vec2 headBack, float halfW) {
718
- vec2 d = head - headBack;
719
- float s = length(d);
720
- HeadFrame fr;
721
- if (s <= TANGENT_EPS) { fr.T = vec2(1.0, 0.0); fr.sHat = 0.0; return fr; }
722
- fr.T = d / s;
723
- float refScale = max(halfW * SPEED_REF_HALFWIDTHS, 1e-6);
724
- fr.sHat = clamp(s / refScale, 0.0, 1.0);
725
- return fr;
726
- }
727
-
728
- // The volume-preserving anisotropic head distance (the WGSL headAniso twin).
729
- float headAniso(vec2 p, vec2 head, HeadFrame fr, float k) {
730
- float g = 1.0 + k * fr.sHat;
731
- vec2 n = vec2(-fr.T.y, fr.T.x);
732
- vec2 rel = p - head;
733
- float along = dot(rel, fr.T) / g;
734
- float across = dot(rel, n) * g;
735
- return length(vec2(along, across));
736
- }
737
-
738
- void main() {
739
- vec2 p = vP;
740
- float aa = max(fwidth(p.x) + fwidth(p.y), 1e-5) * 0.75;
741
- float peak = uTrail.y * uEnv.y;
742
- float headGlow = uTrail.z * uEnv.y;
743
- float fadeExp = uTrail.w;
744
- float trailFloor = uEnv.x;
745
- bool rainbow = uEnv.w > 0.5;
746
- float squashGain = uLoom.x;
747
- float celGain = uLoom.y;
748
- float halfW = vHalf;
749
-
750
- if (uLayer == 0) {
751
- // ── TRAIL — the tapered luminous ribbon over its own soft under-glow ──
752
- float age = vAge;
753
- float d = segDist(p, vA, vB);
754
- // Head→tail taper: the ribbon half-width scales with age (thick head, thin tail).
755
- float ribbonHalf = halfW * (RIBBON_TAIL_FRAC + (1.0 - RIBBON_TAIL_FRAC) * age);
756
- vec3 lin = samplePaletteLin(1.0 - age * 0.4);
757
- vec3 rgb = clamp(linearToSrgb(lin), vec3(0.0), vec3(1.0));
758
- // Soft under-glow: a wider, low-alpha copy under the crisp ribbon (one color event).
759
- float glowHalf = ribbonHalf * RIBBON_UNDERGLOW_SCALE;
760
- float glowCover = 1.0 - smoothstep(glowHalf, glowHalf + aa * 3.0, d);
761
- float ga = peak * RIBBON_UNDERGLOW_ALPHA * pow(age, fadeExp) * glowCover;
762
- // The crisp tapered ribbon core.
763
- float cover = 1.0 - smoothstep(ribbonHalf, ribbonHalf + aa, d);
764
- float ca = max(peak * pow(age, fadeExp), peak * trailFloor) * cover;
765
- // core OVER glow OVER transparent (both premultiplied on the SAME rgb → out = rgb·outA).
766
- float outA = ca + ga * (1.0 - ca);
767
- fragColor = vec4(rgb * outA, outA);
768
- } else if (uLayer == 1) {
769
- // ── EPICYCLE — orbit RING + ARM + joint DOT union (MAX-blended, no join seam) ──
770
- float seg = vArg2;
771
- vec3 lin = chainColorLin(seg, rainbow);
772
- vec3 rgb = clamp(linearToSrgb(lin), vec3(0.0), vec3(1.0));
773
- float radius = length(vB - vA);
774
- float dRing = abs(length(p - vA) - radius);
775
- float ringW = max(halfW * 0.45, aa);
776
- float ringMask = (1.0 - smoothstep(ringW, ringW + aa, dRing)) * 0.5;
777
- float dArm = segDist(p, vA, vB);
778
- float armMask = 1.0 - smoothstep(halfW * 0.55, halfW * 0.55 + aa, dArm);
779
- float dotR = max(halfW * 0.7, aa * 2.0);
780
- float dDot = length(p - vA);
781
- float dotMask = 1.0 - smoothstep(dotR, dotR + aa, dDot);
782
- float m = clamp(max(max(ringMask, armMask), dotMask), 0.0, 1.0) * peak * 0.7;
783
- fragColor = vec4(rgb * m, m);
784
- } else if (uLayer == 2) {
785
- // ── HEAD — squash-and-stretch (§2b): halo + saturated core + white specular ──
786
- vec2 head = vA;
787
- vec2 headBack = vB;
788
- HeadFrame fr = headFrame(head, headBack, halfW);
789
- float dHead = headAniso(p, head, fr, squashGain);
790
- float coreR = halfW * 1.5;
791
- float haloR = coreR * 3.0;
792
- vec3 headRgb = clamp(linearToSrgb(samplePaletteLin(0.0)), vec3(0.0), vec3(1.0));
793
- float halo = (1.0 - smoothstep(0.0, haloR, dHead)) * headGlow * 0.35;
794
- vec4 accum = vec4(headRgb * halo, halo);
795
- float core = (1.0 - smoothstep(coreR, coreR + aa, dHead)) * headGlow;
796
- accum = vec4(headRgb * core, core) + accum * (1.0 - core);
797
- float spec = (1.0 - smoothstep(coreR * 0.4, coreR * 0.4 + aa, dHead)) * headGlow * 0.9;
798
- accum = vec4(vec3(1.0) * spec, spec) + accum * (1.0 - spec);
799
- fragColor = accum;
800
- } else {
801
- // ── CEL — the technicolor ink shadow (rope uLayer==3, arm uLayer==4) ──
802
- float celA = clamp(celGain, 0.0, 1.0);
803
- float d = segDist(p, vA, vB);
804
- float cover = 1.0 - smoothstep(halfW, halfW + aa, d);
805
- float m = cover * celA * peak * vArg2; // vArg2 = celStrength (1.0 rope / 0.7 arm)
806
- fragColor = vec4(0.0, 0.0, 0.0, m);
807
- }
808
- }
809
- `, ve = 256, ye = 64, be = .92, xe = .95, Se = 1.35, Ce = .34;
810
- function we(e, t, n) {
811
- let r = e.createShader(t);
812
- if (e.shaderSource(r, n), e.compileShader(r), !e.getShaderParameter(r, e.COMPILE_STATUS)) {
813
- let t = e.getShaderInfoLog(r);
814
- throw e.deleteShader(r), Error(`[FourierField] shader compile failed: ${t}`);
815
- }
816
- return r;
817
- }
818
- function Te(e) {
819
- let { canvas: t, config: n, getSpectrum: r, getPalette: i, getHeadT: a, shouldContinue: o, onFrame: s } = e, l = e.getPointerLean;
820
- return function(e) {
821
- let u = we(e, e.VERTEX_SHADER, ge), d = we(e, e.FRAGMENT_SHADER, _e), f = e.createProgram();
822
- if (e.attachShader(f, u), e.attachShader(f, d), e.linkProgram(f), !e.getProgramParameter(f, e.LINK_STATUS)) throw Error(`[FourierField] link failed: ${e.getProgramInfoLog(f)}`);
823
- e.deleteShader(u), e.deleteShader(d);
824
- let p = e.createVertexArray();
825
- e.bindVertexArray(p);
826
- let m = e.createBuffer();
827
- e.bindBuffer(e.ARRAY_BUFFER, m), e.bufferData(e.ARRAY_BUFFER, ae, e.STATIC_DRAW);
828
- let g = e.getAttribLocation(f, "aCorner");
829
- e.enableVertexAttribArray(g), e.vertexAttribPointer(g, 2, e.FLOAT, !1, 0, 0), e.vertexAttribDivisor(g, 0);
830
- let v = new Float32Array(639 * 8), y = e.createBuffer();
831
- e.bindBuffer(e.ARRAY_BUFFER, y), e.bufferData(e.ARRAY_BUFFER, v.byteLength, e.DYNAMIC_DRAW);
832
- let b = e.getAttribLocation(f, "aSeg"), x = e.getAttribLocation(f, "aData");
833
- e.enableVertexAttribArray(b), e.enableVertexAttribArray(x), e.vertexAttribPointer(b, 4, e.FLOAT, !1, 32, 0), e.vertexAttribPointer(x, 4, e.FLOAT, !1, 32, 16), e.vertexAttribDivisor(b, 1), e.vertexAttribDivisor(x, 1), e.bindVertexArray(null);
834
- let S = (t) => e.getUniformLocation(f, t), C = S("uFit"), w = S("uTrail"), T = S("uEnv"), E = S("uLoom"), D = S("uLayer"), O = S("uEdgeMargin"), k = S("uStopCount"), A = S("uPalette[0]"), j = new Float32Array(ve * 3), M = /* @__PURE__ */ new Float32Array(130), N = /* @__PURE__ */ new Float32Array(12), P = null, F = {
835
- centerX: 0,
836
- centerY: 0,
837
- scale: 1
838
- }, I = 0;
839
- function L() {
840
- let e = r();
841
- e !== P && (P = e, I = Math.min(e.length, ye), F = X(e));
842
- }
843
- function R(t) {
844
- e.viewport(0, 0, t.w, t.h);
845
- }
846
- function z(r) {
847
- s?.(r), L();
848
- let o = P;
849
- if (!o) return;
850
- let u = a(), d = Math.max(1, Math.min(Math.round(n.harmonics), I)), m = n.showEpicycles ? Math.max(1, Math.min(Math.round(n.epicycleArms), d)) : 0, g = ve, S = Math.max(g - 1, 1);
851
- for (let e = 0; e < g; e++) {
852
- let t = e / S, r = 1 - t, i = u - n.trailArc * t;
853
- i -= Math.floor(i);
854
- let [a, s] = h(o, i, d), c = e * 3;
855
- j[c + 0] = a, j[c + 1] = s, j[c + 2] = r;
856
- }
857
- let R = Math.min(m, d);
858
- for (let e = 0; e <= ye; e++) {
859
- let t = e * 2;
860
- if (e <= R) {
861
- let [n, r] = h(o, u, e);
862
- M[t + 0] = n, M[t + 1] = r;
863
- } else M[t + 0] = 0, M[t + 1] = 0;
864
- }
865
- let z = t.width / Math.max(t.height, 1), B = Math.min(t.width, t.height) / Math.max(_(), 1), V = ce(n.trailWidth, F.scale, B), H = V * .5, ee = 12 / (Math.max(F.scale, 1e-6) * Math.max(B, 1)), U = j[0], te = j[1], ne = j[3], re = j[4], ae = U - ne, oe = te - re, G = Math.hypot(ae, oe), K = G > 1e-4 ? ae / G : 1, se = G > 1e-4 ? oe / G : 0, q = -K * H * 1.4, J = -se * H * 1.4, Y = ie({
866
- sampleCount: g,
867
- armCount: m,
868
- showEpicycles: n.showEpicycles,
869
- celGain: n.celGain
870
- }), X = /* @__PURE__ */ new Map(), Z = 0, Q = (e, t, n, r, i, a, o) => {
871
- v[Z++] = e, v[Z++] = t, v[Z++] = n, v[Z++] = r, v[Z++] = i, v[Z++] = a, v[Z++] = o, v[Z++] = 0;
872
- };
873
- for (let e of Y) if (X.set(e.kind, Z), e.kind === "cel-rope") for (let e = 0; e < g - 1; e++) {
874
- let t = e * 3;
875
- Q(j[t] + q, j[t + 1] + J, j[t + 3] + q, j[t + 4] + J, 0, H, 1);
876
- }
877
- else if (e.kind === "cel-arm") for (let e = 0; e < m; e++) {
878
- let t = e * 2;
879
- Q(M[t] + q, M[t + 1] + J, M[t + 2] + q, M[t + 3] + J, 0, H * .55, .7);
880
- }
881
- else if (e.kind === "epicycle") {
882
- let e = Math.max(m - 1, 1);
883
- for (let t = 0; t < m; t++) {
884
- let n = t * 2;
885
- Q(M[n], M[n + 1], M[n + 2], M[n + 3], 0, H, t / e);
886
- }
887
- } else if (e.kind === "trail") for (let e = 0; e < g - 1; e++) {
888
- let t = e * 3, n = Math.max(j[t + 2], j[t + 5]);
889
- Q(j[t], j[t + 1], j[t + 3], j[t + 4], n, H, 0);
890
- }
891
- else Q(j[0], j[1], j[3], j[4], 0, H, 0);
892
- let le = Z, $ = i(), ue = Math.min($.length, 4);
893
- for (let e = 0; e < 4; e++) {
894
- let t = c($[Math.min(e, ue - 1)] ?? $[0]);
895
- N[e * 3 + 0] = t[0], N[e * 3 + 1] = t[1], N[e * 3 + 2] = t[2];
896
- }
897
- e.useProgram(f), e.bindVertexArray(p), e.enable(e.BLEND), e.clearColor(0, 0, 0, 0), e.clear(e.COLOR_BUFFER_BIT), e.bindBuffer(e.ARRAY_BUFFER, y), e.bufferSubData(e.ARRAY_BUFFER, 0, v.subarray(0, le));
898
- let de = l?.() ?? {
899
- x: 0,
900
- y: 0
901
- };
902
- e.uniform4f(C, F.centerX - de.x, F.centerY - de.y, F.scale, z), e.uniform4f(w, V, be, xe, Se), e.uniform4f(T, Ce, n.intensity, +!!n.showEpicycles, +!!n.rainbowChain), e.uniform4f(E, n.squashGain, n.celGain, 0, 0), e.uniform1f(O, ee), e.uniform1i(k, ue), e.uniform3fv(A, N);
903
- for (let t of Y) {
904
- let n = (X.get(t.kind) ?? 0) * 4;
905
- e.vertexAttribPointer(b, 4, e.FLOAT, !1, 32, n), e.vertexAttribPointer(x, 4, e.FLOAT, !1, 32, n + 16), e.uniform1i(D, W[t.kind]), t.blend === "max" ? (e.blendEquation(e.MAX), e.blendFunc(e.ONE, e.ONE)) : (e.blendEquation(e.FUNC_ADD), e.blendFunc(e.ONE, e.ONE_MINUS_SRC_ALPHA)), e.drawArraysInstanced(e.TRIANGLE_STRIP, 0, 4, t.instanceCount);
906
- }
907
- e.blendEquation(e.FUNC_ADD);
908
- }
909
- return {
910
- frame: z,
911
- shouldContinue: o,
912
- resize: R,
913
- teardown: () => {
914
- e.deleteProgram(f), e.deleteBuffer(m), e.deleteBuffer(y), e.deleteVertexArray(p);
915
- }
916
- };
696
+ var De = "[FourierField] WebGPU is required. This field paints the transform itself, so it ships one renderer and no lookalike — where WebGPU is absent it declares the failure and paints nothing.";
697
+ function Oe() {
698
+ return function() {
699
+ throw Error(De);
917
700
  };
918
701
  }
919
702
  //#endregion
920
- //#region src/components/fourier-field/composables/useFourierField.ts
921
- function Ee(e, t) {
922
- let { config: r, getSpectrum: o, getPalette: s } = t, c = t.mode ?? "live", l = t.frozenT ?? .34, u = t.periodS ?? 16, d = 0, f = -1, p = 0, m = 0;
923
- function h(e) {
924
- if (p === 0 && m === 0) return;
925
- let t = -64 * p - 2 * .62 * 8 * m;
926
- m += t * e, p += m * e, Math.abs(p) < 1e-4 && Math.abs(m) < 1e-4 && (p = 0, m = 0);
927
- }
928
- let g = n({ respectReducedMotion: r.respectReducedMotion }), v = () => (t.freeze?.() ?? !1) || (I?.reducedMotion ?? !1);
929
- function y(e) {
930
- b(e);
931
- }
932
- function b(e) {
933
- let n = f >= 0 ? (e - f) * 1e3 : 16.7;
934
- f = e;
935
- let i = t.routePointer?.();
936
- if (t.routePointer && (g.setActive(i?.active ?? !1), i?.active && g.setPointer(i.x, i.y)), g.tick(n), v()) {
937
- d = l, p = 0, m = 0;
938
- return;
939
- }
940
- let a = Math.min(Math.max(n / 1e3, 0), .05);
941
- if (r.interactive && g.active.value) {
942
- let e = r.speed / u + g.velocity.value.x * B + p;
943
- d = (d + e * a) % 1, d < 0 && (d += 1);
944
- let t = g.burst.value * 4;
945
- t !== 0 && (p = t, m = 0), h(a);
946
- return;
947
- }
948
- let o = r.speed / u + p;
949
- d = (d + o * a) % 1, d < 0 && (d += 1), h(a);
950
- }
951
- let x = () => d, w = null, T = {
952
- centerX: 0,
953
- centerY: 0,
954
- scale: 1
955
- }, E = () => {
956
- let e = o();
957
- return e !== w && (w = e, T = X(e)), T;
958
- }, D = () => {
959
- if (!r.interactive) return {
960
- x: 0,
961
- y: 0
962
- };
963
- let t = e.value;
964
- if (!t || !t.width || !t.height) return {
965
- x: 0,
966
- y: 0
967
- };
968
- let { leanX: n, leanY: o } = a(i(g), {
969
- aspect: t.width / t.height,
970
- scale: Math.max(E().scale, 1e-6)
703
+ //#region src/components/fourier-field/useFourierField.ts
704
+ function ke(e, t) {
705
+ let { config: n, getSpectrum: i, getPalette: a } = t, o = t.mode ?? "live", s = t.interactive, c = ue(), l = -1, u = j(0), d = -Infinity, f = r({ respectReducedMotion: n.respectReducedMotion }), p = () => (t.freeze?.() ?? !1) || (C?.reducedMotion ?? !1);
706
+ function m(e) {
707
+ let t = l >= 0 ? (e - l) * 1e3 : 16.7;
708
+ l = e, f.tick(t);
709
+ let r = s() && f.active.value;
710
+ c.step({
711
+ dt: t / 1e3,
712
+ speed: n.speed,
713
+ scrubVelocity: r ? f.velocity.value.x : 0,
714
+ burst: r ? f.burst.value : 0,
715
+ engaged: r,
716
+ frozen: p()
971
717
  });
972
- return {
973
- x: n,
974
- y: o
975
- };
976
- }, O = (e) => {
977
- g.onPointerMove(e), r.interactive && I?.wake();
978
- }, k = () => {
979
- g.onPointerEnter(), r.interactive && I?.wake();
980
- }, A = (e) => e.parentElement ?? e;
981
- function j(e) {
982
- let t = A(e);
983
- t.addEventListener("pointermove", O), t.addEventListener("pointerenter", k), t.addEventListener("pointerleave", g.onPointerLeave);
718
+ let i = e * 1e3;
719
+ i - d >= 100 && (d = i, u.value = c.headT);
984
720
  }
985
- function N(e) {
986
- let t = A(e);
987
- t.removeEventListener("pointermove", O), t.removeEventListener("pointerenter", k), t.removeEventListener("pointerleave", g.onPointerLeave);
721
+ let h = (e) => {
722
+ f.onPointerMove(e), s() && C?.wake();
723
+ }, _ = () => {
724
+ f.onPointerEnter(), s() && C?.wake();
725
+ }, v = () => {
726
+ f.onPointerLeave();
727
+ }, b = (e) => e.parentElement ?? e;
728
+ function x(e) {
729
+ let t = b(e);
730
+ t.addEventListener("pointermove", h), t.addEventListener("pointerenter", _), t.addEventListener("pointerleave", v);
988
731
  }
989
- let I = null, L = F(C("webgpu")), R = !1, z = () => {
990
- if (R) return null;
732
+ function S(e) {
733
+ let t = b(e);
734
+ t.removeEventListener("pointermove", h), t.removeEventListener("pointerenter", _), t.removeEventListener("pointerleave", v);
735
+ }
736
+ let C = null, w = j(y("webgpu")), T = (e) => e.engine === "webgpu" ? e : {
737
+ ...e,
738
+ engine: "webgpu",
739
+ adapter: "WebGPU unavailable"
740
+ }, E = !1, D = () => {
741
+ if (E) return null;
991
742
  let t = e.value;
992
- return t ? (I || (j(t), I = S(t, {
993
- mode: c,
994
- respectReducedMotion: r.respectReducedMotion,
995
- dprPolicy: _,
743
+ return t ? (C || (x(t), C = g(t, {
744
+ mode: o,
745
+ respectReducedMotion: n.respectReducedMotion,
746
+ dprPolicy: () => 2,
747
+ composeIntersectionPark: !0,
996
748
  revealBloom: !0,
997
- setupWGPU: he({
749
+ setupWGPU: Te({
998
750
  canvas: t,
999
- config: r,
1000
- getSpectrum: o,
1001
- getPalette: s,
1002
- getHeadT: x,
751
+ config: n,
752
+ getSpectrum: i,
753
+ getPalette: a,
754
+ getHeadT: () => c.headT,
1003
755
  shouldContinue: () => !0,
1004
- onFrame: y,
1005
- getPointerLean: D
756
+ onFrame: m
1006
757
  }),
1007
- setupGL: Te({
1008
- canvas: t,
1009
- config: r,
1010
- getSpectrum: o,
1011
- getPalette: s,
1012
- getHeadT: x,
1013
- shouldContinue: () => !0,
1014
- onFrame: y,
1015
- getPointerLean: D
1016
- }),
1017
- onStatus: (e) => L.value = e
1018
- }), I.armAsync().catch(() => void 0)), I) : null;
758
+ setupGL: Oe(),
759
+ onStatus: (e) => w.value = T(e)
760
+ }), C.armAsync().catch(() => void 0)), C) : null;
1019
761
  };
1020
- queueMicrotask(() => z());
1021
- let V = {
762
+ queueMicrotask(() => D());
763
+ let k = {
1022
764
  get backend() {
1023
- return z()?.backend ?? "webgpu";
765
+ return D()?.backend ?? "webgpu";
1024
766
  },
1025
- pause: () => z()?.suspend("manual"),
1026
- resume: () => z()?.resume("manual"),
1027
- wake: () => z()?.wake(),
1028
- renderAt: (e) => z()?.renderAt(e),
767
+ pause: () => D()?.suspend("manual"),
768
+ resume: () => D()?.resume("manual"),
769
+ wake: () => D()?.wake(),
1029
770
  get reducedMotion() {
1030
- return I?.reducedMotion ?? !1;
771
+ return C?.reducedMotion ?? !1;
1031
772
  },
1032
773
  get headT() {
1033
- return d;
774
+ return c.headT;
1034
775
  },
1035
- rendererStatus: P(L),
776
+ headTLive: A(u),
777
+ rendererStatus: A(w),
1036
778
  setHeadT: (e) => {
1037
- d = (e % 1 + 1) % 1, z()?.wake();
779
+ c.set(e), u.value = c.headT, D()?.wake();
780
+ },
781
+ flick: (e) => {
782
+ c.flick(e), D()?.wake();
1038
783
  },
1039
784
  dispose: () => {
1040
- if (R) return;
1041
- R = !0;
785
+ if (E) return;
786
+ E = !0;
1042
787
  let t = e.value;
1043
- t && N(t), g.dispose(), I?.dispose();
788
+ t && S(t), f.dispose(), C?.dispose();
1044
789
  }
1045
790
  };
1046
- return M(() => V.dispose()), V;
791
+ return O(() => k.dispose()), k;
1047
792
  }
1048
793
  //#endregion
1049
- //#region src/components/fourier-field/FourierField.vue
1050
- var De = /*#__PURE__*/ t(/* @__PURE__ */ k({
794
+ //#region src/components/fourier-field/FourierField.vue?vue&type=script&setup=true&lang.ts
795
+ var Ae = [
796
+ "role",
797
+ "tabindex",
798
+ "aria-label",
799
+ "aria-valuemin",
800
+ "aria-valuemax",
801
+ "aria-valuenow",
802
+ "aria-valuetext"
803
+ ], je = /*#__PURE__*/ n(/* @__PURE__ */ T({
1051
804
  __name: "FourierField",
1052
805
  props: {
1053
806
  config: {},
1054
807
  spectrum: {},
1055
808
  getPalette: {},
1056
809
  color: {},
1057
- colorResolver: {},
1058
810
  seed: { default: "" },
1059
811
  freeze: {
1060
812
  type: Boolean,
1061
813
  default: !1
814
+ },
815
+ interactive: {
816
+ type: Boolean,
817
+ default: !0
1062
818
  }
1063
819
  },
1064
820
  emits: ["rendererStatus"],
1065
- setup(t, { expose: n, emit: i }) {
1066
- let a = i, c = t, u = I("hostRef"), d = I("canvasRef"), { isDark: f } = o(), h = r(), g = E(() => {
1067
- let e = c.config ?? te, t = !c.config || e.interactive;
1068
- return {
1069
- ...e,
1070
- interactive: t
1071
- };
1072
- }), _ = E(() => !!c.config && g.value.interactive), v = c.config ? void 0 : () => ({
1073
- x: h.pointer.value.x,
1074
- y: h.pointer.value.y,
1075
- active: h.active.value
1076
- }), y = E(() => {
1077
- if (c.spectrum && c.spectrum.length > 0) return c.spectrum;
1078
- let e = l[g.value.source];
1079
- return e ? m(e) : p(w(T((c.freeze ? "fourier-field/frozen" : c.seed || "fourier-field") + c.seed)), {
1080
- harmonics: Math.min(g.value.harmonics + 4, 62),
1081
- harmonicScale: g.value.harmonicScale
1082
- });
1083
- }), b = () => y.value;
1084
- function x(t) {
1085
- if (!t.includes("var(") && !t.includes("light-dark(")) return t;
1086
- let n = u.value;
1087
- if (typeof window > "u" || !n) return t;
1088
- if (t.includes("var(")) return e(t, n);
821
+ setup(e, { expose: n, emit: r }) {
822
+ let o = r, s = e, l = M("hostRef"), u = M("canvasRef"), { isDark: d } = i(), m = S(() => s.config ?? H), h = S(() => {
823
+ if (s.spectrum && s.spectrum.length > 0) return L(s.spectrum);
824
+ let e = m.value.source;
825
+ if (e !== "elliptic" && !(e in c)) throw Error(`[FourierField] unknown source "${String(e)}". Pass "elliptic", a key of FOURIER_FIGURES, or an explicit \`spectrum\`.`);
826
+ return L(e === "elliptic" ? f(b(x(`fourier-field/${s.seed}`)), m.value.richness) : p(c[e]));
827
+ }), g = () => h.value, _ = S(() => h.value.terms.length), v = S(() => Math.max(1, Math.min(Math.round(m.value.harmonics), _.value))), y = S(() => `N ${v.value}/${_.value}`);
828
+ function T(e) {
829
+ if (!e.includes("var(") && !e.includes("light-dark(")) return e;
830
+ let n = l.value;
831
+ if (typeof window > "u" || !n) return e;
832
+ if (e.includes("var(")) return t(e, n);
1089
833
  let r = n.style.color;
1090
- n.style.color = t;
834
+ n.style.color = e;
1091
835
  let i = getComputedStyle(n).color;
1092
- return n.style.color = r, i || t;
836
+ return n.style.color = r, i || e;
1093
837
  }
1094
- let S = F(g.value.palette);
1095
- function C() {
1096
- if (c.getPalette) {
1097
- S.value = c.getPalette();
1098
- return;
1099
- }
1100
- if (f.value, c.color) {
1101
- let e = x(c.color);
838
+ let O = j(m.value.palette);
839
+ function A() {
840
+ if (s.getPalette) O.value = s.getPalette();
841
+ else if (s.color) {
842
+ d.value;
843
+ let e = T(s.color);
1102
844
  if (e.includes("var(") || e.includes("light-dark(")) return;
1103
- let t = s(e);
1104
- S.value = [{
845
+ let t = a(e);
846
+ O.value = [{
1105
847
  L: Math.max(.5, t.L),
1106
848
  C: Math.max(.12, t.C),
1107
849
  h: t.h
@@ -1110,50 +852,91 @@ var De = /*#__PURE__*/ t(/* @__PURE__ */ k({
1110
852
  C: t.C * .4,
1111
853
  h: t.h + 18
1112
854
  }];
1113
- return;
1114
- }
1115
- S.value = g.value.palette;
855
+ } else O.value = m.value.palette;
856
+ if (O.value.length === 0) throw Error("[FourierField] the palette is empty — there is nothing to paint with.");
1116
857
  }
1117
- let k = Ee(d, {
858
+ let P = ke(u, {
1118
859
  config: new Proxy({}, {
1119
- get: (e, t) => Reflect.get(g.value, t),
1120
- has: (e, t) => Reflect.has(g.value, t),
1121
- ownKeys: () => Reflect.ownKeys(g.value),
1122
- getOwnPropertyDescriptor: (e, t) => Reflect.getOwnPropertyDescriptor(g.value, t)
860
+ get: (e, t) => Reflect.get(m.value, t),
861
+ has: (e, t) => Reflect.has(m.value, t),
862
+ ownKeys: () => Reflect.ownKeys(m.value),
863
+ getOwnPropertyDescriptor: (e, t) => Reflect.getOwnPropertyDescriptor(m.value, t)
1123
864
  }),
1124
- getSpectrum: b,
1125
- getPalette: () => S.value,
1126
- freeze: () => c.freeze,
1127
- routePointer: v
865
+ getSpectrum: g,
866
+ getPalette: () => O.value,
867
+ interactive: () => s.interactive,
868
+ freeze: () => s.freeze
1128
869
  });
1129
- return L(k.rendererStatus, (e) => a("rendererStatus", e), {
870
+ N(P.rendererStatus, (e) => o("rendererStatus", e), {
1130
871
  immediate: !0,
1131
872
  flush: "sync"
1132
- }), L(() => c.config, () => k.wake(), { deep: !0 }), L([
1133
- () => c.color,
1134
- () => c.getPalette,
1135
- f
1136
- ], C, { immediate: !0 }), j(() => {
1137
- C();
1138
- }), L(f, () => k.wake()), L(() => c.freeze, () => k.wake()), n({
1139
- backend: () => k.backend,
1140
- pause: k.pause,
1141
- resume: k.resume,
1142
- wake: k.wake,
1143
- renderAt: k.renderAt,
1144
- setHeadT: k.setHeadT,
1145
- rendererStatus: k.rendererStatus
1146
- }), (e, t) => (N(), D("div", {
873
+ }), N(() => s.config, () => P.wake(), { deep: !0 }), N([
874
+ () => s.color,
875
+ () => s.getPalette,
876
+ d
877
+ ], A, { immediate: !0 }), D(A), N(d, () => P.wake()), N(() => s.freeze, () => P.wake());
878
+ let F = j(!1), I = S(() => Number(P.headTLive.value.toFixed(3))), R = S(() => `${y.value} · ${Math.round(P.headTLive.value * 100)}% through the period`);
879
+ function z(e) {
880
+ P.setHeadT(P.headT + e);
881
+ }
882
+ function B(e) {
883
+ if (s.interactive) {
884
+ switch (e.key) {
885
+ case "ArrowRight":
886
+ z(ae);
887
+ break;
888
+ case "ArrowLeft":
889
+ z(-ae);
890
+ break;
891
+ case "ArrowUp":
892
+ z(V);
893
+ break;
894
+ case "ArrowDown":
895
+ z(-V);
896
+ break;
897
+ case "Home":
898
+ P.setHeadT(0);
899
+ break;
900
+ case "End":
901
+ P.setHeadT(.999);
902
+ break;
903
+ case " ":
904
+ case "Spacebar":
905
+ F.value = !F.value, F.value ? P.pause() : P.resume();
906
+ break;
907
+ default: return;
908
+ }
909
+ e.preventDefault();
910
+ }
911
+ }
912
+ return n({
913
+ backend: () => P.backend,
914
+ pause: P.pause,
915
+ resume: P.resume,
916
+ wake: P.wake,
917
+ setHeadT: P.setHeadT,
918
+ headT: P.headTLive,
919
+ flick: P.flick,
920
+ rendererStatus: P.rendererStatus
921
+ }), (e, t) => (k(), C("div", {
1147
922
  ref_key: "hostRef",
1148
- ref: u,
1149
- class: A(["fourier-field", { "fourier-field--interactive": _.value }])
1150
- }, [O("canvas", {
923
+ ref: l,
924
+ class: E(["fourier-field", { "fourier-field--interactive": s.interactive }]),
925
+ role: s.interactive ? "slider" : void 0,
926
+ tabindex: s.interactive ? 0 : void 0,
927
+ "aria-label": s.interactive ? "Fourier reconstruction parameter" : void 0,
928
+ "aria-valuemin": s.interactive ? 0 : void 0,
929
+ "aria-valuemax": s.interactive ? 1 : void 0,
930
+ "aria-valuenow": s.interactive ? I.value : void 0,
931
+ "aria-valuetext": s.interactive ? R.value : void 0,
932
+ onKeydown: B
933
+ }, [w("canvas", {
1151
934
  ref_key: "canvasRef",
1152
- ref: d,
935
+ ref: u,
1153
936
  class: "fourier-field-canvas",
1154
937
  "aria-hidden": "true"
1155
- }, null, 512)], 2));
938
+ }, null, 512)], 42, Ae));
1156
939
  }
1157
- }), [["__scopeId", "data-v-c4976ac3"]]);
940
+ }), [["__scopeId", "data-v-017224a9"]]);
1158
941
  //#endregion
1159
- export { te as DEFAULT_FOURIER_CONFIG, l as FOURIER_FIGURES, u as FOURIER_FIGURE_KEYS, De as FourierField, z as MAX_CURVE_SAMPLES, R as MAX_PHASORS, U as WARM_IDENTITY_PALETTE, d as comp, f as dftFromPoints, p as makeEllipticSpectrum, m as makeHarmonicFigure, h as partialSumAt, g as positionsAt, Ee as useFourierField };
942
+ export { H as DEFAULT_FOURIER_CONFIG, c as FOURIER_FIGURES, l as FOURIER_FIGURE_KEYS, je as FourierField, d as dftFromPoints, f as makeEllipticSpectrum, p as makeHarmonicFigure, L as mintSpectrum, m as partialSumAt, h as positionsAt };