@mkbabb/glass-ui 3.5.1 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CardAction-D8nSE62a.js +163 -0
- package/dist/CollapsibleContent-D7UNsXQr.js +63 -0
- package/dist/{CommandShortcut-BiVyqipe.js → CommandShortcut-CY0aQTz5.js} +4 -4
- package/dist/{ContextMenuContent-De01_83g.js → ContextMenuContent-DGKZX1Du.js} +1 -1
- package/dist/{ContextMenuSubContent-DrWkXKQP.js → ContextMenuSubContent-BeDz7G6f.js} +4 -4
- package/dist/{DataTable-DyKc_26a.js → DataTable-gtPOLZvl.js} +14 -7
- package/dist/{DialogContent-IQ8_BRrC.js → DialogContent-DG1YMR-P.js} +24 -24
- package/dist/{DialogFooter-Er0wA3K6.js → DialogFooter-BCIaDP15.js} +2 -2
- package/dist/{DiscoGlyph-BaZ8OawK.js → DiscoGlyph-ZoBYpoxI.js} +1 -1
- package/dist/{GlyphFace-B_7vOmYn.js → GlyphFace-Ce_oe3tM.js} +1 -1
- package/dist/{HoverCardContent-ICfIMZX1.js → HoverCardContent-Bn0UBMY9.js} +1 -1
- package/dist/{HoverPopover-DTSYkJtw.js → HoverPopover-DXpOEw1P.js} +1 -1
- package/dist/{IconTooltip-BTyYn4mr.js → IconTooltip-Cu86JR4e.js} +1 -1
- package/dist/{Input-CU9CNKmo.js → Input-DWQQIOC4.js} +4 -1
- package/dist/{Label-CZk-3nTc.js → Label-DBc7bKzZ.js} +1 -1
- package/dist/{MetricBadge-BlrdbOGN.js → MetricBadge-D-bRaQUD.js} +1 -1
- package/dist/{Notification-DP_ApJLo.js → Notification-B5WHP3_1.js} +5 -4
- package/dist/{NumberFieldContent-jO-zGOHf.js → NumberFieldContent-CLNVR1iY.js} +5 -5
- package/dist/{PopoverContent-B8WtJECb.js → PopoverContent-F1bWnYoh.js} +2 -2
- package/dist/{Progress-DjM86vfb.js → Progress-c4vo4ALi.js} +6 -6
- package/dist/{ScrollingText-P9o_DuMn.js → ScrollingText-BEp377U5.js} +2 -2
- package/dist/{SelectScrollDownButton-BGn3rjs9.js → SelectScrollDownButton-DAo5NmY_.js} +8 -7
- package/dist/{Separator-C2XtAXRp.js → Separator-DZyqsCum.js} +1 -1
- package/dist/{Switch-x8n6husW.js → Switch-2rsAtDfA.js} +8 -2
- package/dist/TabsList-COqIdW7i.js +101 -0
- package/dist/{Toaster-DdhMKfus.js → Toaster-BMaeziJJ.js} +54 -60
- package/dist/{ToggleGroupItem-gyXj998A.js → ToggleGroupItem-C-AIkBUw.js} +13 -5
- package/dist/{TooltipProvider-D-JrSqDu.js → TooltipProvider-CD2IXfRv.js} +1 -1
- package/dist/{UnderlineTabs-Cq_AD03t.js → UnderlineTabs-D_c1iYo-.js} +1 -1
- package/dist/animated-digit.js +2 -2
- package/dist/api/index.d.ts +2 -1
- package/dist/api.js +1 -1
- package/dist/aurora.js +845 -204
- package/dist/{badge-Cl6JZ1B7.js → badge-DHQrc_GQ.js} +13 -9
- package/dist/badge.js +1 -1
- package/dist/{button-i8Ek5vvg.js → button-CAywcrhO.js} +8 -5
- package/dist/button.js +1 -1
- package/dist/card.js +2 -2
- package/dist/carousel.js +5 -5
- package/dist/{check-CdkxGxXJ.js → check-BBKKKSTf.js} +1 -1
- package/dist/{chevron-down-pBY8sYfV.js → chevron-down-BSkTP2aL.js} +1 -1
- package/dist/{chevron-right-BjeKC22V.js → chevron-right-sDM0Yvrf.js} +1 -1
- package/dist/{chevron-up-DBeNHUm1.js → chevron-up-CCOD_FTz.js} +1 -1
- package/dist/collapsible.js +1 -1
- package/dist/color-DjJCAQ6f.js +76 -0
- package/dist/color.js +2 -2
- package/dist/command.js +1 -1
- package/dist/compile-iM4IUB66.js +158 -0
- package/dist/components/custom/aurora/Aurora.vue.d.ts +1 -0
- package/dist/components/custom/aurora/composables/atoms.d.ts +60 -0
- package/dist/components/custom/aurora/composables/color.d.ts +36 -9
- package/dist/components/custom/aurora/composables/cursorModel.d.ts +35 -2
- package/dist/components/custom/aurora/composables/glSetup.d.ts +1 -1
- package/dist/components/custom/aurora/composables/gpuRuntime.d.ts +16 -0
- package/dist/components/custom/aurora/composables/runtime.d.ts +18 -0
- package/dist/components/custom/aurora/composables/uniformBridge.d.ts +29 -0
- package/dist/components/custom/aurora/composables/useAurora.d.ts +6 -0
- package/dist/components/custom/aurora/composables/useCursorInteraction.d.ts +8 -0
- package/dist/components/custom/aurora/constants/presets.d.ts +64 -2
- package/dist/components/custom/aurora/constants/renderMode.d.ts +10 -0
- package/dist/components/custom/aurora/constants/shaders/aurora.wgsl.d.ts +7 -0
- package/dist/components/custom/aurora/constants/shaders/brush.glsl.d.ts +1 -1
- package/dist/components/custom/aurora/constants/shaders/composition.glsl.d.ts +1 -1
- package/dist/components/custom/aurora/constants/shaders/flow.glsl.d.ts +1 -1
- package/dist/components/custom/aurora/constants/shaders/mediums.glsl.d.ts +2 -2
- package/dist/components/custom/aurora/constants/shaders/painterly.wgsl.d.ts +7 -0
- package/dist/components/custom/aurora/constants/shaders/wake.wgsl.d.ts +4 -0
- package/dist/components/custom/aurora/index.d.ts +5 -3
- package/dist/components/custom/constellation/Constellation.vue.d.ts +45 -0
- package/dist/components/custom/constellation/constellationField.d.ts +85 -0
- package/dist/components/custom/constellation/index.d.ts +3 -0
- package/dist/components/custom/dock/GlassDock.vue.d.ts +34 -3
- package/dist/components/custom/fourier-field/FourierField.vue.d.ts +38 -0
- package/dist/components/custom/fourier-field/index.d.ts +2 -0
- package/dist/components/custom/fourier-field/math.d.ts +57 -0
- package/dist/components/custom/goo-blob/GooBlob.vue.d.ts +2 -0
- package/dist/components/custom/goo-blob/composables/useBlobMood.d.ts +14 -2
- package/dist/components/custom/goo-blob/composables/useBlobPointer.d.ts +45 -4
- package/dist/components/custom/goo-blob/composables/useMetaballRenderer.d.ts +1 -1
- package/dist/components/custom/goo-blob/index.d.ts +1 -1
- package/dist/components/custom/goo-blob/shaders/sdf-body.glsl.d.ts +1 -1
- package/dist/components/custom/goo-blob/shaders/watercolor-edges.glsl.d.ts +2 -2
- package/dist/components/custom/goo-blob/types.d.ts +40 -0
- package/dist/components/custom/metric-cell/MetricCell.vue.d.ts +1 -1
- package/dist/components/custom/metric-stack/MetricStack.vue.d.ts +1 -1
- package/dist/components/ui/_shared/ModalOverlay.vue.d.ts +1 -1
- package/dist/components/ui/alert/index.d.ts +1 -1
- package/dist/components/ui/card/Card.vue.d.ts +10 -3
- package/dist/components/ui/card/CardAction.vue.d.ts +24 -0
- package/dist/components/ui/card/index.d.ts +1 -0
- package/dist/components/ui/collapsible/CollapsibleTrigger.vue.d.ts +5 -1
- package/dist/components/ui/tabs/TabsList.vue.d.ts +13 -3
- package/dist/components/ui/toast/Toast.vue.d.ts +5 -5
- package/dist/components/ui/toast/use-toast.d.ts +1 -1
- package/dist/composables/color/index.d.ts +50 -0
- package/dist/composables/glass/canvas2d/index.d.ts +2 -0
- package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +48 -0
- package/dist/composables/glass/createGPUCanvas.d.ts +40 -0
- package/dist/composables/glass/index.d.ts +2 -0
- package/dist/composables/glass/webgl/createCanvasLifecycle.d.ts +45 -0
- package/dist/composables/glass/webgl/shaders/procedural-color.glsl.d.ts +3 -0
- package/dist/composables/glass/webgl/useWebGLCanvas.d.ts +4 -4
- package/dist/configurator.js +1 -1
- package/dist/confirm-dialog.js +3 -3
- package/dist/constellation.d.ts +1 -0
- package/dist/constellation.js +182 -0
- package/dist/context-menu.js +2 -2
- package/dist/controls.js +2 -2
- package/dist/dark.js +1 -1
- package/dist/data-table.js +1 -1
- package/dist/dialog.js +2 -2
- package/dist/disco-glyph.js +1 -1
- package/dist/dock.js +129 -110
- package/dist/{dockContext-spUj_-E5.js → dockContext-DLgrVWsU.js} +1 -1
- package/dist/dom.js +5 -5
- package/dist/{dropdown-menu-EFjl5iKo.js → dropdown-menu-BWpwutt0.js} +5 -5
- package/dist/dropdown-menu.js +1 -1
- package/dist/expandable-container.js +2 -2
- package/dist/forms.js +10 -7
- package/dist/fourier-field.d.ts +1 -0
- package/dist/fourier-field.js +166 -0
- package/dist/glass-carousel.js +1 -1
- package/dist/glass-panel.js +2 -2
- package/dist/glass-ui.css +1 -1
- package/dist/glass-ui.js +207 -191
- package/dist/glyph-face.js +2 -2
- package/dist/goo-blob.js +336 -234
- package/dist/header-ribbon.js +1 -1
- package/dist/hover-card.js +1 -1
- package/dist/hover-popover.js +1 -1
- package/dist/icon-tooltip.js +1 -1
- package/dist/instrument-chassis.js +1 -1
- package/dist/instrument-rail.js +1 -1
- package/dist/keyboard.js +1 -1
- package/dist/{keys-BFoma9vm.js → keys-Dyf_uTs5.js} +1 -1
- package/dist/label.js +1 -1
- package/dist/labeled-field.js +6 -6
- package/dist/metric-badge.js +1 -1
- package/dist/metric-stack.js +1 -1
- package/dist/{minimize-2-BP27-qBY.js → minimize-2-lcXOuc6V.js} +1 -1
- package/dist/motion-core.js +76 -106
- package/dist/motion.js +3 -3
- package/dist/notification.js +1 -1
- package/dist/number-field.js +1 -1
- package/dist/paper-backdrop.js +1 -1
- package/dist/popover.js +1 -1
- package/dist/progress.js +1 -1
- package/dist/pulse.js +1 -1
- package/dist/reactive.js +2 -2
- package/dist/responsive-tabs.js +2 -2
- package/dist/scrolling-text.js +1 -1
- package/dist/{search-DBG8qaRs.js → search-nbkvr2dC.js} +1 -1
- package/dist/search.js +8 -8
- package/dist/select.js +3 -3
- package/dist/separator.js +1 -1
- package/dist/{sheet-CQYYrkr9.js → sheet-Cv1uO-Gq.js} +4 -4
- package/dist/sheet.js +1 -1
- package/dist/{slider-CgNzRSh6.js → slider-BA9wIs5e.js} +5 -4
- package/dist/slider.js +1 -1
- package/dist/sortable-list.js +3 -3
- package/dist/styles/components.css +1 -1
- package/dist/styles/dock.css +185 -1
- package/dist/styles/glass-refract.css +60 -0
- package/dist/styles/glass-specular-track.css +43 -130
- package/dist/styles/glass.css +243 -27
- package/dist/styles/index.css +1 -0
- package/dist/styles/theme.css +55 -0
- package/dist/styles/tokens.css +33 -0
- package/dist/styles/typography.css +3 -3
- package/dist/styles/utilities.css +17 -13
- package/dist/supportsCssTimeline-C5oe0lnD.js +12 -0
- package/dist/switch.js +1 -1
- package/dist/tabs.js +4 -4
- package/dist/timeline.js +2 -2
- package/dist/toast.js +1 -1
- package/dist/toggle-group.js +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/typewriter.js +1 -1
- package/dist/{useAnimatedNumber-BF6r64lA.js → useAnimatedNumber-C5I3wWd8.js} +1 -1
- package/dist/useCanvas2D-C31yfUGY.js +95 -0
- package/dist/{useConfiguratorState-DUtC1jxr.js → useConfiguratorState-B24TcgIn.js} +5 -5
- package/dist/useScrollProgress-CAH8Ofwt.js +35 -0
- package/dist/{useTouchGate-CS5Csc2h.js → useTouchGate-YQRwyUGU.js} +1 -1
- package/dist/{vReveal-u2wSG2El.js → vReveal-BE2TsPoR.js} +6 -17
- package/dist/watercolor-dot.js +2 -2
- package/dist/{x-C4pz9nbD.js → x-Dfsegz9Y.js} +1 -1
- package/package.json +49 -1
- package/src/styles/dock.css +185 -1
- package/src/styles/glass-refract.css +60 -0
- package/src/styles/glass-specular-track.css +43 -130
- package/src/styles/glass.css +243 -27
- package/src/styles/index.css +1 -0
- package/src/styles/theme.css +55 -0
- package/src/styles/tokens.css +33 -0
- package/src/styles/typography.css +3 -3
- package/src/styles/utilities.css +17 -13
- package/dist/CardFooter-C390imy7.js +0 -129
- package/dist/CollapsibleContent-cauTbZEM.js +0 -52
- package/dist/TabsIndicator-DA0x9gPr.js +0 -97
- package/dist/color-rkK4RMx2.js +0 -33
- package/dist/compile-DVgAxagk.js +0 -106
- /package/dist/{InstrumentChassis-DaHIZCqy.js → InstrumentChassis-BLJBl1BW.js} +0 -0
- /package/dist/{InstrumentRail-B0qqLFN0.js → InstrumentRail-BElFwKyt.js} +0 -0
- /package/dist/{ModalOverlay-B_CBtqcE.js → ModalOverlay-D1VrWyoi.js} +0 -0
- /package/dist/{PaperBackdrop-D_YZW47j.js → PaperBackdrop-DCGoGatv.js} +0 -0
- /package/dist/{SelectGroup-CMdoCjRE.js → SelectGroup-BcaFjvP4.js} +0 -0
- /package/dist/{SelectSeparator-CaJnPF3_.js → SelectSeparator-COFMJGng.js} +0 -0
- /package/dist/{_plugin-vue_export-helper-C1je1s0u.js → _plugin-vue_export-helper-gLzyemvl.js} +0 -0
- /package/dist/{constants-DsCdlK9I.js → constants-CS75AWHG.js} +0 -0
- /package/dist/{createContext-DBMGRlx4.js → createContext-CvNyBk4o.js} +0 -0
- /package/dist/{createLucideIcon-DuDoe_ra.js → createLucideIcon-B6uNAU9G.js} +0 -0
- /package/dist/{menuItemVariants-C2QlXqT3.js → menuItemVariants-B4eFR5Ix.js} +0 -0
- /package/dist/{presets-1OhFpaIC.js → presets-BKVyQfbt.js} +0 -0
- /package/dist/{prng-Bz_1Tydc.js → prng-wHuHlNHE.js} +0 -0
- /package/dist/{useGlassRenderer-Dn3WpfG-.js → useGlassRenderer-HebgSEgN.js} +0 -0
- /package/dist/{useGlobalDark-CWiaCoEw.js → useGlobalDark-yhL4Wp-s.js} +0 -0
- /package/dist/{useIntersectionPause-DAdVPVp4.js → useIntersectionPause-Cc-H9ees.js} +0 -0
- /package/dist/{useInterval-CHVYFpXV.js → useInterval-D3USn0yc.js} +0 -0
- /package/dist/{useKeyboardShortcuts-BQfnAHHW.js → useKeyboardShortcuts-Be3w_Rwk.js} +0 -0
- /package/dist/{useResizeObserver-DX-STszm.js → useResizeObserver-CSyxGxIX.js} +0 -0
- /package/dist/{useSortable-DnyGXKKY.js → useSortable-C8ReQ50P.js} +0 -0
- /package/dist/{useSpringMount-CnizvZGm.js → useSpringMount-LRZqJTaF.js} +0 -0
- /package/dist/{useTextHighlight-Dmtofpk2.js → useTextHighlight-aMPdEpkq.js} +0 -0
- /package/dist/{useTimer-DGgoxTXL.js → useTimer-DEJonOZa.js} +0 -0
- /package/dist/{useUserInvalidAria-DmvZ_6Dx.js → useUserInvalidAria-CQKjEfwP.js} +0 -0
- /package/dist/{useViewTransition-CJnqg6wl.js → useViewTransition-DaN5Zrox.js} +0 -0
- /package/dist/{useViewportReady-Dcvf0c4p.js → useViewportReady-K96m-Mdv.js} +0 -0
|
@@ -72,6 +72,15 @@ export interface AuroraRuntimeOptions {
|
|
|
72
72
|
* `instance.arm()` on the deferred path — rethrows from `arm()`.
|
|
73
73
|
*/
|
|
74
74
|
onInitError?: (err: Error) => void;
|
|
75
|
+
/**
|
|
76
|
+
* AW.W7b — the WebGPU backend route. When a `GPUDevice` is supplied (the
|
|
77
|
+
* `resolveRenderModeAsync` probe resolved a non-fallback adapter), the runtime
|
|
78
|
+
* routes to `createGPUCanvas` drawing the SAME single-pass aurora over WebGPU;
|
|
79
|
+
* otherwise the WebGL2 fragment path is the universal fallback. `useAurora` runs
|
|
80
|
+
* the probe past first paint (the CSS placeholder paints first) and threads the
|
|
81
|
+
* device here. `null`/absent → the WebGL2 path (the zero-regression default).
|
|
82
|
+
*/
|
|
83
|
+
gpuDevice?: GPUDevice | null;
|
|
75
84
|
}
|
|
76
85
|
/**
|
|
77
86
|
* The concrete `createAurora` return shape. It IS an {@link AuroraInstance}
|
|
@@ -83,5 +92,14 @@ export interface AuroraRuntimeOptions {
|
|
|
83
92
|
export interface AuroraRuntime extends Omit<AuroraInstance, "pause" | "resume"> {
|
|
84
93
|
pause(reason?: SuspendReason): void;
|
|
85
94
|
resume(reason?: SuspendReason): void;
|
|
95
|
+
/**
|
|
96
|
+
* AW.W8.1 — feed a pointer delta into the velocity-reactive flow (a fast flick →
|
|
97
|
+
* a transient swirl-burst). The PRM early-out lives here: the injection is
|
|
98
|
+
* suppressed when the substrate reports reduced-motion (the cursor write-path
|
|
99
|
+
* fires from the pointermove listener, INDEPENDENT of the parked rAF loop).
|
|
100
|
+
*/
|
|
101
|
+
injectCursorVelocity(dx: number, dy: number): void;
|
|
102
|
+
/** AW.W8.1 — the live reduced-motion state (the cursor listener early-outs on it). */
|
|
103
|
+
readonly reducedMotion: boolean;
|
|
86
104
|
}
|
|
87
105
|
export declare function createAurora(canvas: HTMLCanvasElement, initial: AuroraConfig, options?: AuroraRuntimeOptions): AuroraRuntime;
|
|
@@ -24,6 +24,14 @@ export declare const MEDIUM_ID: {
|
|
|
24
24
|
readonly watercolor: 2;
|
|
25
25
|
readonly oil: 3;
|
|
26
26
|
readonly crayon: 4;
|
|
27
|
+
readonly vangogh: 5;
|
|
28
|
+
readonly "oil-pastel": 6;
|
|
29
|
+
};
|
|
30
|
+
export declare const HUE_PATH_ID: {
|
|
31
|
+
readonly shorter: 0;
|
|
32
|
+
readonly longer: 1;
|
|
33
|
+
readonly increasing: 2;
|
|
34
|
+
readonly decreasing: 3;
|
|
27
35
|
};
|
|
28
36
|
export declare const FLOW_ID: {
|
|
29
37
|
readonly none: 0;
|
|
@@ -31,6 +39,11 @@ export declare const FLOW_ID: {
|
|
|
31
39
|
readonly swirl: 2;
|
|
32
40
|
readonly diagonal: 3;
|
|
33
41
|
readonly multi: 4;
|
|
42
|
+
readonly tensor: 5;
|
|
43
|
+
};
|
|
44
|
+
export declare const STROKE_ORIENT_ID: {
|
|
45
|
+
readonly flow: 0;
|
|
46
|
+
readonly tensor: 1;
|
|
34
47
|
};
|
|
35
48
|
export declare const WARP_ID: {
|
|
36
49
|
readonly fbm: 0;
|
|
@@ -55,6 +68,22 @@ export declare function resolveMediumId(cfg: AuroraConfig): number;
|
|
|
55
68
|
* oil default; the other modes map directly.
|
|
56
69
|
*/
|
|
57
70
|
export declare function resolveStrokeModeId(cfg: AuroraConfig): number;
|
|
71
|
+
/**
|
|
72
|
+
* AW.W4.1/W4.3/W4.4 — the `uStrokeOrient` int for a config. The painterly mediums
|
|
73
|
+
* (`vangogh`, `oil-pastel`) FORCE the structure-tensor orientation regardless of
|
|
74
|
+
* the config's `strokeOrient` field — their brushwork must hug the color zones (the
|
|
75
|
+
* congruent-to-real-Van-Gogh contract). Every other medium honors the config's
|
|
76
|
+
* `strokeOrient` (default `"flow"` — the pre-W4 path renders identically).
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveStrokeOrientId(cfg: AuroraConfig): number;
|
|
79
|
+
export declare const WGPU_UNIFORM_FLOATS: number;
|
|
80
|
+
/**
|
|
81
|
+
* Pack an `AuroraConfig` + cursor into the std140 WGSL Uniforms layout (the WebGPU
|
|
82
|
+
* write path). Fills `out` in place (the caller owns one reused buffer so a slider
|
|
83
|
+
* drag does not churn the GC). The WebGL2 fallback path is unaffected (it keeps its
|
|
84
|
+
* `gl.uniform*` calls). The float layout MUST mirror aurora.wgsl.ts `struct Uniforms`.
|
|
85
|
+
*/
|
|
86
|
+
export declare function packGPUUniforms(cfg: AuroraConfig, timeSec: number, out: Float32Array): Float32Array;
|
|
58
87
|
/**
|
|
59
88
|
* Build the per-config uniform uploader bound to a program's uniform location
|
|
60
89
|
* cache + cursor state. Returns `uploadConfig` — call it to push a fresh
|
|
@@ -33,6 +33,12 @@ export interface UseAuroraReturn {
|
|
|
33
33
|
setCursor: (x: number, y: number, strength?: number) => void;
|
|
34
34
|
clearCursor: () => void;
|
|
35
35
|
setCursorRadius: (r: number) => void;
|
|
36
|
+
/**
|
|
37
|
+
* AW.W8.1 — feed a pointer delta into the velocity-reactive flow (a fast flick →
|
|
38
|
+
* a transient swirl-burst). PRM-gated at the runtime (the cursor write-path
|
|
39
|
+
* early-out). The aurora demo wires this from its stage pointermove listener.
|
|
40
|
+
*/
|
|
41
|
+
injectCursorVelocity: (dx: number, dy: number) => void;
|
|
36
42
|
renderAt: (t: number) => void;
|
|
37
43
|
pause: () => void;
|
|
38
44
|
resume: () => void;
|
|
@@ -10,6 +10,14 @@ export interface CursorInteractionOptions {
|
|
|
10
10
|
* 0.35 matches the overlay's default ring thickness.
|
|
11
11
|
*/
|
|
12
12
|
hitFraction?: number;
|
|
13
|
+
/**
|
|
14
|
+
* AW.W8.1 — feed the per-move pointer delta into the velocity-reactive flow (a
|
|
15
|
+
* fast flick → a transient swirl-burst). The runtime's `injectCursorVelocity`
|
|
16
|
+
* EARLY-OUTS on reduced-motion (the cursor write-path PRM check), so a parked
|
|
17
|
+
* loop with a live pointermove does NOT move the field under reduce. Optional —
|
|
18
|
+
* absent keeps the position-only steady swirl.
|
|
19
|
+
*/
|
|
20
|
+
injectVelocity?: (dx: number, dy: number) => void;
|
|
13
21
|
}
|
|
14
22
|
/**
|
|
15
23
|
* Wires pointer events on the stage element to (a) continuous cursor swirl,
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { OklchStop } from "../../../../composables/color";
|
|
14
14
|
export type { OklchStop };
|
|
15
|
+
import type { HueInterpolationMethod } from "@mkbabb/value.js";
|
|
16
|
+
export type AuroraHuePath = HueInterpolationMethod;
|
|
15
17
|
export interface AuroraNucleus {
|
|
16
18
|
/** 0..1 in CSS-top-origin space (0 = top, 1 = bottom). Runtime flips Y. */
|
|
17
19
|
x: number;
|
|
@@ -37,11 +39,35 @@ export interface AuroraNucleus {
|
|
|
37
39
|
*/
|
|
38
40
|
angle?: number;
|
|
39
41
|
}
|
|
40
|
-
export type AuroraMedium = "smooth" | "pastel" | "watercolor" | "oil";
|
|
42
|
+
export type AuroraMedium = "smooth" | "pastel" | "watercolor" | "oil" | "vangogh" | "oil-pastel";
|
|
41
43
|
/** Applied only when `medium === "oil"`. Routes inside the shader. */
|
|
42
44
|
export type StrokeMode = "oil" | "knife" | "crayon" | "chunky";
|
|
43
|
-
export type FlowPattern = "none" | "radial" | "swirl" | "diagonal" | "multi";
|
|
45
|
+
export type FlowPattern = "none" | "radial" | "swirl" | "diagonal" | "multi" | "tensor";
|
|
46
|
+
/**
|
|
47
|
+
* AW.W4.1 — how a stroke's orientation is sourced. `flow` keeps the hand-authored
|
|
48
|
+
* `flowField` pattern + per-cell jitter (the named legacy choice); `tensor`
|
|
49
|
+
* substitutes the structure-tensor minor-eigenvector (the color field's own
|
|
50
|
+
* edge-tangent flow). No default that hides one as a duplicate of the other.
|
|
51
|
+
*/
|
|
52
|
+
export type StrokeOrient = "flow" | "tensor";
|
|
44
53
|
export type WarpMode = "fbm" | "cellular" | "hybrid";
|
|
54
|
+
/**
|
|
55
|
+
* AW.W6/W8 — the interactivity flag SHAPE (declared at W6, default OFF; behavior
|
|
56
|
+
* wired at W8). Each axis opts the aurora into a pointer/scroll response:
|
|
57
|
+
* - `light` — the cursor drives the impasto `uLightDir` (cursor-as-light + idle orbit).
|
|
58
|
+
* - `flow` — pointer/scroll VELOCITY injects a transient swirl-burst (velocity-reactive).
|
|
59
|
+
* - `scroll` — palette/breath progress couples to scroll (via `useScrollProgress`).
|
|
60
|
+
* - `wake` — the WebGPU stateful pointer wake (a ping-pong velocity texture; AW.W8.2).
|
|
61
|
+
* Every axis is suppressed under `prefers-reduced-motion` + the DockBackgroundToggle
|
|
62
|
+
* pause (the master tempo scalar is the single suppression seam). The wispy-sky
|
|
63
|
+
* default carries no `interactivity` (every axis OFF — the default stays static).
|
|
64
|
+
*/
|
|
65
|
+
export interface AuroraInteractivity {
|
|
66
|
+
light?: boolean;
|
|
67
|
+
flow?: boolean;
|
|
68
|
+
scroll?: boolean;
|
|
69
|
+
wake?: boolean;
|
|
70
|
+
}
|
|
45
71
|
export interface AuroraFlow {
|
|
46
72
|
pattern: FlowPattern;
|
|
47
73
|
/** 0..1 CSS-top-origin. Runtime flips Y. */
|
|
@@ -63,7 +89,22 @@ export interface AuroraConfig {
|
|
|
63
89
|
warpMode: WarpMode;
|
|
64
90
|
noiseOctaves: 3 | 4 | 5;
|
|
65
91
|
medium: AuroraMedium;
|
|
92
|
+
/**
|
|
93
|
+
* W5 — the hue-arc method for the OKLCh palette interpolation. Optional;
|
|
94
|
+
* omitted = `"shorter"` (the OKLab-rectangular ramp — the muddy-midtone-free
|
|
95
|
+
* default). `"increasing"`/`"decreasing"` request the OKLCh hue-arc for a
|
|
96
|
+
* deliberate rainbow sweep across the stops. Aliased on `/api` as `AuroraHuePath`.
|
|
97
|
+
*/
|
|
98
|
+
huePath?: AuroraHuePath;
|
|
66
99
|
flow: AuroraFlow;
|
|
100
|
+
/**
|
|
101
|
+
* AW.W4.1 — how the painterly stroke direction is sourced. Optional; omitted =
|
|
102
|
+
* `"flow"` (the hand-authored `flowField` pattern — the pre-W4 path, so an
|
|
103
|
+
* unset config renders identically). `"tensor"` substitutes the structure-tensor
|
|
104
|
+
* minor eigenvector (the color field's own edge-tangent flow); the `vangogh` /
|
|
105
|
+
* `oil-pastel` mediums force it.
|
|
106
|
+
*/
|
|
107
|
+
strokeOrient?: StrokeOrient;
|
|
67
108
|
strokeAmount: number;
|
|
68
109
|
strokeScale: number;
|
|
69
110
|
strokeAnisotropy: number;
|
|
@@ -74,6 +115,20 @@ export interface AuroraConfig {
|
|
|
74
115
|
impasto: number;
|
|
75
116
|
brokenColor: number;
|
|
76
117
|
canvasGrain: number;
|
|
118
|
+
/**
|
|
119
|
+
* AW.W4.2 — the impasto relight direction (the movable directional source the
|
|
120
|
+
* accumulated paint-height field catches). Optional; omitted = upper-left
|
|
121
|
+
* (matching the prior fixed-rim default, so the still default reads identically).
|
|
122
|
+
* Unit-ish [x, y, z] in screen space (z toward the viewer). AW.W8 drives this
|
|
123
|
+
* from the cursor (cursor-as-light); the shader re-normalizes it.
|
|
124
|
+
*/
|
|
125
|
+
lightDir?: [number, number, number];
|
|
126
|
+
/**
|
|
127
|
+
* AW.W4.2 — the relight tint (linear-light RGB). Optional; omitted = a warm
|
|
128
|
+
* white (the canonical impasto catch-light). The diffuse + Blinn-specular terms
|
|
129
|
+
* modulate by this.
|
|
130
|
+
*/
|
|
131
|
+
lightColor?: [number, number, number];
|
|
77
132
|
nucleiDrift: number;
|
|
78
133
|
paletteDrift: number;
|
|
79
134
|
breathDepth: number;
|
|
@@ -81,6 +136,13 @@ export interface AuroraConfig {
|
|
|
81
136
|
saturation: number;
|
|
82
137
|
paperGrain: number;
|
|
83
138
|
alpha: number;
|
|
139
|
+
/**
|
|
140
|
+
* AW.W6/W8 — the pointer/scroll interactivity opt-in (default OFF — the wispy-sky
|
|
141
|
+
* default stays static). W6 declares the SHAPE; W8 wires the cursor-as-light /
|
|
142
|
+
* velocity-reactive flow / scroll-coupling / WebGPU-wake behavior. Omitted = every
|
|
143
|
+
* axis off. Aliased on `/api` as part of the `AuroraConfig` surface.
|
|
144
|
+
*/
|
|
145
|
+
interactivity?: AuroraInteractivity;
|
|
84
146
|
}
|
|
85
147
|
/**
|
|
86
148
|
* Pointer-cursor API exposed by `createAurora`. Values in 0..1 (CSS-top-origin).
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
* low-power / reduced-motion / save-data devices.
|
|
8
8
|
*/
|
|
9
9
|
export type AuroraRenderMode = "webgl" | "css" | "auto";
|
|
10
|
+
/**
|
|
11
|
+
* AW.W7b — the resolved render SUBSTRATE after the async WebGPU probe. `"webgpu"` is
|
|
12
|
+
* the capability-gated enhancement; `"webgl"` the universal fallback; `"css"` the
|
|
13
|
+
* placeholder floor.
|
|
14
|
+
*/
|
|
15
|
+
export type AuroraSubstrate = "webgpu" | "webgl" | "css";
|
|
10
16
|
/**
|
|
11
17
|
* Resolve `"auto"` to a concrete substrate per device tier. `"webgl"` and
|
|
12
18
|
* `"css"` pass through unchanged.
|
|
@@ -28,3 +34,7 @@ export type AuroraRenderMode = "webgl" | "css" | "auto";
|
|
|
28
34
|
* the consumer remounts to re-evaluate, matching the deferred-arm contract).
|
|
29
35
|
*/
|
|
30
36
|
export declare function resolveRenderMode(mode: AuroraRenderMode): "webgl" | "css";
|
|
37
|
+
export declare function resolveRenderModeAsync(mode: AuroraRenderMode): Promise<{
|
|
38
|
+
substrate: AuroraSubstrate;
|
|
39
|
+
device: GPUDevice | null;
|
|
40
|
+
}>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const WGSL_MAX_NUCLEI = 6;
|
|
2
|
+
declare const WGSL_MAX_STOPS = 8;
|
|
3
|
+
export declare const AURORA_VERTEX_WGSL = "\nstruct VSOut {\n @builtin(position) pos: vec4f,\n @location(0) uv: vec2f,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {\n // Full-screen triangle (matches the WebGL2 [-1,-1, 3,-1, -1,3] quad).\n var p = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));\n var out: VSOut;\n let xy = p[vi];\n out.pos = vec4f(xy, 0.0, 1.0);\n out.uv = 0.5 * (xy + vec2f(1.0));\n return out;\n}";
|
|
4
|
+
export declare const AURORA_FRAGMENT_WGSL = "\nconst PI: f32 = 3.141592653589793;\nconst W_LUMA: vec3f = vec3f(0.2126, 0.7152, 0.0722);\nconst MAX_NUCLEI: u32 = 6u;\nconst MAX_STOPS: u32 = 8u;\n\n// The std140 uniform struct \u2014 the WebGPU write path packs an AuroraConfig here.\nstruct Uniforms {\n time: f32,\n stopCount: i32,\n nucleiCount: i32,\n softmaxBeta: f32,\n valueVariance: f32,\n warpAmount: f32,\n warpScale: f32,\n warpDrift: f32,\n warpMode: i32,\n noiseOctaves: i32,\n medium: i32,\n breathDepth: f32,\n breathPeriod: f32,\n saturation: f32,\n paperGrain: f32,\n alpha: f32,\n palette: array<vec4f, 8>, // .xyz = linear-sRGB stop\n nucleiPos: array<vec4f, 6>, // .xy pos, .z radius, .w paletteBias\n nucleiMod: array<vec4f, 6>, // .x valueBias, .y elong, .z angle, .w driftRadius\n};\n\n@group(0) @binding(0) var<uniform> U: Uniforms;\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n return select(pow((c + 0.055) / 1.055, 2.4), c / 12.92, c <= 0.04045);\n}\nfn srgbToLinear(c: vec3f) -> vec3f {\n return vec3f(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\nfn linearToSrgbCh(c: f32) -> f32 {\n return select(1.055 * pow(c, 1.0 / 2.4) - 0.055, c * 12.92, c <= 0.0031308);\n}\nfn linearToSrgb(c: vec3f) -> vec3f {\n return vec3f(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\nconst FBM_ROT: mat2x2f = mat2x2f(0.8, 0.6, -0.6, 0.8);\n\nconst LINEAR_SRGB_TO_LMS: mat3x3f = mat3x3f(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\nconst LMS_TO_OKLAB: mat3x3f = mat3x3f(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\nconst OKLAB_TO_LMS: mat3x3f = mat3x3f(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\nconst LMS_TO_LINEAR_SRGB: mat3x3f = mat3x3f(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn srgbToOklab(c: vec3f) -> vec3f {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n let lmsCbrt = sign(lms) * pow(abs(lms), vec3f(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\nfn oklabToLinearSrgb(lab: vec3f) -> vec3f {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\nfn oklabToOklch(lab: vec3f) -> vec3f {\n let C = length(lab.yz);\n var H = atan2(lab.z, lab.y);\n if (H < 0.0) { H = H + 2.0 * PI; }\n return vec3f(lab.x, C, H);\n}\nfn oklchToOklab(lch: vec3f) -> vec3f {\n return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\nfn hash21(p0: vec2f) -> f32 {\n var p = fract(p0 * vec2f(123.34, 456.21));\n p = p + dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\nfn vnoise(p: vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = hash21(i);\n let b = hash21(i + vec2f(1.0, 0.0));\n let c = hash21(i + vec2f(0.0, 1.0));\n let d = hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\nfn fbm(p0: vec2f) -> f32 {\n var v = 0.0;\n var a = 0.5;\n var p = p0;\n for (var i = 0; i < 5; i = i + 1) {\n if (i >= U.noiseOctaves) { break; }\n v = v + a * vnoise(p);\n p = FBM_ROT * p * 2.02;\n a = a * 0.5;\n }\n return v;\n}\n\nfn samplePalette(id: f32) -> vec3f {\n // Nearest-pair OKLab interpolation across the stops (mirrors the GLSL default ramp).\n let n = U.stopCount;\n if (n <= 1) { return U.palette[0].xyz; }\n let t = clamp(id, 0.0, 1.0) * f32(n - 1);\n let i0 = i32(floor(t));\n let i1 = min(i0 + 1, n - 1);\n let f = t - f32(i0);\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * U.palette[i0].xyz) * pow(abs(LINEAR_SRGB_TO_LMS * U.palette[i0].xyz), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * U.palette[i1].xyz) * pow(abs(LINEAR_SRGB_TO_LMS * U.palette[i1].xyz), vec3f(1.0 / 3.0)));\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\nfn domainWarp(p: vec2f, t: f32) -> vec2f {\n let drift = t * U.warpDrift * 5.0;\n let q = vec2f(\n fbm(p * U.warpScale + vec2f(drift, 0.0)),\n fbm(p * U.warpScale + vec2f(5.2, 1.3 + drift))\n );\n let r = vec2f(\n fbm(p * U.warpScale + 4.0 * q + vec2f(1.7, 9.2)),\n fbm(p * U.warpScale + 4.0 * q + vec2f(8.3, 2.8))\n );\n return p + U.warpAmount * r;\n}\n\nfn nucleiField(p: vec2f, t: f32) -> vec2f {\n // Returns (paletteId, valueMod) \u2014 softmax over the nuclei (isotropic; the WebGPU\n // twin keeps the parity-floor isotropic Gaussian).\n var accumId = 0.0;\n var accumVal = 0.0;\n var w = 0.0;\n let n = U.nucleiCount;\n for (var i = 0; i < 6; i = i + 1) {\n if (i >= n) { break; }\n let pos = U.nucleiPos[i].xy;\n let rad = max(U.nucleiPos[i].z, 0.01);\n let d2 = dot(p - pos, p - pos);\n let wi = exp((-U.softmaxBeta * d2) / (rad * rad));\n accumId = accumId + wi * U.nucleiPos[i].w;\n accumVal = accumVal + wi * U.nucleiMod[i].x;\n w = w + wi;\n }\n if (w < 1e-4) { return vec2f(0.5, 0.0); }\n return vec2f(accumId / w, accumVal / w);\n}\n\n// ACES (Narkowicz) \u2014 matches the GLSL tonemap.\nfn aces(x: vec3f) -> vec3f {\n let a = 2.51; let b = 0.03; let c = 2.43; let d = 0.59; let e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), vec3f(0.0), vec3f(1.0));\n}\n\n@fragment\nfn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n let t = U.time;\n let pw = domainWarp(uv, t);\n let nf = nucleiField(pw, t);\n var col = samplePalette(nf.x);\n col = col * (1.0 + U.valueVariance * nf.y);\n\n // Breath.\n let breath = sin(t * 6.2831 / max(U.breathPeriod, 1.0));\n col = col * (1.0 + U.breathDepth * breath * 0.5);\n\n // (The WebGPU smooth pole; the painterly mediums run the W7c multi-pass passes.)\n col = aces(col);\n col = linearToSrgb(col); // the mandatory OETF (linear \u2192 display).\n return vec4f(col * U.alpha, U.alpha); // premultiply AFTER the OETF (parity).\n}";
|
|
5
|
+
/** The assembled WGSL module string (vertex + fragment) for createGPUCanvas. */
|
|
6
|
+
export declare const AURORA_WGSL: string;
|
|
7
|
+
export { WGSL_MAX_NUCLEI, WGSL_MAX_STOPS };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AURORA_BRUSH_GLSL = "// \u2500\u2500 Curved swept brushstroke primitive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// A stroke is a curved spine from A to B with quadratic bulge K (sideways offset\n// at midpoint). Width varies along length via a shape profile. Edge is ragged\n// (bristle-modulated), ends are rounded blobs, inside has streak modulation.\n//\n// For pixel p, we invert an approximate projection onto the curved spine:\n// 1) project p onto straight AB axis -> along0, cross0\n// 2) at parameter along0, spine offset = 4\u00B7K\u00B7along0\u00B7(1-along0) perpendicular\n// 3) cross = cross0 - spineOffset, refine along one iteration\n// Coverage blends paint atop col based on per-stroke color sampled from the\n// underlying base at spine midpoint \u2014 so overlapping strokes from different\n// palette regions meet at hard, ragged demarcations.\n\nstruct StrokeHit {\n float coverage; // 0..1\n vec3 color;\n float alongT; // 0..1 along spine, for internal modulation\n float crossN; // cross distance / current half-width, signed\n float edgeN; // distance to edge in half-widths (0 = at edge, 1 = at spine)\n};\n\nStrokeHit noHit() { return StrokeHit(0.0, vec3(0.0), 0.0, 0.0, 0.0); }\n\nvec2 rotateDir(vec2 dir, float angle) {\n float ca = cos(angle), sa = sin(angle);\n return vec2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);\n}\n\nvec2 safeDir(vec2 dir) {\n float len = length(dir);\n return len > 1e-4 ? dir / len : vec2(1.0, 0.0);\n}\n\n// Shape profile along the stroke. type:\n// 0 tapered \u2014 thin-fat-thin (classic brush)\n// 1 load-drag \u2014 fat start, tapers to a point\n// 2 dab \u2014 ellipse-like, round center\n// 3 even \u2014 near-constant, slight end taper\nfloat strokeShape(float t, int type) {\n t = clamp(t, 0.0, 1.0);\n if (type == 1) {\n // loaded at t=0, tapering\n return pow(1.0 - t, 0.55) * smoothstep(0.0, 0.08, t);\n } else if (type == 2) {\n // dab / blob\n float d = t - 0.5;\n return exp(-d * d * 12.0);\n } else if (type == 3) {\n // mostly even, slight end softening\n return smoothstep(0.0, 0.08, t) * smoothstep(1.0, 0.92, t) * 0.95 + 0.05;\n }\n // 0 tapered\n return smoothstep(0.0, 0.22, t) * smoothstep(1.0, 0.78, t);\n}\n\n// A single curved stroke.\n// a, b \u2014 endpoints\n// halfW \u2014 base half-width (world units)\n// bulge \u2014 signed perpendicular midpoint offset, in world units\n// shapeType \u2014 0..3\n// bristleFreq \u2014 spatial frequency of edge raggedness\n// bristleAmp \u2014 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed \u2014 uniqueness seed for internal streaks\n// colAtMid \u2014 pre-sampled color at midpoint\nStrokeHit curvedStroke(vec2 p, vec2 a, vec2 b, float halfW,\n float bulge, int shapeType,\n float bristleFreq, float bristleAmp,\n float streakSeed, vec3 colAtMid) {\n vec2 ab = b - a;\n float L = length(ab);\n if (L < 1e-5) return noHit();\n vec2 tang = ab / L;\n vec2 norm = vec2(-tang.y, tang.x);\n\n vec2 rel = p - a;\n float along0 = dot(rel, tang) / L; // straight projection 0..1 along AB\n float cross0 = dot(rel, norm); // signed cross\n\n // Spine sideways offset due to bulge (parabolic)\n float bend = 4.0 * bulge * along0 * (1.0 - along0);\n float cross1 = cross0 - bend;\n\n // One refinement step: as the spine bends, the closest-point along shifts.\n // Spine tangent differs from straight tangent; first-order correction:\n float dBend = 4.0 * bulge * (1.0 - 2.0 * along0); // d(bend)/d(along)\n float along1 = along0 + (cross1 * dBend) / (L * (1.0 + dBend * dBend / (L * L)));\n\n // Bristle-ragged edge: half-width gets chewed by 1D noise along the spine\n float edgeNoise = fbm(vec2(along1 * bristleFreq, streakSeed * 7.3)) - 0.5;\n float edgeNoise2 = fbm(vec2(along1 * bristleFreq * 2.3, streakSeed * 3.1 + 17.0)) - 0.5;\n float edgeMod = 1.0 - bristleAmp * (0.6 + 0.4 * edgeNoise) * (0.5 + edgeNoise2);\n\n // Width profile along the stroke\n float shape = strokeShape(along1, shapeType);\n float halfWNow = halfW * shape * edgeMod;\n\n // Inside-segment coverage\n float cov = 0.0;\n if (along1 >= 0.0 && along1 <= 1.0 && halfWNow > 1e-6) {\n float cn = abs(cross1) / halfWNow;\n // fwidth-derived AA: half-width from the field's screen-space gradient so the\n // edge holds ~1px regardless of zoom/DPR (mirrors metaball.frag.ts:252-255).\n float aaC = max(fwidth(cn), 1e-6);\n cov = 1.0 - smoothstep(1.0 - aaC, 1.0 + aaC, cn);\n }\n\n // End-cap blobs (rounded tips, not perpendicular cuts)\n float capA = 0.0, capB = 0.0;\n {\n vec2 capCenterA = a + norm * (bulge * 0.0); // at a\n vec2 capCenterB = a + tang * L + norm * bend * 0.0 + norm * 0.0; // at b; bend is 0 at endpoints\n capCenterB = b;\n float endShapeA = strokeShape(0.02, shapeType);\n float endShapeB = strokeShape(0.98, shapeType);\n float rA = halfW * endShapeA * (0.9 + 0.2 * fbm(vec2(streakSeed * 13.0)));\n float rB = halfW * endShapeB * (0.9 + 0.2 * fbm(vec2(streakSeed * 19.0 + 5.0)));\n float dA = length(p - capCenterA);\n float dB = length(p - capCenterB);\n // fwidth-derived AA on the clean Euclidean cap distances (mirrors metaball.frag.ts:252-255).\n float aaA = max(fwidth(dA), 1e-6);\n float aaB = max(fwidth(dB), 1e-6);\n capA = 1.0 - smoothstep(rA - aaA, rA + aaA, dA);\n capB = 1.0 - smoothstep(rB - aaB, rB + aaB, dB);\n // Only apply caps if we're BEYOND the segment; otherwise the segment wins.\n capA *= (along1 < 0.05) ? 1.0 : 0.0;\n capB *= (along1 > 0.95) ? 1.0 : 0.0;\n }\n\n float coverage = max(cov, max(capA, capB));\n if (coverage < 0.002) return noHit();\n\n // Edge distance (for impasto later): 0 at edge, 1 at spine\n float edgeDist = halfWNow > 1e-6 ? clamp(1.0 - abs(cross1) / halfWNow, 0.0, 1.0) : 0.0;\n\n return StrokeHit(\n coverage,\n colAtMid,\n clamp(along1, 0.0, 1.0),\n halfWNow > 1e-6 ? cross1 / halfWNow : 0.0,\n edgeDist\n );\n}\n\n// Composite stroke over col with internal streaking + impasto edge highlight.\n// streakAmp \u2014 0..0.2 how much internal streaks darken/lighten\n// impastoAmp \u2014 0..1 edge catch-light strength\n// hardness \u2014 0..1 how crisp the compositing transition is (1 = razor, 0 = creamy)\nvoid paintOver(inout vec3 col, StrokeHit s, float streakFreq, float streakAmp,\n float impastoAmp, float hardness, float streakSeed) {\n if (s.coverage < 0.002) return;\n float strokeOpacity = clamp(uStrokeAmount, 0.0, 1.0);\n if (strokeOpacity <= 0.001) return;\n vec3 c = s.color;\n\n // Internal streaks \u2014 fbm along spine, modulated by cross position. Gives\n // the loaded-brush look: some bristles carry more pigment than others.\n float streakA = fbm(vec2(s.alongT * streakFreq, s.crossN * 2.7 + streakSeed));\n float streakB = fbm(vec2(s.alongT * streakFreq * 0.6 + streakSeed * 3.7, s.crossN * 4.1));\n float streak = 0.6 * (streakA - 0.5) + 0.4 * (streakB - 0.5);\n c *= 1.0 + streak * streakAmp * 2.0;\n\n // Subtle value variance across width (hollow-center catch-light)\n float crossShade = smoothstep(0.0, 0.4, s.edgeN) * (1.0 - smoothstep(0.65, 1.0, s.edgeN));\n c *= 1.0 + crossShade * 0.05;\n\n // Impasto edge highlight \u2014 bright rim on one side of the stroke\n float rim = smoothstep(0.85, 1.0, 1.0 - s.edgeN) * step(0.0, s.crossN);\n c += impastoAmp * rim * vec3(0.18, 0.15, 0.11);\n // Shadow on the other side (darker, cooler)\n float shadow = smoothstep(0.85, 1.0, 1.0 - s.edgeN) * step(0.0, -s.crossN);\n c -= impastoAmp * shadow * 0.25 * vec3(0.10, 0.09, 0.07);\n\n float softLimit = mix(0.35, 0.98, hardness);\n float alpha = smoothstep(0.0, 1.0 - softLimit, s.coverage) * strokeOpacity;\n col = mix(col, c, alpha);\n}\n\n// Best-of-9-neighbor placement: sample 3x3 surrounding cells and take the\n// stroke that covers this pixel most. Breaks the grid by per-cell jitter and\n// sparse density via noise thresholding.\nStrokeHit bestOil(vec2 p, float cellSize, float lenMul, float halfWMul,\n float jitterAmt, float density, int shapeType,\n float bristleAmp, vec2 flow, float t, float seed) {\n vec2 cell = floor(p / cellSize);\n\n StrokeHit best = noHit();\n // 3x3 neighborhood\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cc = cell + vec2(float(dx), float(dy));\n vec2 hh = hash22(cc + seed) - 0.5;\n // Density gate \u2014 noise threshold; sparse placement.\n float gate = hash21(cc * 1.7 + seed * 0.3);\n if (gate > density) continue;\n\n vec2 center = (cc + 0.5 + hh * jitterAmt) * cellSize;\n\n // Per-stroke direction: consume the layer-provided flow, then add only\n // deterministic local perturbation so alternate stroke layers stay live.\n vec2 f = safeDir(flow);\n float angJ = (hash21(cc + seed + 11.0) - 0.5) * 0.9; // +/- 0.45 rad\n float localCurl = (fbm(center * (2.6 + seed * 0.11) + seed * 1.9) - 0.5) * 0.55 * uFlowCurl;\n vec2 dir = rotateDir(f, angJ + localCurl);\n\n float lenV = cellSize * lenMul * (0.65 + 0.55 * hash21(cc + seed + 23.0));\n float halfW = cellSize * halfWMul * (0.70 + 0.55 * hash21(cc + seed + 41.0));\n float bulge = (hash21(cc + seed + 53.0) - 0.5) * lenV * 0.35;\n\n vec2 a = center - dir * (lenV * 0.5);\n vec2 b = center + dir * (lenV * 0.5);\n\n vec3 colMid = brokenColorJitter(\n sampleBase(center, t),\n hash21(cc + seed + 89.0),\n hash21(cc * 2.3 + seed + 97.0),\n 1.0\n );\n\n StrokeHit h = curvedStroke(p, a, b, halfW, bulge, shapeType,\n 7.0, bristleAmp,\n hash21(cc + seed + 67.0), colMid);\n if (h.coverage > best.coverage) best = h;\n }\n }\n return best;\n}\n";
|
|
1
|
+
export declare const AURORA_BRUSH_GLSL = "// \u2500\u2500 Curved swept brushstroke primitive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// A stroke is a curved spine from A to B with quadratic bulge K (sideways offset\n// at midpoint). Width varies along length via a shape profile. Edge is ragged\n// (bristle-modulated), ends are rounded blobs, inside has streak modulation.\n//\n// For pixel p, we invert an approximate projection onto the curved spine:\n// 1) project p onto straight AB axis -> along0, cross0\n// 2) at parameter along0, spine offset = 4\u00B7K\u00B7along0\u00B7(1-along0) perpendicular\n// 3) cross = cross0 - spineOffset, refine along one iteration\n// Coverage blends paint atop col based on per-stroke color sampled from the\n// underlying base at spine midpoint \u2014 so overlapping strokes from different\n// palette regions meet at hard, ragged demarcations.\n\nstruct StrokeHit {\n float coverage; // 0..1\n vec3 color;\n float alongT; // 0..1 along spine, for internal modulation\n float crossN; // cross distance / current half-width, signed\n float edgeN; // distance to edge in half-widths (0 = at edge, 1 = at spine)\n};\n\nStrokeHit noHit() { return StrokeHit(0.0, vec3(0.0), 0.0, 0.0, 0.0); }\n\nvec2 rotateDir(vec2 dir, float angle) {\n float ca = cos(angle), sa = sin(angle);\n return vec2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);\n}\n\nvec2 safeDir(vec2 dir) {\n float len = length(dir);\n return len > 1e-4 ? dir / len : vec2(1.0, 0.0);\n}\n\n// Shape profile along the stroke. type:\n// 0 tapered \u2014 thin-fat-thin (classic brush)\n// 1 load-drag \u2014 fat start, tapers to a point\n// 2 dab \u2014 ellipse-like, round center\n// 3 even \u2014 near-constant, slight end taper\nfloat strokeShape(float t, int type) {\n t = clamp(t, 0.0, 1.0);\n if (type == 1) {\n // loaded at t=0, tapering\n return pow(1.0 - t, 0.55) * smoothstep(0.0, 0.08, t);\n } else if (type == 2) {\n // dab / blob\n float d = t - 0.5;\n return exp(-d * d * 12.0);\n } else if (type == 3) {\n // mostly even, slight end softening\n return smoothstep(0.0, 0.08, t) * smoothstep(1.0, 0.92, t) * 0.95 + 0.05;\n }\n // 0 tapered\n return smoothstep(0.0, 0.22, t) * smoothstep(1.0, 0.78, t);\n}\n\n// A single curved stroke.\n// a, b \u2014 endpoints\n// halfW \u2014 base half-width (world units)\n// bulge \u2014 signed perpendicular midpoint offset, in world units\n// shapeType \u2014 0..3\n// bristleFreq \u2014 spatial frequency of edge raggedness\n// bristleAmp \u2014 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed \u2014 uniqueness seed for internal streaks\n// colAtMid \u2014 pre-sampled color at midpoint\nStrokeHit curvedStroke(vec2 p, vec2 a, vec2 b, float halfW,\n float bulge, int shapeType,\n float bristleFreq, float bristleAmp,\n float streakSeed, vec3 colAtMid) {\n vec2 ab = b - a;\n float L = length(ab);\n if (L < 1e-5) return noHit();\n vec2 tang = ab / L;\n vec2 norm = vec2(-tang.y, tang.x);\n\n vec2 rel = p - a;\n float along0 = dot(rel, tang) / L; // straight projection 0..1 along AB\n float cross0 = dot(rel, norm); // signed cross\n\n // Spine sideways offset due to bulge (parabolic)\n float bend = 4.0 * bulge * along0 * (1.0 - along0);\n float cross1 = cross0 - bend;\n\n // One refinement step: as the spine bends, the closest-point along shifts.\n // Spine tangent differs from straight tangent; first-order correction:\n float dBend = 4.0 * bulge * (1.0 - 2.0 * along0); // d(bend)/d(along)\n float along1 = along0 + (cross1 * dBend) / (L * (1.0 + dBend * dBend / (L * L)));\n\n // Bristle-ragged edge: half-width gets chewed by 1D noise along the spine\n float edgeNoise = fbm(vec2(along1 * bristleFreq, streakSeed * 7.3)) - 0.5;\n float edgeNoise2 = fbm(vec2(along1 * bristleFreq * 2.3, streakSeed * 3.1 + 17.0)) - 0.5;\n float edgeMod = 1.0 - bristleAmp * (0.6 + 0.4 * edgeNoise) * (0.5 + edgeNoise2);\n\n // Width profile along the stroke\n float shape = strokeShape(along1, shapeType);\n float halfWNow = halfW * shape * edgeMod;\n\n // Inside-segment coverage\n float cov = 0.0;\n if (along1 >= 0.0 && along1 <= 1.0 && halfWNow > 1e-6) {\n float cn = abs(cross1) / halfWNow;\n // fwidth-derived AA: half-width from the field's screen-space gradient so the\n // edge holds ~1px regardless of zoom/DPR (mirrors metaball.frag.ts:252-255).\n float aaC = max(fwidth(cn), 1e-6);\n cov = 1.0 - smoothstep(1.0 - aaC, 1.0 + aaC, cn);\n }\n\n // End-cap blobs (rounded tips, not perpendicular cuts)\n float capA = 0.0, capB = 0.0;\n {\n vec2 capCenterA = a + norm * (bulge * 0.0); // at a\n vec2 capCenterB = a + tang * L + norm * bend * 0.0 + norm * 0.0; // at b; bend is 0 at endpoints\n capCenterB = b;\n float endShapeA = strokeShape(0.02, shapeType);\n float endShapeB = strokeShape(0.98, shapeType);\n float rA = halfW * endShapeA * (0.9 + 0.2 * fbm(vec2(streakSeed * 13.0)));\n float rB = halfW * endShapeB * (0.9 + 0.2 * fbm(vec2(streakSeed * 19.0 + 5.0)));\n float dA = length(p - capCenterA);\n float dB = length(p - capCenterB);\n // fwidth-derived AA on the clean Euclidean cap distances (mirrors metaball.frag.ts:252-255).\n float aaA = max(fwidth(dA), 1e-6);\n float aaB = max(fwidth(dB), 1e-6);\n capA = 1.0 - smoothstep(rA - aaA, rA + aaA, dA);\n capB = 1.0 - smoothstep(rB - aaB, rB + aaB, dB);\n // Only apply caps if we're BEYOND the segment; otherwise the segment wins.\n capA *= (along1 < 0.05) ? 1.0 : 0.0;\n capB *= (along1 > 0.95) ? 1.0 : 0.0;\n }\n\n float coverage = max(cov, max(capA, capB));\n if (coverage < 0.002) return noHit();\n\n // Edge distance (for impasto later): 0 at edge, 1 at spine\n float edgeDist = halfWNow > 1e-6 ? clamp(1.0 - abs(cross1) / halfWNow, 0.0, 1.0) : 0.0;\n\n return StrokeHit(\n coverage,\n colAtMid,\n clamp(along1, 0.0, 1.0),\n halfWNow > 1e-6 ? cross1 / halfWNow : 0.0,\n edgeDist\n );\n}\n\n// Composite stroke over col with internal streaking + ACCUMULATE paint HEIGHT.\n// streakAmp \u2014 0..0.2 how much internal streaks darken/lighten\n// impastoAmp \u2014 0..1 paint-thickness contribution (the height the relight reads)\n// hardness \u2014 0..1 how crisp the compositing transition is (1 = razor, 0 = creamy)\n//\n// AW.W4.2 \u2014 the fixed-RGB edge rim (the phantom upper-left light) is RETIRED. Each\n// stroke now deposits PAINT HEIGHT into the height accumulator (coverage x per-stroke\n// thickness, perturbed by the bristle/streak fbm for ridges/grooves); mediumOil\n// derives a normal from the accumulated height gradient (dFdx/dFdy) and relights it\n// once with the movable uLightDir source (relightImpasto), in linear before aces().\nvoid paintOver(inout vec3 col, inout float height, StrokeHit s,\n float streakFreq, float streakAmp,\n float impastoAmp, float hardness, float streakSeed) {\n if (s.coverage < 0.002) return;\n float strokeOpacity = clamp(uStrokeAmount, 0.0, 1.0);\n if (strokeOpacity <= 0.001) return;\n vec3 c = s.color;\n\n // Internal streaks \u2014 fbm along spine, modulated by cross position. Gives\n // the loaded-brush look: some bristles carry more pigment than others.\n float streakA = fbm(vec2(s.alongT * streakFreq, s.crossN * 2.7 + streakSeed));\n float streakB = fbm(vec2(s.alongT * streakFreq * 0.6 + streakSeed * 3.7, s.crossN * 4.1));\n float streak = 0.6 * (streakA - 0.5) + 0.4 * (streakB - 0.5);\n c *= 1.0 + streak * streakAmp * 2.0;\n\n // Subtle value variance across width (hollow-center catch-light)\n float crossShade = smoothstep(0.0, 0.4, s.edgeN) * (1.0 - smoothstep(0.65, 1.0, s.edgeN));\n c *= 1.0 + crossShade * 0.05;\n\n float softLimit = mix(0.35, 0.98, hardness);\n float alpha = smoothstep(0.0, 1.0 - softLimit, s.coverage) * strokeOpacity;\n col = mix(col, c, alpha);\n\n // \u2500\u2500 Accumulate paint HEIGHT (AW.W4.2). Thickness peaks at the stroke spine and\n // falls to zero at the edge (edgeN: 0=edge, 1=spine), perturbed by the bristle\n // streak so ridges/grooves form. The relight reads the gradient of this field.\n float ridge = 0.7 + 0.3 * (streak + 0.5);\n height += s.coverage * impastoAmp * (0.4 + 0.6 * s.edgeN) * ridge * alpha;\n}\n\n// AW.W4.2 \u2014 relight the accumulated paint height. Derive a normal from the height\n// gradient (dFdx/dFdy \u2014 derivatives are in-pattern; fwidth is already used for AA),\n// then apply diffuse + Blinn specular from the movable uLightDir/uLightColor source.\n// LINEAR-space lighting BEFORE aces() (the tonemap/OETF stay locked); thin strokes\n// inherit a low shininess, thick impasto raises it (high-height ridges catch a sharp\n// glint). canvasBase is the tooth height so the canvas weave also catches the light.\nvec3 relightImpasto(vec3 col, float height, float canvasBase) {\n float h = height + canvasBase;\n // Height-gradient normal. heightScale trades relief depth for surface flatness.\n float heightScale = 6.0;\n vec3 N = normalize(vec3(-dFdx(h) * 40.0, -dFdy(h) * 40.0, 1.0 / heightScale));\n vec3 L = normalize(uLightDir);\n vec3 V = vec3(0.0, 0.0, 1.0); // view straight on\n vec3 H = normalize(L + V);\n float diff = max(dot(N, L), 0.0);\n // Thick impasto \u2192 high shininess (sharp glint); thin paint \u2192 broad soft sheen.\n float shininess = mix(8.0, 64.0, clamp(height * 2.0, 0.0, 1.0));\n float spec = pow(max(dot(N, H), 0.0), shininess);\n // Modulate the lit terms by how much paint sits here (no relief on bare ground).\n float relief = clamp(h * 1.5, 0.0, 1.0);\n vec3 lit = col * (1.0 + uImpasto * relief * (diff - 0.5) * 0.5 * uLightColor);\n lit += uImpasto * relief * spec * 0.6 * uLightColor;\n return lit;\n}\n\n// Best-of-9-neighbor placement: sample 3x3 surrounding cells and take the\n// stroke that covers this pixel most. Breaks the grid by per-cell jitter and\n// sparse density via noise thresholding.\nStrokeHit bestOil(vec2 p, float cellSize, float lenMul, float halfWMul,\n float jitterAmt, float density, int shapeType,\n float bristleAmp, vec2 flow, float t, float seed) {\n vec2 cell = floor(p / cellSize);\n\n StrokeHit best = noHit();\n // 3x3 neighborhood\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cc = cell + vec2(float(dx), float(dy));\n vec2 hh = hash22(cc + seed) - 0.5;\n // Density gate \u2014 noise threshold; sparse placement.\n float gate = hash21(cc * 1.7 + seed * 0.3);\n if (gate > density) continue;\n\n vec2 center = (cc + 0.5 + hh * jitterAmt) * cellSize;\n\n // Per-stroke direction source \u2014 the AW.W4.1 strokeOrient switch:\n // uStrokeOrient==0 (flow) \u2014 the layer-provided hand-authored flowField.\n // uStrokeOrient==1 (tensor) \u2014 the structure-tensor minor eigenvector at the\n // cell center (the color field's edge-tangent),\n // so strokes hug the color zones (van Gogh).\n // coh carries the per-cell coherence A for the W4.3 energy grading (long\n // confident strokes where structure is coherent, stubby dabs in flat zones).\n vec2 f;\n float coh = 1.0;\n if (uStrokeOrient == 1) {\n vec3 tf = structureTensorField(center, t, safeDir(flow));\n f = safeDir(tf.xy);\n coh = tf.z;\n } else {\n f = safeDir(flow);\n }\n // Per-stroke deterministic local perturbation so alternate stroke layers stay\n // live. Tensor strokes get LESS angular jitter (the field already orients them).\n float jScale = (uStrokeOrient == 1) ? 0.4 : 1.0;\n float angJ = (hash21(cc + seed + 11.0) - 0.5) * 0.9 * jScale; // +/- 0.45/0.18 rad\n float localCurl = (fbm(center * (2.6 + seed * 0.11) + seed * 1.9) - 0.5) * 0.55 * uFlowCurl;\n vec2 dir = rotateDir(f, angJ + localCurl);\n\n // AW.W4.3 \u2014 energy-graded length/density for the van-Gogh cascade: in vangogh\n // mode (uMedium==5) modulate the stroke length by local luminance (bright \u2192\n // long confident strokes, dark \u2192 short dabs, the Starry-Night Kolmogorov/\n // Batchelor congruence) AND by coherence (coherent zones \u2192 long, flat \u2192 stubby).\n float energy = 1.0;\n if (uMedium == 5) {\n float luma = clamp(dot(sampleBase(center, t), W_LUMA), 0.0, 1.0);\n // long in the lights + coherent zones; short dabs in the darks + flat zones.\n energy = mix(0.5, 1.5, luma) * mix(0.6, 1.0, coh);\n }\n\n float lenV = cellSize * lenMul * (0.65 + 0.55 * hash21(cc + seed + 23.0)) * energy;\n float halfW = cellSize * halfWMul * (0.70 + 0.55 * hash21(cc + seed + 41.0));\n float bulge = (hash21(cc + seed + 53.0) - 0.5) * lenV * 0.35;\n\n vec2 a = center - dir * (lenV * 0.5);\n vec2 b = center + dir * (lenV * 0.5);\n\n vec3 colMid = brokenColorJitter(\n sampleBase(center, t),\n hash21(cc + seed + 89.0),\n hash21(cc * 2.3 + seed + 97.0),\n 1.0\n );\n\n StrokeHit h = curvedStroke(p, a, b, halfW, bulge, shapeType,\n 7.0, bristleAmp,\n hash21(cc + seed + 67.0), colMid);\n if (h.coverage > best.coverage) best = h;\n }\n }\n return best;\n}\n";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AURORA_COMPOSITION_GLSL = "// \u2500\u2500 Palette LUT \
|
|
1
|
+
export declare const AURORA_COMPOSITION_GLSL = "// \u2500\u2500 Palette LUT (W5 \u2014 OKLCh/OKLab interpolation) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// The endpoints stay CPU-baked to LINEAR sRGB (the Aras precompute pattern); only\n// the BETWEEN-endpoint interpolation moves to perceptual space. OKLab-rectangular\n// for the default ramp (holds chroma, no grey midpoint); the OKLCh hue-arc only on\n// uHuePath increasing/decreasing (deliberate rainbow travel). The prior linear\n// mix() (the muddy-midtone source) is GONE.\nvec3 samplePalette(float id) {\n id = clamp(id, 0.0, 1.0);\n float scaled = id * float(uStopCount - 1);\n int i0 = int(floor(scaled));\n int i1 = min(i0 + 1, uStopCount - 1);\n float t = fract(scaled);\n t = smoothstep(0.0, 1.0, t);\n vec3 a = uPalette[i0];\n vec3 b = uPalette[i1];\n // increasing(2)/decreasing(3) request the OKLCh hue-arc (rainbow sweep); every\n // other method takes the OKLab-rectangular straight line (no hue detour).\n if (uHuePath == 2 || uHuePath == 3) {\n return mixPaletteOklchArc(a, b, t, uHuePath);\n }\n return mixPaletteOklab(a, b, t);\n}\n\n// \u2500\u2500 Nuclei field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nvoid nucleiField(vec2 p, float t, out float paletteId, out float valueMod) {\n float accumBias = 0.0;\n float accumValue = 0.0;\n float accumW = 0.0;\n for (int i = 0; i < MAX_NUCLEI; i++) {\n if (i >= uNucleiCount) break;\n vec2 posI = uNucleiPos[i]\n + uNucleiDriftRadius[i] * vec2(\n cos(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i]),\n sin(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i] * 1.13)\n );\n vec2 diff = p - posI;\n // Anisotropic Gaussian: rotate diff into the nucleus's local frame\n // (major axis along uNucleiAngle), then scale the major-axis component by\n // 1/elongation so the squared distance describes an ellipse. Defaults\n // 1.0/0.0 reduce to the isotropic dot(diff, diff).\n float ca = cos(uNucleiAngle[i]);\n float sa = sin(uNucleiAngle[i]);\n vec2 local = vec2( ca * diff.x + sa * diff.y,\n -sa * diff.x + ca * diff.y);\n float along = local.x / max(uNucleiElong[i], 0.01);\n float across = local.y;\n float d2 = along * along + across * across;\n float r = max(uNucleiRadius[i], 0.01);\n float w = exp(-uSoftmaxBeta * d2 / (r * r));\n accumBias += w * uNucleiPaletteBias[i];\n accumValue += w * uNucleiValueBias[i];\n accumW += w;\n }\n paletteId = accumBias / max(accumW, 1e-4);\n valueMod = accumValue / max(accumW, 1e-4);\n\n // Palette drift \u2014 slow global paletteId breathe between adjacent stops. The\n // rate rides K_PAL (perceptible ~30\u201360s hue cycle at the default coefficient);\n // the amplitude lifts the authored coefficient into a 0.03..0.06 paletteId band\n // so the palette visibly travels rather than dithering within one stop.\n float palAmp = clamp(uPaletteDrift * 6.0, 0.0, 0.06);\n paletteId += palAmp * sin(t * uPaletteDrift * K_PAL);\n paletteId = clamp(paletteId, 0.0, 1.0);\n}\n";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AURORA_FLOW_GLSL = "// \u2500\u2500 Flow field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nvec2 flowField(vec2 p, float t) {\n vec2 dir = vec2(1.0, 0.0);\n if (uFlowPattern == 1) {\n // radial\n dir = normalize(p - uFlowFocal + 1e-4);\n } else if (uFlowPattern == 2) {\n // swirl \u2014 tangent to radial\n vec2 rad = normalize(p - uFlowFocal + 1e-4);\n dir = vec2(-rad.y, rad.x);\n } else if (uFlowPattern == 3) {\n // diagonal\n float a = radians(uFlowAngle);\n dir = vec2(cos(a), sin(a));\n } else if (uFlowPattern == 4) {\n // multi \u2014 curl-noise driven\n float n = fbm(p * 2.0 + t * 0.02);\n float a = n * 6.2831;\n dir = vec2(cos(a), sin(a));\n }\n // curl \u2014 perturb by noise. Radial gets much less curl so rays stay clean.\n if (uFlowCurl > 0.0) {\n float n = fbm(p * 3.0) - 0.5;\n float curlAmt = uFlowCurl;\n if (uFlowPattern == 1) curlAmt *= 0.25; // radial: preserve ray clarity\n if (uFlowPattern == 2) curlAmt *= 0.55; // swirl: allow moderate curl\n float a = atan(dir.y, dir.x) + n * 3.14159 * curlAmt;\n dir = vec2(cos(a), sin(a));\n }\n // cursor influence \u2014 swirl around the cursor position\n if (uCursorStrength > 0.001) {\n vec2 toCur = uCursor - p;\n float d = length(toCur);\n float r = max(uCursorRadius, 0.01);\n float w = exp(-(d * d) / (r * r * 0.5));\n // swirl tangent\n vec2 tangent = vec2(-toCur.y, toCur.x) / max(d, 1e-4);\n float a0 = atan(dir.y, dir.x);\n float a1 = atan(tangent.y, tangent.x);\n // blend angle\n float da = a1 - a0;\n da = atan(sin(da), cos(da)); // wrap to [-pi, pi]\n float a = a0 + da * w * uCursorStrength;\n dir = vec2(cos(a), sin(a));\n }\n return dir;\n}\n";
|
|
1
|
+
export declare const AURORA_FLOW_GLSL = "// \u2500\u2500 Flow field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Structure-tensor prototype (body defined post-sampleBase in the mediums block).\n// Returns vec3(dir.x, dir.y, coherence) \u2014 the minor-eigenvector tangent + A.\nvec3 structureTensorField(vec2 p, float t, vec2 fallbackDir);\n\nvec2 flowField(vec2 p, float t) {\n vec2 dir = vec2(1.0, 0.0);\n if (uFlowPattern == 1) {\n // radial\n dir = normalize(p - uFlowFocal + 1e-4);\n } else if (uFlowPattern == 2) {\n // swirl \u2014 tangent to radial\n vec2 rad = normalize(p - uFlowFocal + 1e-4);\n dir = vec2(-rad.y, rad.x);\n } else if (uFlowPattern == 3) {\n // diagonal\n float a = radians(uFlowAngle);\n dir = vec2(cos(a), sin(a));\n } else if (uFlowPattern == 4) {\n // multi \u2014 curl-noise driven\n float n = fbm(p * 2.0 + t * 0.02);\n float a = n * 6.2831;\n dir = vec2(cos(a), sin(a));\n } else if (uFlowPattern == 5) {\n // tensor / ETF \u2014 the color field's OWN edge-tangent flow (AW.W4.1). The\n // diagonal angle seeds the smooth fallback the coherence-weighted blend\n // relaxes toward in low-structure zones. Returns directly (no curl/cursor\n // post-mix \u2014 the tensor field already carries the structure orientation;\n // the cursor warp still drives the underlying color field in domainWarp).\n float a = radians(uFlowAngle);\n vec2 fallback = vec2(cos(a), sin(a));\n return structureTensorField(p, t, fallback).xy;\n }\n // curl \u2014 perturb by noise. Radial gets much less curl so rays stay clean.\n if (uFlowCurl > 0.0) {\n float n = fbm(p * 3.0) - 0.5;\n float curlAmt = uFlowCurl;\n if (uFlowPattern == 1) curlAmt *= 0.25; // radial: preserve ray clarity\n if (uFlowPattern == 2) curlAmt *= 0.55; // swirl: allow moderate curl\n float a = atan(dir.y, dir.x) + n * 3.14159 * curlAmt;\n dir = vec2(cos(a), sin(a));\n }\n // cursor influence \u2014 swirl around the cursor position\n if (uCursorStrength > 0.001) {\n vec2 toCur = uCursor - p;\n float d = length(toCur);\n float r = max(uCursorRadius, 0.01);\n float w = exp(-(d * d) / (r * r * 0.5));\n // swirl tangent\n vec2 tangent = vec2(-toCur.y, toCur.x) / max(d, 1e-4);\n float a0 = atan(dir.y, dir.x);\n float a1 = atan(tangent.y, tangent.x);\n // blend angle\n float da = a1 - a0;\n da = atan(sin(da), cos(da)); // wrap to [-pi, pi]\n float a = a0 + da * w * uCursorStrength;\n dir = vec2(cos(a), sin(a));\n }\n // AW.W8.1 \u2014 velocity-reactive swirl-BURST: a fast flick (uCursorBurst, decaying\n // over ~1s) injects a transient flow bias along the pointer velocity, distinct from\n // the steady attraction above (the stateless swirl is SUPERSEDED by this form). The\n // burst is gated by the master tempo scalar on the CPU side (it decays to 0 under\n // PRM/pause), so this term cannot leak motion under reduced-motion.\n if (uCursorBurst > 0.001) {\n vec2 toCur = uCursor - p;\n float d = length(toCur);\n float r = max(uCursorRadius, 0.01);\n float w = exp(-(d * d) / (r * r * 0.7));\n // Bias the flow toward the velocity direction, scaled by the decaying burst.\n vec2 velDir = uCursorVelocity / max(length(uCursorVelocity), 1e-4);\n float a0 = atan(dir.y, dir.x);\n float a1 = atan(velDir.y, velDir.x);\n float da = atan(sin(a1 - a0), cos(a1 - a0));\n float a = a0 + da * w * uCursorBurst * 0.8;\n dir = vec2(cos(a), sin(a));\n }\n return dir;\n}\n";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const AURORA_MEDIUMS_PRE_BRUSH_GLSL = "// \u2500\u2500 Medium overlays \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// A quick re-computation of base color for edge-mask sampling\nvec3 sampleBase(vec2 p, float t) {\n vec2 pw = domainWarp(p, t);\n float id; float vm;\n nucleiField(pw, t, id, vm);\n vec3 c = samplePalette(id);\n c *= 1.0 + uValueVariance * vm;\n return c;\n}\n\nvec3 mediumWatercolor(vec3 col, vec2 p, float t) {\n // Wet-edge cauliflowers via luma-gradient magnitude\n float eps = 0.004;\n vec3 cx1 = sampleBase(p + vec2(eps, 0.0), t);\n vec3 cx2 = sampleBase(p - vec2(eps, 0.0), t);\n vec3 cy1 = sampleBase(p + vec2(0.0, eps), t);\n vec3 cy2 = sampleBase(p - vec2(0.0, eps), t);\n float gx = dot(cx1 - cx2, W_LUMA);\n float gy = dot(cy1 - cy2, W_LUMA);\n float edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n float mask = smoothstep(0.0, 2.5, edge);\n col *= mix(1.0, 0.78, mask * uWetEdge);\n\n // Granulation \u2014 pigment settles in paper tooth\n float paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n float pigLoad = 1.0 - dot(col, W_LUMA);\n col *= 1.0 - uGranulation * pigLoad * (paper - 0.5);\n\n // Wash banding \u2014 faint horizontal wet gradient\n float band = fbm(vec2(p.x * 1.5, p.y * 0.4));\n col *= 1.0 + 0.04 * (band - 0.5);\n return col;\n}\n\nvec3 mediumPastel(vec3 col, vec2 p, float t) {\n vec2 flow = flowField(p, t);\n vec2 perp = vec2(-flow.y, flow.x);\n float along = dot(p, flow) * uStrokeScale;\n float across = dot(p, perp) * uStrokeScale;\n across += 0.03 * (vnoise(p * 260.0) - 0.5);\n\n float aniso = mix(1.0, 0.18, uStrokeAnisotropy);\n float stroke = fbm(vec2(along * aniso, across));\n col *= mix(1.0, 0.82 + 0.32 * stroke, uStrokeAmount);\n\n // Pastel tooth \u2014 tiny high-frequency grain\n float tooth = vnoise(p * 800.0);\n col *= 1.0 - 0.08 * uStrokeAmount * (tooth - 0.5);\n return col;\n}\n";
|
|
2
|
-
export declare const AURORA_MEDIUMS_POST_BRUSH_GLSL = "// \u2500\u2500 Crayon / oil-pastel \u2014 paper tooth \u00D7 wax pigment (PEER medium) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Crayon is not strokes. It's pigment crumbs dragged across paper tooth.\n// Model: heavy 2D tooth noise at multiple scales, anisotropically stretched\n// along flow direction, multiplied into the base color. Add a slow \"waxy\n// film\" that slightly unifies hues, and occasional darker \"pressed\" spots\n// where the crayon dug in. NO straight segments. Dispatched at main() level as\n// a peer of pastel/watercolor/oil \u2014 never a branch inside mediumOil().\nvec3 mediumCrayon(vec3 col, vec2 p, float t) {\n vec2 flow = flowField(p, t);\n float ang = atan(flow.y, flow.x);\n float ca = cos(-ang), sa = sin(-ang);\n vec2 pr = vec2(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n // Anisotropic tooth \u2014 squished along flow, coarse cross flow.\n float aniso = mix(0.45, 0.95, uStrokeAnisotropy);\n float scale = max(uStrokeScale * 1.6, 180.0);\n\n float t1 = vnoise(vec2(pr.x * scale * aniso, pr.y * scale));\n float t2 = vnoise(vec2(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + 11.0);\n float t3 = vnoise(vec2(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + 23.0);\n float tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n // Center at 0, amplify\n tooth = (tooth - 0.5) * 1.4;\n\n // Multiplicative darkening where tooth is low (pigment skipped paper valleys)\n float lay = 1.0 + tooth * 0.32 * uStrokeAmount;\n vec3 result = col * lay;\n\n // Occasional pressed-in crumbs \u2014 rare darker crumbs\n float crumbs = smoothstep(0.78, 0.95, vnoise(pr * scale * 3.0));\n result *= 1.0 - crumbs * 0.18 * uStrokeAmount;\n\n // Waxy highlight film \u2014 slight lightening on tooth peaks\n float waxy = smoothstep(0.55, 0.85, t1);\n result += waxy * 0.04 * vec3(1.0);\n\n // Paper tooth overlay (subtler than oil's canvas)\n float paperTooth = vnoise(p * 340.0) - 0.5;\n result *= 1.0 + paperTooth * 0.14 * uCanvasGrain;\n\n // Broken-color pigment: stable wax/pigment patches, not temporal flicker.\n vec2 pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n float pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + 19.0));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + 17.0),\n hash21(pigmentCell * 2.1 + 31.0),\n 0.45 + 0.55 * pigmentMask\n );\n\n // Crayon is saturation-amplified\n result = saturate3(result, 1.12);\n\n return result;\n}\n\nvec3 mediumOil(vec3 col, vec2 p, float t) {\n // Mode knobs (uStrokeMode) \u2014 oil-stroke modes ONLY (crayon is a peer medium,\n // uMedium==4, dispatched at main()):\n // 0 oil \u2014 balanced modern-abstract/palette-knife hybrid\n // 1 knife \u2014 palette-knife impasto: razor edges, heavy bristle/shadow\n // 3 brushwork \u2014 thick bristle brush\n int mode = uStrokeMode;\n\n // Per-mode parameters\n int shapeType = 0; // tapered\n float bristleAmp = 0.25; // 0..0.5\n float streakFreq = 9.0;\n float streakAmp = 0.09;\n float impastoAmp = 0.9;\n float hardness = 0.80; // edge compositing\n float toothScale = 240.0;\n float toothAmp = 0.09;\n float pigmentSat = 1.03;\n float densityBig = 0.65;\n float densityMed = 0.78;\n float densitySml = 0.90;\n\n if (mode == 1) { // palette knife\n shapeType = 3; // flat, even\n bristleAmp = 0.12;\n streakFreq = 4.0; streakAmp = 0.05;\n impastoAmp = 1.6;\n hardness = 0.95;\n toothAmp = 0.04;\n densityBig = 0.80; densityMed = 0.88; densitySml = 0.70;\n } else if (mode == 3) { // thick brushwork\n shapeType = 0; // tapered\n bristleAmp = 0.32;\n streakFreq = 14.0; streakAmp = 0.14;\n impastoAmp = 1.2;\n hardness = 0.85;\n toothAmp = 0.07;\n }\n\n // Scales & multipliers from uniforms\n float baseScale = max(uStrokeScale * 0.006, 0.008);\n // Three layers: big gestural, medium body, small dabs\n float sBig = baseScale * 2.4;\n float sMed = baseScale * 1.1;\n float sSml = baseScale * 0.45;\n\n float lenMulBig = mix(2.2, 3.8, uStrokeAnisotropy);\n float widMulBig = mix(0.55, 0.32, uStrokeAnisotropy);\n float lenMulMed = mix(2.0, 3.4, uStrokeAnisotropy);\n float widMulMed = mix(0.50, 0.30, uStrokeAnisotropy);\n float lenMulSml = mix(1.6, 2.6, uStrokeAnisotropy);\n float widMulSml = mix(0.45, 0.32, uStrokeAnisotropy);\n\n float jitterAmt = 0.75; // large jitter \u2014 no grid\n vec2 flow = flowField(p, t);\n\n vec3 result = col;\n\n // Layer 1 \u2014 big gestural strokes (sparse, shaping)\n StrokeHit hBig = bestOil(p, sBig, lenMulBig, widMulBig, jitterAmt * 0.55,\n densityBig, shapeType, bristleAmp, flow, t, 1.3);\n paintOver(result, hBig, streakFreq * 0.7, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 1.3);\n\n // Layer 2 \u2014 medium body strokes\n StrokeHit hMed = bestOil(p + vec2(11.3, 3.7), sMed, lenMulMed, widMulMed,\n jitterAmt, densityMed, shapeType, bristleAmp, flow, t, 2.7);\n paintOver(result, hMed, streakFreq, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 2.7);\n\n // Layer 3 \u2014 small dabs (more frequent, smaller)\n int smlShape = (mode == 1) ? 2 : shapeType; // knife uses dabs for sparkle\n StrokeHit hSml = bestOil(p + vec2(-5.1, 8.4), sSml, lenMulSml, widMulSml,\n jitterAmt * 1.3, densitySml, smlShape,\n bristleAmp * 0.85, flow, t, 4.1);\n paintOver(result, hSml, streakFreq * 1.4, streakAmp * 0.8,\n uImpasto * impastoAmp * 0.65 * uStrokeAmount, hardness, 4.1);\n\n // Layer 4 \u2014 fill dabs (very dense, very small) \u2014 covers bald spots\n float sFill = baseScale * 0.22;\n float lenMulFill = mix(1.4, 2.0, uStrokeAnisotropy);\n float widMulFill = mix(0.50, 0.38, uStrokeAnisotropy);\n int fillShape = (mode == 1) ? 3 : 2; // knife=even, others=dab (round fills)\n StrokeHit hFill = bestOil(p + vec2(3.9, -6.2), sFill, lenMulFill, widMulFill,\n jitterAmt * 1.5, 0.95, fillShape,\n bristleAmp * 0.6, flow, t, 8.9);\n paintOver(result, hFill, streakFreq * 1.8, streakAmp * 0.6,\n uImpasto * impastoAmp * 0.4 * uStrokeAmount, hardness * 0.9, 8.9);\n\n // Optional crosshatch layer\n if (uStrokeLayers == 2) {\n vec2 flow2 = vec2(-flow.y, flow.x);\n StrokeHit hX = bestOil(p + vec2(7.3, -2.1), sMed, lenMulMed * 0.9, widMulMed,\n jitterAmt, densityMed * 0.7, shapeType, bristleAmp, flow2, t, 6.5);\n paintOver(result, hX, streakFreq, streakAmp * 0.85,\n uImpasto * impastoAmp * 0.55 * uStrokeAmount, hardness, 6.5);\n }\n\n // Canvas tooth \u2014 linen weave\n float tooth1 = vnoise(p * toothScale);\n float tooth2 = vnoise(p * toothScale * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * toothAmp * uCanvasGrain;\n\n // Pigment saturation boost\n result = saturate3(result, pigmentSat);\n\n return result;\n}";
|
|
1
|
+
export declare const AURORA_MEDIUMS_PRE_BRUSH_GLSL = "// \u2500\u2500 Medium overlays \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// A quick re-computation of base color for edge-mask sampling\nvec3 sampleBase(vec2 p, float t) {\n vec2 pw = domainWarp(p, t);\n float id; float vm;\n nucleiField(pw, t, id, vm);\n vec3 c = samplePalette(id);\n c *= 1.0 + uValueVariance * vm;\n return c;\n}\n\n// \u2500\u2500 Structure-tensor / edge-tangent-flow (AW.W4.1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// The keystone of the painterly engine: derive stroke orientation from the COLOR\n// FIELD's OWN structure, not a hand-authored flow pattern. A 3x3 Sobel over\n// luma(sampleBase) yields the gradient (Gx,Gy); the 2x2 structure tensor\n// J = [[Gx\u00B2, GxGy],[GxGy, Gy\u00B2]] eigen-decomposes closed-form into the MINOR\n// eigenvector (the edge-TANGENT \u2014 least color change, the stroke direction; the\n// MAJOR eigenvector is the gradient/edge-NORMAL and would make strokes cross the\n// bands) and the coherence A = (\u03BB1-\u03BB2)/(\u03BB1+\u03BB2). Low-coherence (flat) zones relax\n// toward the smooth fallbackDir by (1-A) so tensor noise never reads as jitter.\n// Single-pass small-tap WebGL2 form; the Gaussian-smoothed multi-tap + LIC smear\n// is the AW.W7 WebGPU multi-pass fold. Returns vec3(tangent.x, tangent.y, A).\nvec3 structureTensorField(vec2 p, float t, vec2 fallbackDir) {\n float e = 0.0035; // small-tap neighborhood (edge-mask scale)\n // 3x3 luma samples.\n float l00 = dot(sampleBase(p + vec2(-e, -e), t), W_LUMA);\n float l10 = dot(sampleBase(p + vec2( 0.0, -e), t), W_LUMA);\n float l20 = dot(sampleBase(p + vec2( e, -e), t), W_LUMA);\n float l01 = dot(sampleBase(p + vec2(-e, 0.0), t), W_LUMA);\n float l21 = dot(sampleBase(p + vec2( e, 0.0), t), W_LUMA);\n float l02 = dot(sampleBase(p + vec2(-e, e), t), W_LUMA);\n float l12 = dot(sampleBase(p + vec2( 0.0, e), t), W_LUMA);\n float l22 = dot(sampleBase(p + vec2( e, e), t), W_LUMA);\n // Sobel derivatives.\n float Gx = (l20 + 2.0 * l21 + l22) - (l00 + 2.0 * l01 + l02);\n float Gy = (l02 + 2.0 * l12 + l22) - (l00 + 2.0 * l10 + l20);\n // Structure tensor components.\n float Jxx = Gx * Gx;\n float Jyy = Gy * Gy;\n float Jxy = Gx * Gy;\n // Closed-form 2x2 eigenvalues: \u03BB = 0.5(tr \u00B1 sqrt((Jxx-Jyy)\u00B2 + 4Jxy\u00B2)).\n float tr = Jxx + Jyy;\n float disc = sqrt(max((Jxx - Jyy) * (Jxx - Jyy) + 4.0 * Jxy * Jxy, 0.0));\n float lambdaMaj = 0.5 * (tr + disc);\n float lambdaMin = 0.5 * (tr - disc);\n // The MAJOR eigenvector points along the gradient (the edge NORMAL); the MINOR\n // is its perpendicular \u2014 the edge TANGENT (the stroke direction). The principal\n // angle is \u03B8 = 0.5\u00B7atan2(2\u00B7Jxy, Jxx-Jyy); this form is robust at Jxy\u22480 (where the\n // (\u03BB-Jyy, Jxy) eigenvector formula collapses to a zero vector). The tangent is at\n // \u03B8 + 90\u00B0. Guard the fully-isotropic (no-gradient) degenerate case \u2192 fallback dir.\n vec2 tangent;\n if (disc < 1e-7) {\n tangent = fallbackDir;\n } else {\n float theta = 0.5 * atan(2.0 * Jxy, Jxx - Jyy); // major (gradient) angle\n tangent = vec2(-sin(theta), cos(theta)); // perpendicular = tangent\n }\n // Coherence A in [0,1] \u2014 high where one eigenvalue dominates (a strong edge),\n // ~0 in flat zones. Relax toward the smooth fallback by (1 - A).\n float A = (lambdaMaj - lambdaMin) / (lambdaMaj + lambdaMin + 1e-6);\n A = clamp(A, 0.0, 1.0);\n // Coherence-weighted blend: full tensor in coherent zones, smooth in flat ones.\n // Resolve the 180\u00B0 sign ambiguity toward the fallback so the blend is continuous.\n if (dot(tangent, fallbackDir) < 0.0) tangent = -tangent;\n vec2 dir = normalize(mix(fallbackDir, tangent, A) + 1e-6);\n return vec3(dir, A);\n}\n\nvec3 mediumWatercolor(vec3 col, vec2 p, float t) {\n // Wet-edge cauliflowers via luma-gradient magnitude\n float eps = 0.004;\n vec3 cx1 = sampleBase(p + vec2(eps, 0.0), t);\n vec3 cx2 = sampleBase(p - vec2(eps, 0.0), t);\n vec3 cy1 = sampleBase(p + vec2(0.0, eps), t);\n vec3 cy2 = sampleBase(p - vec2(0.0, eps), t);\n float gx = dot(cx1 - cx2, W_LUMA);\n float gy = dot(cy1 - cy2, W_LUMA);\n float edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n float mask = smoothstep(0.0, 2.5, edge);\n col *= mix(1.0, 0.78, mask * uWetEdge);\n\n // Granulation \u2014 pigment settles in paper tooth\n float paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n float pigLoad = 1.0 - dot(col, W_LUMA);\n col *= 1.0 - uGranulation * pigLoad * (paper - 0.5);\n\n // Wash banding \u2014 faint horizontal wet gradient\n float band = fbm(vec2(p.x * 1.5, p.y * 0.4));\n col *= 1.0 + 0.04 * (band - 0.5);\n return col;\n}\n\nvec3 mediumPastel(vec3 col, vec2 p, float t) {\n vec2 flow = flowField(p, t);\n vec2 perp = vec2(-flow.y, flow.x);\n float along = dot(p, flow) * uStrokeScale;\n float across = dot(p, perp) * uStrokeScale;\n across += 0.03 * (vnoise(p * 260.0) - 0.5);\n\n float aniso = mix(1.0, 0.18, uStrokeAnisotropy);\n float stroke = fbm(vec2(along * aniso, across));\n col *= mix(1.0, 0.82 + 0.32 * stroke, uStrokeAmount);\n\n // Pastel tooth \u2014 tiny high-frequency grain\n float tooth = vnoise(p * 800.0);\n col *= 1.0 - 0.08 * uStrokeAmount * (tooth - 0.5);\n return col;\n}\n";
|
|
2
|
+
export declare const AURORA_MEDIUMS_POST_BRUSH_GLSL = "// \u2500\u2500 Crayon / oil-pastel \u2014 paper tooth \u00D7 wax pigment (PEER medium) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Crayon is not strokes. It's pigment crumbs dragged across paper tooth.\n// Model: heavy 2D tooth noise at multiple scales, anisotropically stretched\n// along flow direction, multiplied into the base color. Add a slow \"waxy\n// film\" that slightly unifies hues, and occasional darker \"pressed\" spots\n// where the crayon dug in. NO straight segments. Dispatched at main() level as\n// a peer of pastel/watercolor/oil \u2014 never a branch inside mediumOil().\n// AW.W4.4 \u2014 GENUINE oil pastel: pigment DEPOSITED on the paper tooth, a broken\n// SCUMBLE upper layer letting the paper/lower color show through, and a waxy BURNISH\n// film whose sheen grows with layer count. Reworked from the old tooth-multiply\n// gradient into a material-truth deposition model. Oriented along the ETF field\n// (W4.1); OKLCh broken color (W5). The oil-pastel first-class medium (uMedium==6)\n// AND the legacy crayon peer (uMedium==4) both dispatch this single body.\nvec3 mediumCrayon(vec3 col, vec2 p, float t) {\n // Orient the deposition streaks along the structure-tensor edge-tangent. The\n // painterly mediums force uStrokeOrient==tensor, so read the tensor field directly;\n // fall back to flowField for the legacy crayon strokeMode route.\n vec2 flow = (uStrokeOrient == 1)\n ? structureTensorField(p, t, flowField(p, t)).xy\n : flowField(p, t);\n float ang = atan(flow.y, flow.x);\n float ca = cos(-ang), sa = sin(-ang);\n vec2 pr = vec2(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n float aniso = mix(0.45, 0.95, uStrokeAnisotropy);\n float scale = max(uStrokeScale * 1.6, 180.0);\n\n // \u2500\u2500 Paper TOOTH height field \u2014 the relief the pigment rides. Anisotropic\n // (squished along flow, coarse cross-flow). Peaks = ridges, valleys = paper pits.\n float t1 = vnoise(vec2(pr.x * scale * aniso, pr.y * scale));\n float t2 = vnoise(vec2(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + 11.0);\n float t3 = vnoise(vec2(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + 23.0);\n float paperHeight = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n\n // \u2500\u2500 DEPOSITION \u2014 pigment lands on the tooth PEAKS and skips the valleys. Light\n // pressure (low uStrokeAmount) shows paper through the valleys; heavy pressure\n // fills them. deposit in [0,1] is the per-pixel pigment coverage.\n float pressure = clamp(uStrokeAmount, 0.0, 1.0);\n float toothFloor = mix(0.62, 0.18, pressure); // heavy pressure lowers the floor\n float deposit = smoothstep(toothFloor, toothFloor + 0.35, paperHeight);\n\n // \u2500\u2500 SCUMBLE \u2014 a broken UPPER layer at coverage < 1 (the signature oil-pastel\n // move). A coarse mask gates a lighter dragged stroke over the deposition so the\n // LOWER color shows through the gaps. Drag direction follows the tensor flow.\n float scumbleMask = smoothstep(0.35, 0.85, vnoise(vec2(pr.x * scale * 0.55, pr.y * scale * 0.22) + 7.0));\n float scumbleCoverage = scumbleMask * pressure * 0.6; // < 1 \u2014 paper/lower shows\n\n // The lower color is the base; the deposited pigment is the base lifted into the\n // tooth. Mix the base toward the deposited layer by the deposition coverage, then\n // let the scumble break the upper layer so the lower reads through the gaps.\n vec3 paper = col * 0.92; // the paper-showing-through tone\n vec3 deposited = col; // the pigment-on-tooth tone\n vec3 result = mix(paper, deposited, deposit);\n // Scumble: a broken pass of the deposited color at < 1 coverage.\n result = mix(result, deposited * (0.96 + 0.08 * t1), scumbleCoverage);\n\n // \u2500\u2500 WAXY BURNISH FILM \u2014 a low-roughness BROAD specular lobe whose sheen grows\n // with the pigment build-up (burnish), distinct from oil's sharp glint. Reads the\n // paper-height gradient as a soft normal and lights it with the movable uLightDir.\n float waxNormalZ = 2.4;\n vec3 N = normalize(vec3(-dFdx(paperHeight) * 30.0, -dFdy(paperHeight) * 30.0, waxNormalZ));\n vec3 L = normalize(uLightDir);\n vec3 V = vec3(0.0, 0.0, 1.0);\n vec3 H = normalize(L + V);\n float burnish = deposit * (0.5 + 0.5 * scumbleMask); // sheen grows with build-up\n float sheen = pow(max(dot(N, H), 0.0), 6.0); // broad waxy lobe\n result += burnish * sheen * 0.10 * uLightColor;\n\n // \u2500\u2500 Broken-color pigment: stable wax/pigment patches in OKLCh (W5), not flicker.\n vec2 pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n float pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + 19.0));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + 17.0),\n hash21(pigmentCell * 2.1 + 31.0),\n 0.45 + 0.55 * pigmentMask\n );\n\n // Oil pastel is saturation-amplified (the waxy chroma punch).\n result = saturate3(result, 1.12);\n\n return result;\n}\n\nvec3 mediumOil(vec3 col, vec2 p, float t) {\n // Mode knobs (uStrokeMode) \u2014 oil-stroke modes ONLY (crayon is a peer medium,\n // uMedium==4, dispatched at main()):\n // 0 oil \u2014 balanced modern-abstract/palette-knife hybrid\n // 1 knife \u2014 palette-knife impasto: razor edges, heavy bristle/shadow\n // 3 brushwork \u2014 thick bristle brush\n int mode = uStrokeMode;\n\n // Per-mode parameters\n int shapeType = 0; // tapered\n float bristleAmp = 0.25; // 0..0.5\n float streakFreq = 9.0;\n float streakAmp = 0.09;\n float impastoAmp = 0.9;\n float hardness = 0.80; // edge compositing\n float toothScale = 240.0;\n float toothAmp = 0.09;\n float pigmentSat = 1.03;\n float densityBig = 0.65;\n float densityMed = 0.78;\n float densitySml = 0.90;\n\n if (mode == 1) { // palette knife\n shapeType = 3; // flat, even\n bristleAmp = 0.12;\n streakFreq = 4.0; streakAmp = 0.05;\n impastoAmp = 1.6;\n hardness = 0.95;\n toothAmp = 0.04;\n densityBig = 0.80; densityMed = 0.88; densitySml = 0.70;\n } else if (mode == 3) { // thick brushwork\n shapeType = 0; // tapered\n bristleAmp = 0.32;\n streakFreq = 14.0; streakAmp = 0.14;\n impastoAmp = 1.2;\n hardness = 0.85;\n toothAmp = 0.07;\n }\n\n // Scales & multipliers from uniforms\n float baseScale = max(uStrokeScale * 0.006, 0.008);\n // Three layers: big gestural, medium body, small dabs\n float sBig = baseScale * 2.4;\n float sMed = baseScale * 1.1;\n float sSml = baseScale * 0.45;\n\n float lenMulBig = mix(2.2, 3.8, uStrokeAnisotropy);\n float widMulBig = mix(0.55, 0.32, uStrokeAnisotropy);\n float lenMulMed = mix(2.0, 3.4, uStrokeAnisotropy);\n float widMulMed = mix(0.50, 0.30, uStrokeAnisotropy);\n float lenMulSml = mix(1.6, 2.6, uStrokeAnisotropy);\n float widMulSml = mix(0.45, 0.32, uStrokeAnisotropy);\n\n float jitterAmt = 0.75; // large jitter \u2014 no grid\n vec2 flow = flowField(p, t);\n\n vec3 result = col;\n // AW.W4.2 \u2014 accumulated paint HEIGHT across the stroke layers. The relight reads\n // its gradient for the normal; the canvas tooth seeds the base relief.\n float height = 0.0;\n\n // Layer 1 \u2014 big gestural strokes (sparse, shaping)\n StrokeHit hBig = bestOil(p, sBig, lenMulBig, widMulBig, jitterAmt * 0.55,\n densityBig, shapeType, bristleAmp, flow, t, 1.3);\n paintOver(result, height, hBig, streakFreq * 0.7, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 1.3);\n\n // Layer 2 \u2014 medium body strokes\n StrokeHit hMed = bestOil(p + vec2(11.3, 3.7), sMed, lenMulMed, widMulMed,\n jitterAmt, densityMed, shapeType, bristleAmp, flow, t, 2.7);\n paintOver(result, height, hMed, streakFreq, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 2.7);\n\n // Layer 3 \u2014 small dabs (more frequent, smaller)\n int smlShape = (mode == 1) ? 2 : shapeType; // knife uses dabs for sparkle\n StrokeHit hSml = bestOil(p + vec2(-5.1, 8.4), sSml, lenMulSml, widMulSml,\n jitterAmt * 1.3, densitySml, smlShape,\n bristleAmp * 0.85, flow, t, 4.1);\n paintOver(result, height, hSml, streakFreq * 1.4, streakAmp * 0.8,\n uImpasto * impastoAmp * 0.65 * uStrokeAmount, hardness, 4.1);\n\n // Layer 4 \u2014 fill dabs (very dense, very small) \u2014 covers bald spots\n float sFill = baseScale * 0.22;\n float lenMulFill = mix(1.4, 2.0, uStrokeAnisotropy);\n float widMulFill = mix(0.50, 0.38, uStrokeAnisotropy);\n int fillShape = (mode == 1) ? 3 : 2; // knife=even, others=dab (round fills)\n StrokeHit hFill = bestOil(p + vec2(3.9, -6.2), sFill, lenMulFill, widMulFill,\n jitterAmt * 1.5, 0.95, fillShape,\n bristleAmp * 0.6, flow, t, 8.9);\n paintOver(result, height, hFill, streakFreq * 1.8, streakAmp * 0.6,\n uImpasto * impastoAmp * 0.4 * uStrokeAmount, hardness * 0.9, 8.9);\n\n // Optional crosshatch layer\n if (uStrokeLayers == 2) {\n vec2 flow2 = vec2(-flow.y, flow.x);\n StrokeHit hX = bestOil(p + vec2(7.3, -2.1), sMed, lenMulMed * 0.9, widMulMed,\n jitterAmt, densityMed * 0.7, shapeType, bristleAmp, flow2, t, 6.5);\n paintOver(result, height, hX, streakFreq, streakAmp * 0.85,\n uImpasto * impastoAmp * 0.55 * uStrokeAmount, hardness, 6.5);\n }\n\n // Canvas tooth \u2014 linen weave\n float tooth1 = vnoise(p * toothScale);\n float tooth2 = vnoise(p * toothScale * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * toothAmp * uCanvasGrain;\n\n // AW.W4.2 \u2014 relight the accumulated paint height with the movable uLightDir\n // source (diffuse + Blinn specular, in LINEAR before aces()). The canvas tooth\n // is the base relief term so the weave also catches the raking light.\n float canvasBase = tooth * toothAmp * 0.5;\n result = relightImpasto(result, height, canvasBase);\n\n // Pigment saturation boost\n result = saturate3(result, pigmentSat);\n\n return result;\n}\n\n// AW.W4.3 \u2014 the van-Gogh atomic-stroke medium. Composes the oil stroke engine\n// (mediumOil) with the W4.1 ETF orientation (forced tensor via the bridge), the\n// W4.3 energy grading (gated by uMedium==5 inside bestOil \u2014 long confident strokes\n// in the lights/coherent zones, short dabs in the darks/flat zones, the Starry-Night\n// Kolmogorov/Batchelor cascade), the W5 OKLCh per-stroke pigment jitter (already in\n// bestOil's brokenColorJitter), and the W4.2 real impasto relight. No subject matter\n// \u2014 the \"source image\" is the generated nuclei field, so strokes trace its iso-bands.\nvec3 mediumVangogh(vec3 col, vec2 p, float t) {\n return mediumOil(col, p, t);\n}";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** The tensor-target format the CONSUMER declares (NOT baked in the substrate). */
|
|
2
|
+
export declare const PAINTERLY_TENSOR_FORMAT: "rgba16float";
|
|
3
|
+
/** The anisotropic-Kuwahara sector count the CONSUMER declares. */
|
|
4
|
+
export declare const PAINTERLY_KUWAHARA_SECTORS: 8;
|
|
5
|
+
export declare const PAINTERLY_TENSOR_WGSL = "\n@group(0) @binding(0) var baseTex: texture_2d<f32>;\n@group(0) @binding(1) var baseSampler: sampler;\n\nconst W_LUMA: vec3f = vec3f(0.2126, 0.7152, 0.0722);\n\n@fragment\nfn tensor_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n let texel = 1.0 / vec2f(textureDimensions(baseTex));\n // 3x3 Sobel over luma.\n var l: array<f32, 9>;\n var k = 0;\n for (var dy = -1; dy <= 1; dy = dy + 1) {\n for (var dx = -1; dx <= 1; dx = dx + 1) {\n let s = textureSample(baseTex, baseSampler, uv + vec2f(f32(dx), f32(dy)) * texel).rgb;\n l[k] = dot(s, W_LUMA);\n k = k + 1;\n }\n }\n let gx = (l[2] + 2.0 * l[5] + l[8]) - (l[0] + 2.0 * l[3] + l[6]);\n let gy = (l[6] + 2.0 * l[7] + l[8]) - (l[0] + 2.0 * l[1] + l[2]);\n // (Jxx, Jyy, Jxy) \u2014 RGBA16F target (magnitudes exceed [0,1], 8-bit LDR clips).\n return vec4f(gx * gx, gy * gy, gx * gy, 1.0);\n}";
|
|
6
|
+
export declare const PAINTERLY_SMOOTH_WGSL = "\n@group(0) @binding(0) var tensorTex: texture_2d<f32>;\n@group(0) @binding(1) var tensorSampler: sampler;\n@group(0) @binding(2) var<uniform> dir: vec2f;\n\n@fragment\nfn smooth_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n let texel = 1.0 / vec2f(textureDimensions(tensorTex));\n // 9-tap Gaussian (sigma ~2) along the dir axis.\n let w = array<f32, 5>(0.227027, 0.194594, 0.121622, 0.054054, 0.016216);\n var sum = textureSample(tensorTex, tensorSampler, uv).xyz * w[0];\n for (var i = 1; i < 5; i = i + 1) {\n let off = dir * texel * f32(i);\n sum = sum + textureSample(tensorTex, tensorSampler, uv + off).xyz * w[i];\n sum = sum + textureSample(tensorTex, tensorSampler, uv - off).xyz * w[i];\n }\n return vec4f(sum, 1.0);\n}";
|
|
7
|
+
export declare const PAINTERLY_KUWAHARA_WGSL: string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** The wake velocity-texture format (the CONSUMER declares it; the substrate bakes none). */
|
|
2
|
+
export declare const WAKE_TEXTURE_FORMAT: "rg16float";
|
|
3
|
+
export declare const WAKE_ADVECT_WGSL = "\nstruct WakeUniforms {\n dt: f32,\n tempo: f32, // the master tempo scalar (0 under PRM/pause \u2014 freezes injection)\n dissipation: f32,\n pad: f32,\n splatPos: vec2f, // the delta-tracked pointer position (0..1)\n splatVel: vec2f, // the pointer velocity (the splat direction/strength)\n splatRadius: f32,\n rippleAmp: f32, // a click \u2192 a radial-ripple impulse\n pad2: vec2f,\n};\n\n@group(0) @binding(0) var velTex: texture_2d<f32>;\n@group(0) @binding(1) var velSampler: sampler;\n@group(0) @binding(2) var<uniform> U: WakeUniforms;\n\n@fragment\nfn advect_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n let texel = 1.0 / vec2f(textureDimensions(velTex));\n // Self-advection: trace back along the local velocity by dt (semi-Lagrangian).\n let v0 = textureSample(velTex, velSampler, uv).xy;\n let back = uv - v0 * U.dt;\n var v = textureSample(velTex, velSampler, back).xy;\n // Dissipate (the eddies decay so the wake lingers but fades).\n v = v * U.dissipation;\n\n // Delta-tracked Gaussian SPLAT \u2014 the pointer writes velocity into the field. The\n // injection is gated by the master tempo (0 under PRM \u2192 no new motion under reduce).\n let d = uv - U.splatPos;\n let g = exp(-dot(d, d) / max(U.splatRadius * U.splatRadius, 1e-6));\n v = v + U.splatVel * g * U.tempo;\n\n // Click \u2192 a radial-ripple impulse (pushes velocity radially outward from the splat).\n if (U.rippleAmp > 0.001) {\n let dir = d / max(length(d), 1e-4);\n v = v + dir * U.rippleAmp * g * U.tempo;\n }\n\n return vec4f(v, 0.0, 1.0);\n}";
|
|
4
|
+
export declare const WAKE_APPLY_WGSL = "\n@group(0) @binding(0) var wakeTex: texture_2d<f32>;\n@group(0) @binding(1) var wakeSampler: sampler;\n\nfn wakeBias(uv: vec2f) -> vec2f {\n return textureSample(wakeTex, wakeSampler, uv).xy;\n}";
|
|
@@ -5,6 +5,8 @@ export type { UseAuroraReturn } from "./composables/useAurora";
|
|
|
5
5
|
export { useCursorInteraction } from "./composables/useCursorInteraction";
|
|
6
6
|
export { createAurora } from "./composables/runtime";
|
|
7
7
|
export type { AuroraRuntimeMode, AuroraRuntimeOptions } from "./composables/runtime";
|
|
8
|
-
export { DEFAULT_AURORA_CONFIG, MAX_NUCLEI, MAX_STOPS, type AuroraConfig, type AuroraCursorApi, type AuroraFlow, type AuroraInstance, type AuroraMedium, type AuroraNucleus, type FlowPattern, type OklchStop, type StrokeMode, type WarpMode, } from "./constants/presets";
|
|
9
|
-
export {
|
|
10
|
-
export
|
|
8
|
+
export { DEFAULT_AURORA_CONFIG, MAX_NUCLEI, MAX_STOPS, type AuroraConfig, type AuroraCursorApi, type AuroraFlow, type AuroraInstance, type AuroraInteractivity, type AuroraMedium, type AuroraNucleus, type FlowPattern, type OklchStop, type StrokeMode, type StrokeOrient, type WarpMode, } from "./constants/presets";
|
|
9
|
+
export { resolveAtoms, DEFAULT_ATOMS, type AuroraAtoms, type AuroraMoodAtom, type AuroraMotionAtom, } from "./composables/atoms";
|
|
10
|
+
export { cssToOklch, deriveAurora, deriveScene, flattenPalette, hexToOklchStop, oklchStopToHex, oklchToLinear, paletteToCssGradient, } from "./composables/color";
|
|
11
|
+
export type { AuroraHarmony, AuroraMood, DeriveAuroraOptions, DeriveEasing, } from "./composables/color";
|
|
12
|
+
export type { AuroraHuePath } from "./constants/presets";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type HTMLAttributes } from "vue";
|
|
2
|
+
import { type ConstellationField } from "./constellationField";
|
|
3
|
+
/**
|
|
4
|
+
* Constellation — a slow, geometric proximity-graph lattice on a Canvas2D
|
|
5
|
+
* surface. Nodes drift + bounce; near nodes link with distance-falloff
|
|
6
|
+
* hairlines; the web leans toward the cursor and taps ripple. It composes the
|
|
7
|
+
* `useCanvas2D` substrate, so it inherits the offscreen / tab-hidden /
|
|
8
|
+
* reduced-motion freeze for free (no hand-rolled RAF-park).
|
|
9
|
+
*
|
|
10
|
+
* The lattice ships NEUTRAL. A branded skin (a focal ring, a callout label) is
|
|
11
|
+
* the consumer's `drawOverlay(ctx, field, now)` pass — it runs AFTER the four
|
|
12
|
+
* neutral passes and receives the live field so it can pin itself to a real
|
|
13
|
+
* node. Zero deck-domain content lives in the component.
|
|
14
|
+
*
|
|
15
|
+
* Palette reads `--constellation-node` / `--constellation-node-dim` /
|
|
16
|
+
* `--constellation-line` off the canvas (neutral fallbacks), so a consumer
|
|
17
|
+
* override or a dark-mode flip re-tints the lattice.
|
|
18
|
+
*/
|
|
19
|
+
type __VLS_Props = {
|
|
20
|
+
/** Node count. Default 64. */
|
|
21
|
+
count?: number;
|
|
22
|
+
/** Link distance in px (the falloff reach). Default 132. */
|
|
23
|
+
link?: number;
|
|
24
|
+
/** Drift speed. Default 0.16. */
|
|
25
|
+
speed?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Seed for a REPRODUCIBLE field. A `number` or `string` (hashed) seeds the
|
|
28
|
+
* shared `mulberry32`; omit for a fresh `Math.random` field each mount.
|
|
29
|
+
*/
|
|
30
|
+
seed?: number | string;
|
|
31
|
+
/** Steer-toward-cursor + tap ripples. Default true; auto-off under reduced-motion. */
|
|
32
|
+
pointerReactive?: boolean;
|
|
33
|
+
class?: HTMLAttributes["class"];
|
|
34
|
+
/**
|
|
35
|
+
* The skin seam. Runs after the neutral passes with the live field, so a
|
|
36
|
+
* consumer paints its own focal mark pinned to a real node. The branded
|
|
37
|
+
* content (a domain accent, a callout label) lives HERE, in the consumer.
|
|
38
|
+
*/
|
|
39
|
+
drawOverlay?: (ctx: CanvasRenderingContext2D, field: ConstellationField, now: number) => void;
|
|
40
|
+
};
|
|
41
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
42
|
+
field: ConstellationField;
|
|
43
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
44
|
+
declare const _default: typeof __VLS_export;
|
|
45
|
+
export default _default;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Reference width the `k` scale factor is keyed to (CSS px). */
|
|
2
|
+
export declare const BASE_WIDTH = 1280;
|
|
3
|
+
export interface ConstellationNode {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
vx: number;
|
|
7
|
+
vy: number;
|
|
8
|
+
r: number;
|
|
9
|
+
dim: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ConstellationRipple {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
/** `performance.now()` at first draw; -1 until stamped. */
|
|
15
|
+
start: number;
|
|
16
|
+
}
|
|
17
|
+
/** The pointer position in canvas-local CSS px (`-1` = inactive). */
|
|
18
|
+
export interface ConstellationPointer {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
}
|
|
22
|
+
/** The neutral palette resolved off the canvas custom properties. */
|
|
23
|
+
export interface ConstellationPalette {
|
|
24
|
+
node: string;
|
|
25
|
+
nodeDim: string;
|
|
26
|
+
line: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The field state the component exposes to its `drawOverlay` consumer so a
|
|
30
|
+
* skin can pin itself to a real field node. `k` is the `width / BASE_WIDTH`
|
|
31
|
+
* scale, `dpr` the device-pixel ratio applied by the substrate.
|
|
32
|
+
*/
|
|
33
|
+
export interface ConstellationField {
|
|
34
|
+
nodes: ConstellationNode[];
|
|
35
|
+
/** The host canvas — for `getComputedStyle` token reads in an overlay. */
|
|
36
|
+
canvas: HTMLCanvasElement | null;
|
|
37
|
+
w: number;
|
|
38
|
+
h: number;
|
|
39
|
+
k: number;
|
|
40
|
+
dpr: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The `<Constellation>` consumer prop surface (the public type a consumer types
|
|
44
|
+
* a wrapper against). The `drawOverlay` seam is the branded-skin injection — it
|
|
45
|
+
* runs AFTER the neutral passes with the live field.
|
|
46
|
+
*/
|
|
47
|
+
export interface ConstellationProps {
|
|
48
|
+
/** Node count. Default 64. */
|
|
49
|
+
count?: number;
|
|
50
|
+
/** Link distance in px (the falloff reach). Default 132. */
|
|
51
|
+
link?: number;
|
|
52
|
+
/** Drift speed. Default 0.16. */
|
|
53
|
+
speed?: number;
|
|
54
|
+
/** Seed for a reproducible field (number or hashed string); omit for `Math.random`. */
|
|
55
|
+
seed?: number | string;
|
|
56
|
+
/** Steer-toward-cursor + tap ripples. Default true; auto-off under reduced-motion. */
|
|
57
|
+
pointerReactive?: boolean;
|
|
58
|
+
class?: string;
|
|
59
|
+
/** The skin seam — paints the consumer's focal mark on the live field. */
|
|
60
|
+
drawOverlay?: (ctx: CanvasRenderingContext2D, field: ConstellationField, now: number) => void;
|
|
61
|
+
}
|
|
62
|
+
export declare const DEFAULT_PALETTE: ConstellationPalette;
|
|
63
|
+
/** Resolve the neutral palette off a canvas's resolved custom properties. */
|
|
64
|
+
export declare function readPalette(canvas: HTMLCanvasElement): ConstellationPalette;
|
|
65
|
+
/**
|
|
66
|
+
* Seed `count` drifting nodes inside `w × h`. `rng` is a `() => number` in
|
|
67
|
+
* `[0, 1)` (the glass-ui `mulberry32`/`Math.random`); a seeded `rng` lays out a
|
|
68
|
+
* reproducible field. No node is pinned — every node drifts (pinning a focal
|
|
69
|
+
* node is consumer skin, not field mechanics).
|
|
70
|
+
*/
|
|
71
|
+
export declare function seedField(rng: () => number, count: number, w: number, h: number, speed: number): ConstellationNode[];
|
|
72
|
+
/**
|
|
73
|
+
* Advance the field one step: drift + wall-bounce every node, then (if the
|
|
74
|
+
* pointer is live) lean nodes within reach toward the cursor WITHOUT changing
|
|
75
|
+
* their speed (the slow geometric drift is preserved). `k` is the scale factor.
|
|
76
|
+
*/
|
|
77
|
+
export declare function stepField(field: ConstellationField, k: number, speed: number, pointer: ConstellationPointer | null): void;
|
|
78
|
+
/** Pass 1 — hairline edges between any two nodes within `link` px (alpha falls off with distance). */
|
|
79
|
+
export declare function drawEdges(ctx: CanvasRenderingContext2D, field: ConstellationField, link: number, palette: ConstellationPalette): void;
|
|
80
|
+
/** Pass 2 — ambient node dots. */
|
|
81
|
+
export declare function drawNodes(ctx: CanvasRenderingContext2D, field: ConstellationField, palette: ConstellationPalette): void;
|
|
82
|
+
/** Pass 3 — the cursor joins the web: faint links to nodes within reach + a soft cursor node. */
|
|
83
|
+
export declare function drawPointerWeb(ctx: CanvasRenderingContext2D, field: ConstellationField, link: number, palette: ConstellationPalette, pointer: ConstellationPointer | null): void;
|
|
84
|
+
/** Pass 4 — tap ripples: expanding rings that fade over ~0.9s; consumed entries are spliced. */
|
|
85
|
+
export declare function drawRipples(ctx: CanvasRenderingContext2D, field: ConstellationField, now: number, ripples: ConstellationRipple[], palette: ConstellationPalette): void;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { default as Constellation } from "./Constellation.vue";
|
|
2
|
+
export { seedField, stepField, readPalette, drawEdges, drawNodes, drawPointerWeb, drawRipples, BASE_WIDTH, DEFAULT_PALETTE, } from "./constellationField";
|
|
3
|
+
export type { ConstellationNode, ConstellationRipple, ConstellationPointer, ConstellationPalette, ConstellationField, ConstellationProps, } from "./constellationField";
|