@mkbabb/glass-ui 8.0.0 → 9.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 (142) hide show
  1. package/dist/aurora.js +371 -349
  2. package/dist/blob.js +42 -42
  3. package/dist/{checkbox-BnXuGgJ3.js → checkbox-B1qn65se.js} +1 -1
  4. package/dist/checkbox.js +1 -1
  5. package/dist/color.glsl-DTJhdekG.js +34 -0
  6. package/dist/color.wgsl-BTszLY39.js +398 -0
  7. package/dist/{command-BuBnYpxF.js → command-tEzAaVY_.js} +1 -1
  8. package/dist/command.js +1 -1
  9. package/dist/components/_shared/control.d.ts +0 -1
  10. package/dist/components/_shared/index.d.ts +0 -1
  11. package/dist/components/aurora/Aurora.vue.d.ts +1 -0
  12. package/dist/components/aurora/composables/frameLoop.d.ts +6 -5
  13. package/dist/components/aurora/composables/runtime.d.ts +26 -1
  14. package/dist/components/aurora/composables/useAurora.d.ts +28 -0
  15. package/dist/components/aurora/composables/wgpuSetup.d.ts +1 -1
  16. package/dist/components/aurora/constants/presets.d.ts +44 -5
  17. package/dist/components/aurora/index.d.ts +1 -1
  18. package/dist/components/configurator/Configurator.vue.d.ts +25 -2
  19. package/dist/components/configurator/ConfiguratorRow.vue.d.ts +4 -2
  20. package/dist/components/configurator/styles.css +1 -1
  21. package/dist/components/dock/DockSeparator.vue.d.ts +2 -3
  22. package/dist/components/dock/GlassDock.vue.d.ts +1 -0
  23. package/dist/components/dock/composables/dockContext.d.ts +1 -8
  24. package/dist/components/dock/composables/useDockRun.d.ts +20 -0
  25. package/dist/components/dock/composables/useDockSearch.d.ts +1 -1
  26. package/dist/components/dock/composables/useDockShellProps.d.ts +18 -101
  27. package/dist/components/dock/composables/useDockState.d.ts +2 -9
  28. package/dist/components/dock/constants.d.ts +15 -0
  29. package/dist/components/dock/index.d.ts +2 -2
  30. package/dist/components/dock/styles/controls/icon-button.css +1 -1
  31. package/dist/components/dock/styles/controls/tab-button.css +1 -1
  32. package/dist/components/dock/styles/controls/triggers.css +1 -1
  33. package/dist/components/dock/styles/controls.css +1 -1
  34. package/dist/components/dock/styles/density.css +1 -1
  35. package/dist/components/dock/styles/index.css +1 -1
  36. package/dist/components/dock/styles/layer-group.css +1 -1
  37. package/dist/components/dock/styles/layers.css +1 -1
  38. package/dist/components/dock/styles/morph.css +1 -1
  39. package/dist/components/dock/styles/run.css +1 -0
  40. package/dist/components/dock/styles/search.css +1 -1
  41. package/dist/components/dock/styles/shape.css +1 -1
  42. package/dist/components/dock/styles/shell-regions.css +1 -1
  43. package/dist/components/dock/styles/shell.css +1 -1
  44. package/dist/components/fourier-field/FourierField.vue.d.ts +28 -25
  45. package/dist/components/fourier-field/clock.d.ts +35 -0
  46. package/dist/components/fourier-field/constants.d.ts +78 -103
  47. package/dist/components/fourier-field/index.d.ts +3 -28
  48. package/dist/components/fourier-field/math.d.ts +69 -77
  49. package/dist/components/fourier-field/renderer/mint.d.ts +50 -0
  50. package/dist/components/fourier-field/renderer/uniforms.d.ts +84 -0
  51. package/dist/components/fourier-field/renderer/wgpu.d.ts +28 -0
  52. package/dist/components/fourier-field/shaders/compute.wgsl.d.ts +1 -0
  53. package/dist/components/fourier-field/shaders/render.wgsl.d.ts +1 -0
  54. package/dist/components/fourier-field/useFourierField.d.ts +49 -0
  55. package/dist/components/handmark/HandMark.vue.d.ts +18 -14
  56. package/dist/components/handmark/index.d.ts +4 -23
  57. package/dist/components/handmark/stroke.d.ts +92 -0
  58. package/dist/composables/dark/darkModeSyncScript.d.ts +19 -0
  59. package/dist/composables/motion/pointer/pointerFieldMappings.d.ts +0 -41
  60. package/dist/{components/search/composables → composables/search}/index.d.ts +2 -2
  61. package/dist/{components/search/composables/fuzzySearchIndex.d.ts → composables/search/match.d.ts} +8 -3
  62. package/dist/configurator-BHJymdrF.js +339 -0
  63. package/dist/configurator.js +1 -1
  64. package/dist/constellation.js +2 -2
  65. package/dist/control-DJp15L4U.js +7 -0
  66. package/dist/{dark-z_P5QwqI.js → dark-CLlmo7dD.js} +2 -2
  67. package/dist/dark-mode-toggle.js +1 -1
  68. package/dist/dark.js +1 -1
  69. package/dist/dock.js +712 -486
  70. package/dist/dom.js +90 -8
  71. package/dist/easing.js +3 -3
  72. package/dist/expandable-container-BY1hQXi4.js +142 -0
  73. package/dist/expandable-container.js +2 -142
  74. package/dist/fourier-field.js +643 -855
  75. package/dist/fourier-math.js +6 -6
  76. package/dist/glass-ui.css +1 -1
  77. package/dist/glass-ui.js +85 -72
  78. package/dist/handmark.js +330 -718
  79. package/dist/index.d.ts +1 -1
  80. package/dist/labeled-field.js +2 -2
  81. package/dist/{menu-wLu1XGKn.js → menu-TDMNtZjG.js} +1 -1
  82. package/dist/menu.js +1 -1
  83. package/dist/motion-core.js +34 -34
  84. package/dist/{participation-B0hdsIRo.js → participation-Di4UHbzk.js} +8 -8
  85. package/dist/{pointerFieldMappings-DP5tla5z.js → pointerFieldMappings-DXwlU5uj.js} +7 -23
  86. package/dist/{popover-DBMKTPkL.js → popover-C04Rrdcz.js} +1 -1
  87. package/dist/popover.js +1 -1
  88. package/dist/{radio-group-Ci8o-pDx.js → radio-group-BNV7PN1O.js} +1 -1
  89. package/dist/radio-group.js +1 -1
  90. package/dist/{select-CDgwGH_P.js → select-COox45Kv.js} +1 -1
  91. package/dist/select.js +1 -1
  92. package/dist/{slider-gsc8jDIo.js → slider-5ChjjWSR.js} +1 -1
  93. package/dist/slider.js +1 -1
  94. package/dist/styles/components.css +1 -1
  95. package/dist/styles/glass/material.css +1 -1
  96. package/dist/styles/glass/overlay-plate.css +1 -1
  97. package/dist/{switch-DxZzDrmV.js → switch-lLkbREB-.js} +1 -1
  98. package/dist/switch.js +1 -1
  99. package/dist/{tabs-CYscnq5H.js → tabs-DSJMd1Et.js} +1 -1
  100. package/dist/tabs.js +1 -1
  101. package/dist/{toggle-group-11aPGMfE.js → toggle-group-DUcmKnDw.js} +1 -1
  102. package/dist/toggle-group.js +1 -1
  103. package/dist/{tooltip-DqKMZ4dn.js → tooltip-BKk2qVDn.js} +1 -1
  104. package/dist/tooltip.js +1 -1
  105. package/dist/{resolveCanvasColor-6MCrswTB.js → useCanvas2D-CQRElMyE.js} +1 -11
  106. package/dist/useResolveTokenColor-B2FfJnd9.js +24 -0
  107. package/package.json +2 -20
  108. package/dist/canvas.d.ts +0 -1
  109. package/dist/canvas.js +0 -2
  110. package/dist/color.wgsl-B0N3mKMb.js +0 -412
  111. package/dist/compile-D7VYG3gi.js +0 -19
  112. package/dist/components/dock/composables/useDockOverflowFit.d.ts +0 -20
  113. package/dist/components/dock/composables/useDockTouchGate.d.ts +0 -42
  114. package/dist/components/dock/styles/overflow.css +0 -1
  115. package/dist/components/fourier-field/composables/fourierFieldGLSetup.d.ts +0 -24
  116. package/dist/components/fourier-field/composables/fourierFieldWGPUSetup.d.ts +0 -31
  117. package/dist/components/fourier-field/composables/uniformBridgeWGPU.d.ts +0 -39
  118. package/dist/components/fourier-field/composables/useFourierField.d.ts +0 -56
  119. package/dist/components/fourier-field/shaders/fourier-field.compute.wgsl.d.ts +0 -1
  120. package/dist/components/fourier-field/shaders/fourier-field.glsl.d.ts +0 -4
  121. package/dist/components/fourier-field/shaders/fourier-field.render.wgsl.d.ts +0 -1
  122. package/dist/components/fourier-field/shaders/fourier-field.ribbon.d.ts +0 -51
  123. package/dist/components/handmark/brush.d.ts +0 -269
  124. package/dist/components/handmark/composables/useHandMark.d.ts +0 -99
  125. package/dist/components/handmark/constants.d.ts +0 -69
  126. package/dist/components/handmark/freehand.d.ts +0 -69
  127. package/dist/components/handmark/geometry.d.ts +0 -62
  128. package/dist/components/handmark/ink.d.ts +0 -11
  129. package/dist/components/handmark/noise.d.ts +0 -30
  130. package/dist/components/handmark/texture.d.ts +0 -39
  131. package/dist/components/handmark/types.d.ts +0 -70
  132. package/dist/components/search/SearchBar.vue.d.ts +0 -38
  133. package/dist/components/search/index.d.ts +0 -8
  134. package/dist/components/search/searchVariants.d.ts +0 -16
  135. package/dist/configurator-BoeScnDL.js +0 -311
  136. package/dist/control-Da4XeT43.js +0 -11
  137. package/dist/search.d.ts +0 -1
  138. package/dist/search.js +0 -61
  139. package/dist/useFuzzySearch-CUnZknpm.js +0 -177
  140. package/dist/useResolveTokenColor-CspQv_ik.js +0 -107
  141. /package/dist/{components/search/composables → composables/search}/types.d.ts +0 -0
  142. /package/dist/{components/search/composables → composables/search}/useFuzzySearch.d.ts +0 -0
@@ -1 +1 @@
1
- @layer components{.glass-dock{--glass-blur-resting: blur( calc(var(--glass-blur-resting-radius) * var(--glass-level)) ) saturate(1.2);--dock-surface-blur: var(--glass-blur-resting);--dock-separator-height: calc(var(--dock-h,var(--size-icon-btn)) * 0.5);--dock-collapsed-hover-scale: var(--scale-hover-dock);--dock-stagger-window-size: 0.4;--dock-stagger-step: 0.08;--dock-card-radius: var(--radius-dock-card);--dock-tile-min: 4.5rem;position: relative;z-index: 0;--glass-backdrop: light;--dock-plate-collapsed-tier: var(--glass-veil-wash);--dock-plate-collapsed-border: var(--glass-border-floating);--dock-plate-expanded-tier: var(--glass-veil-dock);--dock-plate-expanded-border: var(--glass-border-dock);contain: layout style;display: inline-flex;align-items: center;white-space: nowrap;max-inline-size: var(--dock-max-inline-size);padding: var(--dock-padding-block,0.375rem) var(--dock-padding-inline,0.5rem);box-shadow: var(--glass-rim-top),var(--glass-rim-bottom),var(--shadow-dock-override,var(--shadow-dock));}.dark .glass-dock{--glass-blur-resting: blur( calc(var(--glass-blur-resting-radius) * var(--glass-level)) ) saturate(1.3) brightness(1.14);}.glass-dock{overflow: visible;}.glass-dock.vertical.always-expanded:not([data-morphing]),.glass-dock.vertical.expanded:not([data-morphing]){overflow-x: visible;overflow-y: auto;scrollbar-width: none;overscroll-behavior-y: contain;}.glass-dock.vertical.always-expanded:not([data-morphing])::-webkit-scrollbar,.glass-dock.vertical.expanded:not([data-morphing])::-webkit-scrollbar{display: none;}.glass-dock.vertical.always-expanded:not([data-morphing]),.glass-dock.vertical.expanded:not([data-morphing]){mask-image: linear-gradient( to bottom,transparent 0,black var(--fade-start,0px),black calc(100% - var(--fade-end,0px)),transparent 100% );animation: gl-fade-start-in linear both,gl-fade-end-out linear both;animation-timeline: scroll(self block),scroll(self block);animation-range: 0 var(--fade-scroll-width),calc(100% - var(--fade-scroll-width)) 100%;}.glass-dock.vertical{display: inline-flex;flex-direction: column;align-items: stretch;justify-content: flex-start;gap: var(--dock-layer-gap,0.375rem);white-space: normal;width: auto;height: auto;max-block-size: var(--dock-max-block-size);padding: var(--dock-padding-block,0.375rem);border-radius: var(--radius-dock);transition: box-shadow var(--dock-motion-standard),transform var(--dock-motion-resize),scale var(--dock-motion-resize);}.glass-dock.vertical[data-morphing]{transition-property: box-shadow,transform;}.glass-dock.vertical:hover,.glass-dock.vertical:active,.glass-dock.vertical[data-held]{will-change: transform;}.glass-dock.vertical{padding: var(--dock-vertical-padding,var(--dock-padding-inline,0.5rem));}.glass-dock.vertical.shape-pill{border-radius: var(--radius-pill);}.glass-dock.vertical.shape-rounded{border-radius: var(--radius-card);}.glass-dock.vertical.shape-card{border-radius: calc( var(--radius-pill) + (var(--dock-card-radius) - var(--radius-pill)) * var(--dock-expand-t) );}.glass-dock:not(.vertical).shape-rounded{border-radius: var(--radius-card);}.glass-dock:not(.vertical).shape-card{border-radius: calc( var(--radius-pill) + (var(--dock-card-radius) - var(--radius-pill)) * var(--dock-expand-t) );}@supports (corner-shape: superellipse(2)){.glass-dock:not(.vertical).shape-card{corner-shape: var(--corner-shape-bigdock);}}.glass-dock:where(.fixed){z-index: var(--z-dock);}.dock-inline{margin: 0 auto;}.dock-sticky{position: sticky;top: 0;z-index: var(--z-dock);margin: 0 auto;}}
1
+ @layer components{.glass-dock{--glass-blur-resting: blur( calc(var(--glass-blur-resting-radius) * var(--glass-level)) ) saturate(1.2);--dock-surface-blur: var(--glass-blur-resting);--dock-separator-height: calc(var(--dock-h,var(--size-icon-btn)) * 0.5);--dock-stagger-window-size: 0.4;--dock-stagger-step: 0.08;--dock-card-radius: var(--radius-dock-card);position: relative;z-index: 0;margin: 0 auto;--glass-backdrop: light;--dock-plate-collapsed-tier: var(--glass-veil-wash);--dock-plate-collapsed-border: var(--glass-border-floating);--dock-plate-expanded-tier: var(--glass-veil-dock);--dock-plate-expanded-border: var(--glass-border-dock);contain: layout style;display: inline-flex;align-items: center;white-space: nowrap;padding: var(--dock-padding-block,0.375rem) var(--dock-padding-inline,0.5rem);box-shadow: var(--glass-rim-top),var(--glass-rim-bottom),var(--shadow-dock-override,var(--shadow-dock));}.dark .glass-dock{--glass-blur-resting: blur( calc(var(--glass-blur-resting-radius) * var(--glass-level)) ) saturate(1.3) brightness(1.14);}.glass-dock{overflow: visible;}.glass-dock.vertical{display: inline-flex;flex-direction: column;align-items: stretch;justify-content: flex-start;gap: var(--dock-layer-gap,0.375rem);white-space: normal;width: auto;height: auto;max-block-size: var(--dock-max-block-size);padding: var(--dock-padding-block,0.375rem);border-radius: var(--radius-dock);transition: box-shadow var(--dock-motion-standard),transform var(--dock-motion-resize),scale var(--dock-motion-resize);}.glass-dock.vertical[data-morphing]{transition-property: box-shadow,transform;}.glass-dock.vertical:hover,.glass-dock.vertical:active,.glass-dock.vertical[data-held]{will-change: transform;}.glass-dock.vertical{padding: var(--dock-vertical-padding,var(--dock-padding-inline,0.5rem));}.glass-dock.vertical.shape-pill{border-radius: var(--radius-pill);}.glass-dock.vertical.shape-rounded{border-radius: var(--radius-card);}.glass-dock.vertical.shape-card{border-radius: calc( var(--radius-pill) + (var(--dock-card-radius) - var(--radius-pill)) * var(--dock-expand-t) );}.glass-dock:not(.vertical).shape-rounded{border-radius: var(--radius-card);}.glass-dock:not(.vertical).shape-card{border-radius: calc( var(--radius-pill) + (var(--dock-card-radius) - var(--radius-pill)) * var(--dock-expand-t) );}@supports (corner-shape: superellipse(2)){.glass-dock:not(.vertical).shape-card{corner-shape: var(--corner-shape-bigdock);}}}
@@ -1,48 +1,50 @@
1
- import type { ColorResolver, OklchStop } from "../../composables/color/index.js";
1
+ import type { OklchStop } from "../../composables/color/index.js";
2
2
  import { type BasisComponent } from "./math.js";
3
- import type { FourierFieldConfig } from "./constants.js";
3
+ import { type FourierFieldConfig } from "./constants.js";
4
4
  import type { RendererStatus } from "../../composables/glass/webgpu/rendererStatus.js";
5
5
  /**
6
- * FourierField — a Fourier epicycle field on the GPU substrate, a
7
- * sibling primitive to Aurora, GooBlob, and DotFlowField. A chain of rotating circles
8
- * stacked tip-to-tail draws the reconstructing partial-sum curve; a glowing comet head
9
- * leads the fading trail. WebGPU-FIRST: the compute pass writes the partial-sum curve +
10
- * the epicycle chain tips, the fullscreen-fragment render pass composites the SDF field; a
11
- * WebGL2 GLSL twin is the fallback. It composes `useFourierField` with the
12
- * `createGpuSubstrate` picker over the ONE canvas lifecycle leaf (offscreen-pause, live-PRM
13
- * freeze, consumer-owned DPR) and the SHARED `usePointerVelocityField` (the pointer SCRUBS
14
- * the reconstruction).
6
+ * FourierField — the drawing machine.
15
7
  *
16
- * The DEFAULT palette is the warm-cream library identity (`--viz-fourier` warm-amber); a
17
- * demo Teal/cool preset stays demo-LOCAL (presets-in-consumers — never a library token).
8
+ * Aurora is weather and the blob is a creature; this one is the substrate whose motion is
9
+ * a CHECKABLE CLAIM. Every frame is the truncated inverse transform of a fixed spectrum,
10
+ * `Σ c_k·e^{2πikt}`, and a reader can verify it: the rings are the terms, the chain is the
11
+ * sum, the head is where the parameter currently is. Nothing decorative moves.
18
12
  *
19
- * Visual bundles live in config presets. An ambient-background consumer passes `color` and the
20
- * `colorResolver` for a thin warm default preserving the recessive look; the studio passes
21
- * the full `config` + `spectrum` + `getPalette`.
13
+ * The two payoffs are CLOSURE — every index is an integer, so the figure completes exactly
14
+ * at `t = 1` — and ASSEMBLY — the spectrum is amplitude-ordered, so every step of the N
15
+ * axis adds the largest remaining correction rather than the next arbitrary frequency.
16
+ *
17
+ * WebGPU only. There is one renderer, because a second one would be a second paint law,
18
+ * and where WebGPU is absent the field declares it and paints nothing.
22
19
  */
23
20
  type __VLS_Props = {
24
- /** The full author config (the studio's `useConfiguratorState` model). Defaults to the warm identity. */
21
+ /** The author config. Defaults to the shipped identity. */
25
22
  config?: FourierFieldConfig;
26
- /** An explicit CPU-minted spectrum (a curated shape's DFT). When absent, a seeded elliptic spectrum is generated. */
23
+ /** An explicit spectrum. When absent, the config's `source` decides. */
27
24
  spectrum?: readonly BasisComponent[];
28
- /** Resolve the curve palette as OKLCh (the studio themes it). When absent, `color`/`config.palette` is used. */
25
+ /** Resolve the ramp as OKLCh (the studio themes it). Wins over `color`. */
29
26
  getPalette?: () => OklchStop[];
30
- /** Ambient-consumer color seam — a `var()`/`light-dark()` token or a literal; derives a warm 2-stop palette. */
27
+ /** Ambient-consumer colour seam — a token or a literal; derives a two-stop ramp. */
31
28
  color?: string;
32
- /** The color resolver (the GooBlob/Aurora seam) — required when `color` is a `var()` token. */
33
- colorResolver?: ColorResolver;
34
- /** Extra seed mixed into the generated elliptic spectrum PRNG. Default "". */
29
+ /** Seed for the generated spectrum. */
35
30
  seed?: string;
36
- /** Paint ONE static deterministic best-frame and never animate (the capture lever). Default false. */
31
+ /** Hold the clock. The figure stays live; only time stops. */
37
32
  freeze?: boolean;
33
+ /**
34
+ * The pointer scrubs and flicks the clock, and the host becomes a real slider.
35
+ * `false` is the decor mount: no listeners, no role, no tab stop.
36
+ */
37
+ interactive?: boolean;
38
38
  };
39
39
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
40
40
  backend: () => import("../../composables/glass/webgpu/useGpuSubstrate.js").GpuBackend;
41
41
  pause: () => void;
42
42
  resume: () => void;
43
43
  wake: () => void;
44
- renderAt: (timeSec: number) => void;
45
44
  setHeadT: (t: number) => void;
45
+ /** The live loop parameter. Unwrapped on the exposed proxy, so a transport binds it. */
46
+ headT: Readonly<import("vue").Ref<number, number>>;
47
+ flick: (turnsPerSec: number) => void;
46
48
  rendererStatus: Readonly<import("vue").Ref<RendererStatus, RendererStatus>>;
47
49
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
48
50
  rendererStatus: (status: RendererStatus) => any;
@@ -51,6 +53,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
51
53
  }>, {
52
54
  seed: string;
53
55
  freeze: boolean;
56
+ interactive: boolean;
54
57
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
55
58
  declare const _default: typeof __VLS_export;
56
59
  export default _default;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The substrate-travel spring, bound by JOB rather than by a remembered pair. This module
3
+ * owns exactly one invariant about it — the damping fraction it CONSUMES — because that is
4
+ * what decides whether a flick can rewind the drawing.
5
+ */
6
+ export declare const FOURIER_TRAVEL_SPRING: import("../../composables/motion/index.js").SpringPresetRow;
7
+ /** The consumed damping fraction. At or above this, a flick has no backward frames. */
8
+ export declare const FOURIER_SETTLE_ZETA_FLOOR = 0.82;
9
+ export interface FourierClockInput {
10
+ /** Seconds elapsed since the last step. */
11
+ dt: number;
12
+ /** The config's speed multiplier. */
13
+ speed: number;
14
+ /** The pointer's horizontal velocity, or 0 when the pointer is not driving. */
15
+ scrubVelocity: number;
16
+ /** The pointer field's transient flick burst (0..1), or 0. */
17
+ burst: number;
18
+ /** Whether the pointer is engaged at all this step. */
19
+ engaged: boolean;
20
+ /** Hold the parameter and clear the momentum. */
21
+ frozen: boolean;
22
+ }
23
+ export interface FourierClock {
24
+ /** The loop parameter `head_t ∈ [0,1)`. */
25
+ readonly headT: number;
26
+ /** The current rate offset (turns/s) the flick spring is carrying. */
27
+ readonly momentum: number;
28
+ /** Advance one step; returns the rate the step actually used (never negative). */
29
+ step: (input: FourierClockInput) => number;
30
+ /** Seat the parameter directly. Takes no spring — a hand's position is not physics. */
31
+ set: (t: number) => void;
32
+ /** Inject one impulse in turns per second, capped by the advance budget. */
33
+ flick: (turnsPerSec: number) => void;
34
+ }
35
+ export declare function createFourierClock(): FourierClock;
@@ -1,134 +1,109 @@
1
1
  import type { OklchStop } from "../../composables/color/index.js";
2
+ import type { FOURIER_FIGURES } from "./math.js";
3
+ /** The palette-stop ceiling — the WGSL `MAX_FOURIER_STOPS` binding size. */
4
+ export declare const MAX_FOURIER_STOPS = 4;
2
5
  /**
3
- * Curve-sampling resolution for the stable view-fit bbox pass — the renderer samples the
4
- * epicycle curve this many times once at mount to size it to the canvas with a fixed
5
- * margin (so the curve never clips and the scale is constant across the run). 720 = one
6
- * sample per half-degree of the unit period.
6
+ * The CPU-resolved OKLab ramp resolution. The palette is sampled into this many entries
7
+ * once per palette change and uploaded as a table, so the shader never runs an OKLab
8
+ * conversion per fragment and the ink, the mark and the head all read ONE ramp.
7
9
  */
8
- export declare const FOURIER_FIT_SAMPLES = 720;
10
+ export declare const FOURIER_LUT_SIZE = 16;
9
11
  /**
10
- * The phasor-table uniform ceiling — the WGSL `MAX_PHASORS` #define. The configurator's
11
- * harmonic count maxes here; a >64-term custom trace books a storage-buffer widen (cheap —
12
- * the flow-field already uses a storage buffer). The curated ℱ/heart/star set + the
13
- * generated elliptic spectrum all sit under it.
12
+ * The tail chroma floor. A ramp that lets chroma fall to zero paints a grey smear at the
13
+ * trail's end — the blob disease. Every LUT entry is floored here.
14
14
  */
15
- export declare const MAX_PHASORS = 64;
15
+ export declare const FOURIER_TAIL_CHROMA_FLOOR = 0.1;
16
16
  /**
17
- * The comet-curve sample ceiling — the storage-buffer array bound. The compute pass
18
- * writes the partial-sum curve point at `M` samples back along the period from the head;
19
- * the render pass reads them as the comet polyline. 384 gives a smooth bold stroke.
17
+ * THE INK STEP. The ink is the mark's own ramp head taken one step DOWN in lightness and
18
+ * then gamut-mapped at fixed L and hue, so it is a real pigment rather than a wash. This
19
+ * is the module's own ramp step, and it is what solves mark:ink into the [1.5, 3.0]:1
20
+ * band in BOTH schemes at once.
20
21
  */
21
- export declare const MAX_CURVE_SAMPLES = 384;
22
- /** The palette-stop ceiling — the WGSL `MAX_FOURIER_STOPS` #define. */
23
- export declare const MAX_FOURIER_STOPS = 4;
22
+ export declare const FOURIER_INK_DELTA_L = 0.22;
24
23
  /**
25
- * The cursor-velocity → head_t clock-coupling gain. Cursor motion (not absolute X)
26
- * nudges the loop clock: a flick fast-
27
- * forwards, a still cursor lets the field drift at its config speed. Velocity-continuous —
28
- * the head never teleports (an absolute-X `headT = pointerX` scrub reads as "not
29
- * following" a 2-D cursor).
24
+ * THE HEAD LIFT. The comet head's core is the ramp head lifted by half the ink step, with
25
+ * chroma preserved. The head is light-led and stays in the palette family — it is never a
26
+ * white specular, which reads as trite and shiny and belongs to no rung.
30
27
  */
31
- export declare const SCRUB_GAIN = 0.15;
28
+ export declare const FOURIER_HEAD_DELTA_L = 0.11;
32
29
  /**
33
- * The comet ribbon's TAIL width as a fraction of the head width (the head→tail taper). At the
34
- * head (age 1) the stroke is the full `trailWidth`; at the tail (age 0) it is `RIBBON_TAIL_FRAC`
35
- * of it. The taper is `f(age) = RIBBON_TAIL_FRAC + (1 − RIBBON_TAIL_FRAC)·age` (LINEAR, so the
36
- * age-0.5 MID-BODY is `RIBBON_TAIL_FRAC + (1 − RIBBON_TAIL_FRAC)·0.5` of the head). Declared
37
- * ONCE here + MIRRORED verbatim as the `RIBBON_TAIL_FRAC` const in BOTH shaders (the GLSL twin
38
- * cannot import a TS constant); keep the three copies equal so the
39
- * mid-body floor below can never drift off the taper.
30
+ * The chain's hue sweep, in radians, ridden symmetrically about the palette hue. Measured
31
+ * swept range 10.3°–50.5° off a warm anchor, which is why the chain cannot reach
32
+ * chartreuse by construction rather than by a clamp.
40
33
  */
41
- export declare const RIBBON_TAIL_FRAC = 0.4;
42
- /** The mid-body taper fraction (age 0.5) — the width the {@link RIBBON_HEAD_FLOOR_PX} floor targets. */
43
- export declare const RIBBON_MID_TAPER_FRAC: number;
34
+ export declare const FOURIER_CHAIN_HUE_SWEEP = 0.35;
35
+ /** The scaffold stroke as a fraction of the mark stroke — the chain is a rung below the curve. */
36
+ export declare const FOURIER_SCAFFOLD_STROKE_FRAC = 0.4;
37
+ /** The three mark-stroke rungs in CSS px. Three rungs, three visibly distinct pictures. */
38
+ export declare const FOURIER_STROKE_RUNGS: readonly [4, 8, 12];
44
39
  /**
45
- * The MID-BODY minimum in CSS px (the B1 "≥2.5px CSS mid-body at every DPR, never a 1px
46
- * hairline polyline" bar). The width is DPR-independent by construction (converted to model
47
- * via `canvasCssMin` = backing ÷ dpr), so a CSS-px floor holds at every DPR.
40
+ * The flick's advance cap in turns. A full flick advances the clock by at most half a
41
+ * figure (`|∫flick| = v/ω² ≤ FOURIER_FLICK_TURNS`), so the machine stays checkable: the
42
+ * reader can always see where the head went.
48
43
  */
49
- export declare const RIBBON_MID_FLOOR_PX = 2.5;
44
+ export declare const FOURIER_FLICK_TURNS = 0.5;
45
+ /** The cursor-velocity → clock-rate scrub gain (turns per second per unit pointer velocity). */
46
+ export declare const FOURIER_SCRUB_GAIN = 0.15;
47
+ /** Base forward period for one full traversal at 1× speed, in seconds. */
48
+ export declare const FOURIER_PERIOD_S = 16;
49
+ /** The keyboard scrub quanta in turns: fine (←/→) and coarse (↑/↓). */
50
+ export declare const FOURIER_QUANTUM_FINE: number;
51
+ export declare const FOURIER_QUANTUM_COARSE: number;
50
52
  /**
51
- * The HEAD-width floor in CSS px (the `trailWidth` clamp in `trailWidthToModel`). Sized so the
52
- * mid-body (head × {@link RIBBON_MID_TAPER_FRAC}) clears {@link RIBBON_MID_FLOOR_PX} — even a
53
- * consumer setting `trailWidth: 1` gets a ≥2.5px mid-body. A hair over the exact quotient for
54
- * float safety.
53
+ * The device-pixel-ratio cap. Consumed locally: the curve is a thin stroke over mostly
54
+ * empty stage, so it is worth resolving sharply, and 2× is where the sharpness stops
55
+ * being visible and starts being fill rate.
55
56
  */
56
- export declare const RIBBON_HEAD_FLOOR_PX: number;
57
+ export declare const FOURIER_DPR_CAP = 2;
58
+ /** The number of curve samples the comet body is built from. */
59
+ export declare const FOURIER_CURVE_SAMPLES = 384;
57
60
  /**
58
- * The degenerate-tangent guard ( §2b). At a cusp the head's instantaneous
59
- * speed `s = length(head − headBack)` collapses to ~0 and the unit tangent `T = d/s` blows
60
- * up; below `FOURIER_TANGENT_EPS` the shaders + the GL CPU bead path all fall back to the
61
- * LAST stable tangent, so `s→0` resolves IDENTICALLY across both engines (the cross-engine
62
- * cusp-parity fence — one shared constant, no per-engine drift).
61
+ * The degenerate-tangent guard. At a cusp the head's instantaneous speed collapses and
62
+ * the unit tangent blows up; below this the shader falls back to the last stable tangent.
63
63
  */
64
64
  export declare const FOURIER_TANGENT_EPS = 0.0001;
65
- /**
66
- * The head squash-and-stretch gain ( §2b). The round head bead becomes a
67
- * volume-preserving anisotropic ellipse: the tangent extent scales `1 + k·ŝ`, the normal
68
- * extent `1/(1 + k·ŝ)`, where `ŝ` is the normalized head speed. The library identity carries
69
- * a SMALL non-zero gain (cartoon-weight at rest — the head is never a dead disc); a consumer
70
- * vivid preset turns it to 11. `0` = the byte-frozen round disc.
71
- */
72
- export declare const FOURIER_SQUASH_GAIN = 0.55;
73
- /**
74
- * The cartoon cel-shadow gain ( §3b). Each chain ring/arm/dot + the comet
75
- * rope paints a second darker copy OFFSET opposite its travel — the 1940s technicolor ink.
76
- * The library identity carries a SMALL non-zero gain (a quiet weight under the chain); a
77
- * consumer vivid preset floors it UP. `0` = no cel pass (byte-frozen). The offset rides the
78
- * head tangent, mirrored on both engines off the SAME `curveSamples` tangent.
79
- */
80
- export declare const FOURIER_CEL_GAIN = 0.35;
65
+ /** The spectrum source: the seeded generator, or a curated closed figure by key. */
66
+ export type FourierSource = "elliptic" | keyof typeof FOURIER_FIGURES;
81
67
  /**
82
68
  * The full author schema — the studio's `useConfiguratorState` model. Every field is a
83
- * tunable the demo configurator drives; the composable resolves it into the uniform table.
69
+ * tunable a control drives, and every one of them changes the picture.
84
70
  */
85
71
  export interface FourierFieldConfig {
86
- /** The active phasor spectrum source key — `"elliptic"` (generated) or a curated shape key. */
87
- source: string;
88
- /** TRUNCATE the partial sum — the harmonic count N (1..min(spectrum.length, MAX_PHASORS)). */
72
+ /** The spectrum source. An unknown key throws at config time rather than painting a default. */
73
+ source: FourierSource;
74
+ /**
75
+ * The partial-sum term count N. Its live domain is `[1, spectrum.length]`, where the
76
+ * length is what the mint emitted — there is no constant maximum anywhere.
77
+ */
89
78
  harmonics: number;
90
- /** Draw the rotating epicycle chain (orthogonal to N). */
91
- showEpicycles: boolean;
92
- /** How many chain arms to draw (1..N; ≤ the harmonic count). */
93
- epicycleArms: number;
94
- /** Paint the chain as a warm-anchored hue sweep vs one analogous hue. */
79
+ /** Draw the machine: the rings, the arms and the joint dots. */
80
+ showMachine: boolean;
81
+ /** Sweep the chain's hue about the palette anchor rather than painting one hue. */
95
82
  rainbowChain: boolean;
96
- /** Comet-body length as a fraction of the period (0.15..1.0). */
83
+ /** Comet-body length as a fraction of the period (0.15..1). */
97
84
  trailArc: number;
98
- /** Comet stroke weight in CSS px (1..6). */
99
- trailWidth: number;
100
- /** Outer loudness envelope (per-layer alpha multiply, 0..2). */
85
+ /** The mark stroke in CSS px — one of {@link FOURIER_STROKE_RUNGS}. */
86
+ markStroke: number;
87
+ /**
88
+ * The ink's offset from its mark, in stroke widths, taken along this segment's own
89
+ * tangent and opposite the travel. Below ½ the ink hides under its mark; above 1 it
90
+ * detaches and reads as a second line.
91
+ */
92
+ inkOffset: number;
93
+ /** The head's squash-and-stretch gain (0..1). 0 is a dead round disc. */
94
+ squash: number;
95
+ /** The head halo's strength (0..0.3). */
96
+ glow: number;
97
+ /** Outer loudness envelope (0..2). Above 1 the pack clamps, and the row says so. */
101
98
  intensity: number;
102
- /** Character of the generated elliptic spectrum (0.05..0.4 — smooth ellipse → crinkled). */
103
- harmonicScale: number;
104
- /** Clock speed multiplier (0.25/0.5/1/2). */
99
+ /** Character of the GENERATED spectrum (0..1). Inert for a curated source. */
100
+ richness: number;
101
+ /** Clock speed multiplier. */
105
102
  speed: number;
106
- /** The curve-color ramp (the library viz palette; warm default). Resolved via the ColorResolver. */
103
+ /** The curve-color ramp. */
107
104
  palette: OklchStop[];
108
- /** ONE static frame then park under `prefers-reduced-motion: reduce`. */
105
+ /** One static frame then park under `prefers-reduced-motion: reduce`. */
109
106
  respectReducedMotion: boolean;
110
- /** Pointer scrubs `head_t` + a flick injects clock momentum (the consume). */
111
- interactive: boolean;
112
- /**
113
- * Head squash-and-stretch gain ( §2b). The head bead stretches along the
114
- * curve tangent on straights, squashes across it on corners (volume-preserving). The
115
- * library identity carries a small non-zero default (cartoon-weight at rest); a vivid
116
- * consumer preset turns it to 11. 0 = the byte-frozen round disc.
117
- */
118
- squashGain: number;
119
- /**
120
- * Cartoon cel-shadow gain ( §3b). A second darker copy of the chain + rope,
121
- * offset opposite the head travel (the technicolor ink). The library identity carries a
122
- * small non-zero default; a vivid consumer preset floors it UP. 0 = no cel pass.
123
- */
124
- celGain: number;
125
107
  }
126
- /**
127
- * The warm-cream identity palette (the library default — NOT teal). A two-stop warm-amber
128
- * ramp over the warm-cream foreground family, resolved via the ColorResolver. The chain's
129
- * rainbow sweep is warm-anchored over THIS base; a consumer (the demo) themes it through a
130
- * preset, never a token edit.
131
- */
132
- export declare const WARM_IDENTITY_PALETTE: OklchStop[];
133
- /** The shipped warm-identity default config. */
108
+ /** The shipped default config. */
134
109
  export declare const DEFAULT_FOURIER_CONFIG: FourierFieldConfig;
@@ -1,29 +1,4 @@
1
- import type { ColorResolver, OklchStop } from "../../composables/color/index.js";
2
- import type { FourierFieldConfig } from "./constants.js";
3
- import type { BasisComponent } from "./math.js";
4
1
  export { default as FourierField } from "./FourierField.vue.js";
5
- export { comp, positionsAt, partialSumAt, dftFromPoints, makeEllipticSpectrum, makeHarmonicFigure, FOURIER_FIGURES, FOURIER_FIGURE_KEYS, type BasisComponent, type EllipticSpectrumOptions, type HarmonicTerm, } from "./math.js";
6
- export { DEFAULT_FOURIER_CONFIG, WARM_IDENTITY_PALETTE, MAX_PHASORS, MAX_CURVE_SAMPLES, type FourierFieldConfig, } from "./constants.js";
7
- export { useFourierField, type FourierFieldHandle, type UseFourierFieldOptions, } from "./composables/useFourierField.js";
8
- /**
9
- * The props a consumer forwards when wrapping `<FourierField>` (the GPU-substrate epicycle
10
- * field; ships via `/fourier-field`). Mirrors the component's inline `defineProps` shape because
11
- * an SFC cannot export its inline props type. Visual bundles live in config presets; the
12
- * renderer is the WGSL-primary GPU substrate.
13
- */
14
- export interface FourierFieldProps {
15
- /** The full author config (the studio's `useConfiguratorState` model). Defaults to the warm identity. */
16
- config?: FourierFieldConfig;
17
- /** An explicit CPU-minted spectrum (a curated shape's DFT). When absent, a seeded elliptic spectrum is generated. */
18
- spectrum?: readonly BasisComponent[];
19
- /** Resolve the curve palette as OKLCh (the studio themes it). */
20
- getPalette?: () => OklchStop[];
21
- /** Ambient-consumer color seam — a `var()`/`light-dark()` token or a literal; derives a warm palette. */
22
- color?: string;
23
- /** The color resolver (the GooBlob/Aurora seam) — required when `color` is a `var()` token. */
24
- colorResolver?: ColorResolver;
25
- /** Extra seed mixed into the generated elliptic spectrum PRNG. Default "". */
26
- seed?: string;
27
- /** Paint ONE static deterministic best-frame and never animate (the capture lever). Default false. */
28
- freeze?: boolean;
29
- }
2
+ export { dftFromPoints, partialSumAt, positionsAt, makeEllipticSpectrum, makeHarmonicFigure, FOURIER_FIGURES, FOURIER_FIGURE_KEYS, type BasisComponent, } from "./math.js";
3
+ export { mintSpectrum, ringsAt, type MintedSpectrum } from "./renderer/mint.js";
4
+ export { DEFAULT_FOURIER_CONFIG, type FourierFieldConfig } from "./constants.js";
@@ -1,8 +1,7 @@
1
1
  /**
2
- * One Fourier phasor. `index` is the integer frequency (a counter-rotating pair
3
- * is `+1` and `-1`); `coefficient` is the complex coefficient `[re, im]`;
4
- * `amplitude`/`phase` are its polar form (carried for callers that prefer the
5
- * polar read — the reconstruction uses the rectangular `coefficient`).
2
+ * One Fourier phasor. `index` is the integer frequency (a counter-rotating pair is `+1`
3
+ * and `-1`); `coefficient` is the complex coefficient `[re, im]`; `amplitude`/`phase` are
4
+ * its polar form (the reconstruction uses the rectangular `coefficient`).
6
5
  */
7
6
  export interface BasisComponent {
8
7
  index: number;
@@ -11,101 +10,94 @@ export interface BasisComponent {
11
10
  phase: number;
12
11
  }
13
12
  /**
14
- * Build a {@link BasisComponent} from a frequency index and its rectangular
15
- * coefficient `(re, im)`, deriving the polar `amplitude`/`phase`.
13
+ * Build a {@link BasisComponent} from a frequency index and its rectangular coefficient,
14
+ * deriving the polar `amplitude`/`phase`.
16
15
  */
17
16
  export declare function comp(index: number, re: number, im: number): BasisComponent;
18
17
  /**
19
- * The epicycle chain at parameter `t`: the running tip of each phasor stacked on
20
- * the last, starting at the origin. `positions[0]` is `[0, 0]`; the final entry
21
- * is the curve point — the inverse-DFT sum `Σ c_k * exp(2*pi*i*k*t)` at the same
22
- * `t`. `maxCircles` truncates the chain (draw fewer arms than the spectrum
23
- * carries).
18
+ * The epicycle chain at parameter `t`: the running tip of each phasor stacked on the
19
+ * last, starting at the origin. `positions[0]` is `[0, 0]`; the final entry is the curve
20
+ * point. `maxCircles` truncates the chain.
24
21
  */
25
- export declare function positionsAt(components: BasisComponent[], t: number, maxCircles?: number): [number, number][];
22
+ export declare function positionsAt(components: readonly BasisComponent[], t: number, maxCircles?: number): [number, number][];
26
23
  /**
27
- * The PARTIAL-SUM curve point at parameter `t` over the FIRST `maxTerms` phasors
28
- * — the inverse-DFT truncated summation `Σ_{k<maxTerms} c_k * exp(2*pi*i*k*t)`.
29
- * It is {@link positionsAt} truncated at `maxTerms` and read at the FINAL tip:
30
- * the single curve POINT (not the whole chain). The truncation axis is the SAME
31
- * one `positionsAt`'s `maxCircles` arg drives — `maxCircles` reads the whole
32
- * chain truncated, `partialSumAt` reads only its endpoint — so the studio's
33
- * N-harmonics slider can draw the assembling chain (`positionsAt(.., N)`) AND the
34
- * resolving curve point (`partialSumAt(.., t, N)`) off the ONE axis. Sweeping `t`
35
- * over `[0,1)` and joining the points traces the partial-sum CURVE: at `maxTerms`
36
- * = 1 a single ellipse, growing toward the full reconstruction as `maxTerms`
37
- * climbs to `components.length` (the reference's "watch it sum" beauty).
38
- *
39
- * `maxTerms` omitted (or ≥ `components.length`) is the full reconstruction — the
40
- * same point `positionsAt(components, t)` returns at its final tip.
24
+ * The PARTIAL-SUM curve point at `t` over the first `maxTerms` phasors — the truncated
25
+ * inverse DFT `Σ_{k<maxTerms} c_k · e^{2πi·k·t}`. It is {@link positionsAt} read at its
26
+ * final tip without building the chain, so the fit pass and the N axis share one
27
+ * evaluator. Sweeping `t` over `[0,1)` traces the partial-sum curve: at `maxTerms = 1` a
28
+ * single ellipse, growing toward the full reconstruction as `maxTerms` climbs.
41
29
  */
42
- export declare function partialSumAt(components: BasisComponent[], t: number, maxTerms?: number): [number, number];
30
+ export declare function partialSumAt(components: readonly BasisComponent[], t: number, maxTerms?: number): [number, number];
43
31
  /**
44
- * The forward DFT — the inverse of {@link positionsAt}. Takes a closed sequence
45
- * of `[x, y]` samples around a curve (treated as the complex signal `x + i·y`,
46
- * uniformly spaced in the parameter `t ∈ [0, 1)`) and returns the
47
- * {@link BasisComponent} spectrum that {@link positionsAt} reconstructs.
48
- *
49
- * Frequencies run symmetrically around 0 — `0, +1, -1, +2, -2, …` up to the
50
- * Nyquist limit — which is the index ordering the epicycle chain wants (the big
51
- * low-order phasors first). The DC term (`index 0`) is the curve's centroid; the
52
- * caller can subtract it (draw the chain centered) or keep it (draw at the
53
- * sampled position).
32
+ * The forward DFT — the inverse of {@link positionsAt}. Takes a closed sequence of
33
+ * `[x, y]` samples around a curve (the complex signal `x + i·y`, uniformly spaced in
34
+ * `t ∈ [0, 1)`) and returns the spectrum {@link positionsAt} reconstructs.
54
35
  *
55
- * General: any point set drives it (a glyph outline, a hand-traced path, a
56
- * digitized signature). It is NOT a special case — it is the literal forward
57
- * transform whose inverse already ships as `positionsAt`.
36
+ * Frequencies run symmetrically around 0 — `0, +1, -1, +2, -2, …` up to Nyquist. The DC
37
+ * term (`index 0`) is the curve's centroid; the mint hoists it out as the fit anchor.
38
+ * Any point set drives it: a glyph outline, a hand-traced path, a digitized signature.
58
39
  */
59
- export declare function dftFromPoints(points: [number, number][]): BasisComponent[];
60
- /** Tuning for {@link makeEllipticSpectrum}. */
61
- export interface EllipticSpectrumOptions {
62
- /**
63
- * How many smaller higher-order harmonics to layer on top of the dominant
64
- * counter-rotating pair (the dominant pair is always present). Default 4.
65
- * The `final` preset passes a larger count for a denser, busier curve.
66
- */
67
- harmonics?: number;
68
- /**
69
- * Peak magnitude of the higher-order harmonics relative to the dominant
70
- * phasor. Smaller keeps the curve a clean, smooth ellipse with a little
71
- * character; larger crinkles it. Default ~0.22.
72
- */
73
- harmonicScale?: number;
74
- }
40
+ export declare function dftFromPoints(points: readonly [number, number][]): BasisComponent[];
75
41
  /**
76
- * Emit a procedural elliptic spectrum: a DOMINANT counter-rotating pair (index
77
- * `+1` and `-1`) of UNEQUAL magnitude — the inequality is what draws a tilted
78
- * ellipse rather than a circle — plus a few smaller higher-order harmonics with
79
- * random small magnitudes and phases for character. Lower orders carry more
80
- * weight (a `1 / order` falloff), so the curve stays large, smooth and
81
- * few-big-phasor; the `harmonics` knob lifts it to the denser `final` look.
42
+ * Emit a procedural elliptic spectrum: a DOMINANT counter-rotating pair (index `+1` and
43
+ * `-1`) of UNEQUAL magnitude — the inequality is what draws a tilted ellipse rather than
44
+ * a circle — plus higher-order harmonics with a `1/order` falloff.
45
+ *
46
+ * `richness` ∈ `[0,1]` is the ONE character knob: 0 is a clean ellipse, 1 a crinkled
47
+ * figure. It drives both how many harmonics are offered and how loud they are; the mint's
48
+ * paint floor then decides how many of them survive, so the axis maximum is honest
49
+ * per-seed rather than pinned to a constant.
82
50
  *
83
- * Deterministic in `rng`: the same seeded `() => number` yields the same
84
- * spectrum, so a frozen frame reproduces exactly.
51
+ * Deterministic in `rng`: the same seeded `() => number` yields the same spectrum.
85
52
  */
86
- export declare function makeEllipticSpectrum(rng: () => number, opts?: EllipticSpectrumOptions): BasisComponent[];
53
+ export declare function makeEllipticSpectrum(rng: () => number, richness?: number): BasisComponent[];
87
54
  /** One integer-index harmonic term of a {@link FOURIER_FIGURES} recipe (polar form). */
88
55
  export interface HarmonicTerm {
89
- /** The INTEGER frequency index (a closed figure requires integers — the period-1 fence). */
56
+ /** The INTEGER frequency index — a closed figure requires integers (the period-1 fence). */
90
57
  index: number;
91
58
  /** The phasor magnitude (relative; the figure is scale-fit at render). */
92
59
  mag: number;
93
- /** The phase offset in turns (0..1 — multiplied by 2π; a phase rotates the figure). */
60
+ /** The phase offset in TURNS (0..1 — multiplied by 2π). */
94
61
  phase: number;
95
62
  }
96
63
  /**
97
- * Build a {@link BasisComponent} spectrum from a closed-figure recipe (a list of integer-index
98
- * {@link HarmonicTerm}s). The recipe's `phase` is in TURNS; the rectangular coefficient is
99
- * `mag·(cos, sin)` at `2π·phase`. The result CLOSES (all indices integer) — feed it straight
100
- * to `partialSumAt`/`positionsAt`.
64
+ * Build a spectrum from a closed-figure recipe. Because every index is an INTEGER,
65
+ * `e^{2πi·k·t}` has period 1, so the sum CLOSES at `t = 1` by construction — no term can
66
+ * leave the figure open. Two counter-rotating integer phasors trace an epicycloid whose
67
+ * symmetry order is `|p − q|`; a third small term adds character without breaking closure.
101
68
  */
102
69
  export declare function makeHarmonicFigure(terms: readonly HarmonicTerm[]): BasisComponent[];
103
70
  /**
104
- * The curated FLOWER / closed-epicycle figure catalogue — few-term integer-ratio harmonic
105
- * stacks, each a deliberate figure (the symmetry order is `|p − q|` for the dominant pair).
106
- * Selectable/cyclable by key; the LIBRARY beauty register the `boring` random ellipse lacked.
107
- * The magnitudes stay large + few-phasor so the figure reads bold, not crinkled.
71
+ * The curated closed-figure catalogue — few-term integer-ratio harmonic stacks, each a
72
+ * deliberate figure (the symmetry order is `|p − q|` for the dominant pair). Every entry
73
+ * states its own honest term count: two terms draw a foil, three draw a spirograph.
108
74
  */
109
- export declare const FOURIER_FIGURES: Record<string, HarmonicTerm[]>;
75
+ export declare const FOURIER_FIGURES: {
76
+ trefoil: {
77
+ index: number;
78
+ mag: number;
79
+ phase: number;
80
+ }[];
81
+ quatrefoil: {
82
+ index: number;
83
+ mag: number;
84
+ phase: number;
85
+ }[];
86
+ pentafoil: {
87
+ index: number;
88
+ mag: number;
89
+ phase: number;
90
+ }[];
91
+ hexafoil: {
92
+ index: number;
93
+ mag: number;
94
+ phase: number;
95
+ }[];
96
+ spiro: {
97
+ index: number;
98
+ mag: number;
99
+ phase: number;
100
+ }[];
101
+ };
110
102
  /** The ordered figure keys (the demo/consumer cycle order). */
111
- export declare const FOURIER_FIGURE_KEYS: string[];
103
+ export declare const FOURIER_FIGURE_KEYS: (keyof typeof FOURIER_FIGURES)[];