@mkbabb/glass-ui 3.6.0 → 3.8.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-q2qHCJFG.js +157 -0
- package/dist/CollapsibleContent-CP9qF1v4.js +63 -0
- package/dist/{CommandShortcut-BiVyqipe.js → CommandShortcut-SzWRhROC.js} +4 -4
- package/dist/{ContextMenuContent-De01_83g.js → ContextMenuContent-DDa0v27s.js} +1 -1
- package/dist/{ContextMenuSubContent-DrWkXKQP.js → ContextMenuSubContent-Do977Cbf.js} +4 -4
- package/dist/{DataTable-BGVXx0Ww.js → DataTable-CvlWXz65.js} +14 -7
- package/dist/DeckProgress-AVgbIULY.js +23 -0
- package/dist/{DialogContent-IQ8_BRrC.js → DialogContent-DjZWT3lK.js} +24 -24
- package/dist/{DialogFooter-Er0wA3K6.js → DialogFooter-Gprw8H_A.js} +2 -2
- package/dist/{DiscoGlyph-BaZ8OawK.js → DiscoGlyph-CG0F410s.js} +1 -1
- package/dist/{GlyphFace-B_7vOmYn.js → GlyphFace-DRjDkB0s.js} +1 -1
- package/dist/{HoverCardContent-ICfIMZX1.js → HoverCardContent-B6CcQ8He.js} +1 -1
- package/dist/{HoverPopover-DTSYkJtw.js → HoverPopover-D56O6Ylp.js} +1 -1
- package/dist/{IconTooltip-DZlCkU-0.js → IconTooltip-D2T5nYOM.js} +1 -1
- package/dist/{Input-CRJ2owjN.js → Input-DVG_J0ne.js} +4 -1
- package/dist/{Label-CZk-3nTc.js → Label-zRfbQdCn.js} +1 -1
- package/dist/{MetricBadge-BlrdbOGN.js → MetricBadge-lTc002u2.js} +1 -1
- package/dist/{Notification-DP_ApJLo.js → Notification-BxK8LZMw.js} +5 -4
- package/dist/{NumberFieldContent-Bhhmk95p.js → NumberFieldContent-CLNVR1iY.js} +5 -5
- package/dist/{PopoverContent-B8WtJECb.js → PopoverContent-BId6ggf_.js} +2 -2
- package/dist/{Progress-DjM86vfb.js → Progress-iEuD9pxG.js} +7 -7
- package/dist/{ScrollingText-P9o_DuMn.js → ScrollingText-CVyqxvfv.js} +2 -2
- package/dist/{SelectScrollDownButton-BGn3rjs9.js → SelectScrollDownButton-C7TRe-ep.js} +8 -7
- package/dist/{Separator-C2XtAXRp.js → Separator-D7sD7RKj.js} +1 -1
- package/dist/{Switch-x8n6husW.js → Switch-LDKLmmgx.js} +8 -2
- package/dist/TabsList-qyHZ2lKY.js +101 -0
- package/dist/{Toaster-DdhMKfus.js → Toaster-CabYEEF_.js} +54 -60
- package/dist/{ToggleGroupItem-gyXj998A.js → ToggleGroupItem-CkXUcBlt.js} +13 -5
- package/dist/{TooltipProvider-D-JrSqDu.js → TooltipProvider-CmZzCuaG.js} +1 -1
- package/dist/{UnderlineTabs-CuIK16JI.js → UnderlineTabs-Uxn-R3MT.js} +2 -2
- package/dist/animated-digit.js +2 -2
- package/dist/api/index.d.ts +4 -2
- package/dist/api.js +1 -1
- package/dist/aurora.js +753 -359
- package/dist/{badge-Cl6JZ1B7.js → badge-DHQrc_GQ.js} +13 -9
- package/dist/badge.js +1 -1
- package/dist/{button-BcMzVrqs.js → button-CAywcrhO.js} +6 -3
- package/dist/button.js +1 -1
- package/dist/canvas.d.ts +1 -0
- package/dist/canvas.js +3 -0
- package/dist/card.js +2 -2
- package/dist/carousel.js +110 -109
- 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-DMLTunJK.js +84 -0
- package/dist/color.js +2 -2
- package/dist/command.js +1 -1
- package/dist/compile-BBTRjvQS.js +158 -0
- package/dist/components/custom/aurora/Aurora.vue.d.ts +1 -0
- package/dist/components/custom/aurora/composables/atoms.d.ts +125 -0
- package/dist/components/custom/aurora/composables/color.d.ts +7 -25
- 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 +46 -12
- 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 +60 -4
- package/dist/components/custom/aurora/constants/renderMode.d.ts +32 -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 +4 -3
- package/dist/components/custom/constellation/Constellation.vue.d.ts +40 -5
- package/dist/components/custom/constellation/constellationField.d.ts +110 -5
- package/dist/components/custom/constellation/index.d.ts +2 -2
- package/dist/components/custom/dock/GlassDock.vue.d.ts +12 -4
- package/dist/components/custom/dock/composables/dockMorphContext.d.ts +57 -0
- package/dist/components/custom/dock/composables/index.d.ts +3 -0
- package/dist/components/custom/dock/composables/useDockHold.d.ts +61 -0
- package/dist/components/custom/dock/composables/useLayerTransition.d.ts +13 -59
- package/dist/components/custom/dock/index.d.ts +2 -0
- 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 +15 -0
- package/dist/components/custom/goo-blob/composables/useBlobMood.d.ts +18 -2
- package/dist/components/custom/goo-blob/composables/useBlobPointer.d.ts +47 -4
- package/dist/components/custom/goo-blob/composables/useBlobSatellites.d.ts +2 -0
- package/dist/components/custom/goo-blob/composables/useMetaballRenderer.d.ts +19 -4
- 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 +48 -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 +26 -3
- package/dist/components/ui/card/CardAction.vue.d.ts +24 -0
- package/dist/components/ui/card/index.d.ts +2 -1
- package/dist/components/ui/collapsible/CollapsibleTrigger.vue.d.ts +5 -1
- package/dist/components/ui/progress/ProgressDefault.vue.d.ts +14 -4
- package/dist/components/ui/slider/Slider.vue.d.ts +3 -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 +69 -0
- package/dist/composables/dom/index.d.ts +1 -1
- package/dist/composables/dom/useResolveTokenColor.d.ts +27 -0
- package/dist/composables/glass/canvas2d/index.d.ts +2 -1
- package/dist/composables/glass/canvas2d/resolveCanvasColor.d.ts +20 -0
- package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +15 -1
- package/dist/composables/glass/createGPUCanvas.d.ts +40 -0
- package/dist/composables/glass/index.d.ts +3 -1
- package/dist/composables/glass/useSpecularTracking.d.ts +33 -0
- package/dist/composables/glass/webgl/createCanvasLifecycle.d.ts +45 -0
- package/dist/composables/glass/webgl/shaders/procedural-color.glsl.d.ts +7 -0
- package/dist/composables/glass/webgl/useWebGLCanvas.d.ts +5 -5
- package/dist/composables/motion/core/index.d.ts +1 -0
- package/dist/configurator.js +1 -1
- package/dist/confirm-dialog.js +3 -3
- package/dist/constellation.js +144 -75
- 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/deck-progress.d.ts +1 -0
- package/dist/deck-progress.js +2 -0
- package/dist/dialog.js +2 -2
- package/dist/disco-glyph.js +1 -1
- package/dist/dock.js +388 -287
- package/dist/{dockContext-spUj_-E5.js → dockContext-BKeIMmNk.js} +1 -1
- package/dist/dom.js +6 -6
- package/dist/{dropdown-menu-EFjl5iKo.js → dropdown-menu-B_4bWyyG.js} +5 -5
- package/dist/dropdown-menu.js +1 -1
- package/dist/expandable-container.js +2 -2
- package/dist/fonts/README.md +26 -18
- 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 +12 -7
- package/dist/glass-panel.js +7 -7
- package/dist/glass-ui.css +1 -1
- package/dist/glass-ui.js +209 -206
- package/dist/glyph-face.js +2 -2
- package/dist/goo-blob.js +485 -300
- 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/index.d.ts +1 -0
- 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-BsYjEFOW.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 +68 -98
- 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/resolveCanvasColor-BBiYfp8g.js +11 -0
- 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 +12 -12
- package/dist/select.js +3 -3
- package/dist/separator.js +1 -1
- package/dist/{sheet-CQYYrkr9.js → sheet-VZEtNFvO.js} +4 -4
- package/dist/sheet.js +1 -1
- package/dist/slider-D9puf-Ko.js +142 -0
- package/dist/slider.js +1 -1
- package/dist/sortable-list.js +3 -3
- package/dist/styles/components.css +1 -1
- package/dist/styles/dock-controls.css +21 -18
- package/dist/styles/dock.css +415 -224
- package/dist/styles/fonts.css +0 -25
- package/dist/styles/glass.css +100 -41
- package/dist/styles/theme.css +58 -2
- package/dist/styles/tokens.css +139 -32
- package/dist/styles/typography.css +55 -72
- package/dist/styles/utilities.css +19 -14
- package/dist/styles/view-transition.css +12 -40
- package/dist/supportsCssTimeline-C5oe0lnD.js +12 -0
- package/dist/switch.js +1 -1
- package/dist/tabs.js +5 -5
- package/dist/timeline.js +3 -3
- 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-DfVgP4te.js → useAnimatedNumber-C_3wZLx4.js} +1 -1
- package/dist/{useCanvas2D-BeNJqqIS.js → useCanvas2D-ClrQuZtp.js} +2 -1
- package/dist/{useConfiguratorState-DUtC1jxr.js → useConfiguratorState-CMff4GzH.js} +5 -5
- package/dist/useResolveTokenColor-r9pnNjqK.js +24 -0
- package/dist/useScrollProgress-C9ysDic5.js +35 -0
- package/dist/useSpecularTracking-BVRZsRHA.js +26 -0
- package/dist/{useTextHighlight-Dmtofpk2.js → useTextHighlight-9nMW23Qb.js} +1 -1
- package/dist/{useTouchGate-CS5Csc2h.js → useTouchGate-WFyJYsth.js} +1 -1
- package/dist/vReveal-CqaCz_mC.js +44 -0
- package/dist/watercolor-dot.js +2 -2
- package/dist/{x-C4pz9nbD.js → x-Dfsegz9Y.js} +1 -1
- package/package.json +64 -4
- package/src/fonts/README.md +26 -18
- package/src/styles/dock-controls.css +21 -18
- package/src/styles/dock.css +415 -224
- package/src/styles/fonts.css +0 -25
- package/src/styles/glass.css +100 -41
- package/src/styles/theme.css +58 -2
- package/src/styles/tokens.css +139 -32
- package/src/styles/typography.css +55 -72
- package/src/styles/utilities.css +19 -14
- package/src/styles/view-transition.css +12 -40
- package/dist/CardFooter-C390imy7.js +0 -129
- package/dist/CollapsibleContent-cauTbZEM.js +0 -52
- package/dist/TabsIndicator-DA0x9gPr.js +0 -97
- package/dist/color-DS-iLRVM.js +0 -33
- package/dist/compile-C62K_FpC.js +0 -106
- package/dist/fonts/fraunces/fraunces-latin.woff2 +0 -0
- package/dist/slider-CgNzRSh6.js +0 -129
- package/dist/useViewTransition-DI0Rs2rV.js +0 -21
- package/dist/vReveal-u2wSG2El.js +0 -35
- package/src/fonts/fraunces/fraunces-latin.woff2 +0 -0
- /package/dist/{InstrumentChassis-DaHIZCqy.js → InstrumentChassis-C2f6cBaQ.js} +0 -0
- /package/dist/{InstrumentRail-B0qqLFN0.js → InstrumentRail-CUJ05YVG.js} +0 -0
- /package/dist/{ModalOverlay-B_CBtqcE.js → ModalOverlay-B1GkAjNb.js} +0 -0
- /package/dist/{PaperBackdrop-D_YZW47j.js → PaperBackdrop-CqwjnfLv.js} +0 -0
- /package/dist/{SelectGroup-CMdoCjRE.js → SelectGroup-BFiod7X2.js} +0 -0
- /package/dist/{SelectSeparator-CaJnPF3_.js → SelectSeparator-L8pPEn4L.js} +0 -0
- /package/dist/{_plugin-vue_export-helper-C1je1s0u.js → _plugin-vue_export-helper-ok8pgSl5.js} +0 -0
- /package/dist/composables/{dom → motion}/useTextHighlight.d.ts +0 -0
- /package/dist/{constants-C8NMonuo.js → constants-BPOqYJAm.js} +0 -0
- /package/dist/{createContext-DBMGRlx4.js → createContext-B44Sah_H.js} +0 -0
- /package/dist/{createLucideIcon-DuDoe_ra.js → createLucideIcon-B6uNAU9G.js} +0 -0
- /package/dist/{menuItemVariants-C2QlXqT3.js → menuItemVariants-DKM85SNE.js} +0 -0
- /package/dist/{presets-Dyv9Zl7W.js → presets-D1XPuTj-.js} +0 -0
- /package/dist/{prng-CjVgzjQT.js → prng-COGhh02-.js} +0 -0
- /package/dist/{useGlassRenderer-Dn3WpfG-.js → useGlassRenderer-5I0YkI4t.js} +0 -0
- /package/dist/{useGlobalDark-D7R9OypB.js → useGlobalDark-D3EBWzXK.js} +0 -0
- /package/dist/{useIntersectionPause-DsQYGc8V.js → useIntersectionPause-Bj4sPkMh.js} +0 -0
- /package/dist/{useInterval-CHVYFpXV.js → useInterval-CcLfq2WU.js} +0 -0
- /package/dist/{useKeyboardShortcuts-i8tHd5WO.js → useKeyboardShortcuts-BXWXOZ-9.js} +0 -0
- /package/dist/{useResizeObserver-DX-STszm.js → useResizeObserver-CWCbk5Px.js} +0 -0
- /package/dist/{useSortable-muI29agz.js → useSortable-MGAbCGVJ.js} +0 -0
- /package/dist/{useSpringMount-CnizvZGm.js → useSpringMount-BJSHcLl3.js} +0 -0
- /package/dist/{useTimer-DGgoxTXL.js → useTimer-3xqf_54b.js} +0 -0
- /package/dist/{useUserInvalidAria-DmvZ_6Dx.js → useUserInvalidAria-DoJKMZ3s.js} +0 -0
- /package/dist/{useViewportReady-Dcvf0c4p.js → useViewportReady-RIr5Wgkh.js} +0 -0
|
@@ -6,15 +6,16 @@
|
|
|
6
6
|
* NEW union member is a COMPILE error until it gets a slot — no silent stale-Record
|
|
7
7
|
* gap where a fresh enum value uploads `undefined`.
|
|
8
8
|
*
|
|
9
|
-
* Crayon is a
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `
|
|
13
|
-
*
|
|
9
|
+
* Crayon is a FIRST-CLASS medium (`uMedium == 4`, AX.W13), NOT a stroke mode: the
|
|
10
|
+
* shader has a dedicated `mediumCrayon()` body (DRY wax-on-tooth). `MEDIUM_ID` carries
|
|
11
|
+
* the `crayon: 4` slot and `StrokeMode` no longer has a `crayon` member — the legacy
|
|
12
|
+
* `oil` + `strokeMode:"crayon"` peer-route is REMOVED (clean break); a crayon surface
|
|
13
|
+
* selects `medium:"crayon"`.
|
|
14
14
|
*
|
|
15
15
|
* The pre-allocated upload buffers live in the bridge: a slider drag refills them
|
|
16
16
|
* in place rather than allocating ~8 Float32Arrays per frame.
|
|
17
17
|
*/
|
|
18
|
+
import type { OklchStop } from "../../../../composables/color";
|
|
18
19
|
import type { UniformLocations } from "./glSetup";
|
|
19
20
|
import type { CursorState } from "./cursorModel";
|
|
20
21
|
import { type AuroraConfig } from "../constants/presets";
|
|
@@ -24,6 +25,8 @@ export declare const MEDIUM_ID: {
|
|
|
24
25
|
readonly watercolor: 2;
|
|
25
26
|
readonly oil: 3;
|
|
26
27
|
readonly crayon: 4;
|
|
28
|
+
readonly vangogh: 5;
|
|
29
|
+
readonly "oil-pastel": 6;
|
|
27
30
|
};
|
|
28
31
|
export declare const HUE_PATH_ID: {
|
|
29
32
|
readonly shorter: 0;
|
|
@@ -37,6 +40,11 @@ export declare const FLOW_ID: {
|
|
|
37
40
|
readonly swirl: 2;
|
|
38
41
|
readonly diagonal: 3;
|
|
39
42
|
readonly multi: 4;
|
|
43
|
+
readonly tensor: 5;
|
|
44
|
+
};
|
|
45
|
+
export declare const STROKE_ORIENT_ID: {
|
|
46
|
+
readonly flow: 0;
|
|
47
|
+
readonly tensor: 1;
|
|
40
48
|
};
|
|
41
49
|
export declare const WARP_ID: {
|
|
42
50
|
readonly fbm: 0;
|
|
@@ -48,19 +56,45 @@ export declare const STROKE_MODE_ID: {
|
|
|
48
56
|
readonly knife: 1;
|
|
49
57
|
readonly chunky: 3;
|
|
50
58
|
};
|
|
59
|
+
export declare const AURORA_CATCH_LIGHT_ANCHOR: OklchStop;
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a config's `lightColor` to a LINEAR-light `[r,g,b]` triple. Accepts either
|
|
62
|
+
* a raw linear triple (the array form, passed through) OR an `{L,C,h}` OKLCh anchor
|
|
63
|
+
* (derived through the shared `warmCatchLight` helper). Omitted = the canonical aurora
|
|
64
|
+
* warm-white from `AURORA_CATCH_LIGHT_ANCHOR`.
|
|
65
|
+
*/
|
|
66
|
+
export declare function resolveLightColor(lightColor: [number, number, number] | OklchStop | undefined): [number, number, number];
|
|
51
67
|
/**
|
|
52
|
-
* The effective `uMedium` int for a config.
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* `
|
|
68
|
+
* The effective `uMedium` int for a config. AX.W13 — a pure identity map over the
|
|
69
|
+
* medium union (the `oil` + `strokeMode:"crayon"` peer-route is REMOVED; crayon is its
|
|
70
|
+
* own first-class `medium:"crayon"`). `medium` is total over `MEDIUM_ID` (the
|
|
71
|
+
* `satisfies` guarantees it), so this never resolves `undefined`.
|
|
56
72
|
*/
|
|
57
73
|
export declare function resolveMediumId(cfg: AuroraConfig): number;
|
|
58
74
|
/**
|
|
59
|
-
* The `uStrokeMode` int for a config.
|
|
60
|
-
*
|
|
61
|
-
* oil default; the other modes map directly.
|
|
75
|
+
* The `uStrokeMode` int for a config. AX.W13 — `strokeMode` is total over
|
|
76
|
+
* `STROKE_MODE_ID` (no `crayon` special-case), so this is a direct map.
|
|
62
77
|
*/
|
|
63
78
|
export declare function resolveStrokeModeId(cfg: AuroraConfig): number;
|
|
79
|
+
/**
|
|
80
|
+
* AX.W13 — the `uStrokeOrient` int for a config. The painterly mediums (`vangogh`,
|
|
81
|
+
* `oil-pastel`, `crayon`) FORCE the structure-tensor orientation regardless of the
|
|
82
|
+
* config's `strokeOrient` field — their marks must hug the color zones (the
|
|
83
|
+
* congruent-to-real-works contract). Every other medium honors the config's
|
|
84
|
+
* `strokeOrient` (default `"flow"`).
|
|
85
|
+
*/
|
|
86
|
+
export declare function resolveStrokeOrientId(cfg: AuroraConfig): number;
|
|
87
|
+
export declare const WGPU_UNIFORM_FLOATS = 20;
|
|
88
|
+
export declare const WGPU_FIELD_FLOATS: number;
|
|
89
|
+
/**
|
|
90
|
+
* Pack an `AuroraConfig` into the SPLIT WebGPU buffers (AX.W07). `uniformOut` takes the
|
|
91
|
+
* 16 constant-indexed scalars (the f32-packed counts the shader i32()-casts); `fieldOut`
|
|
92
|
+
* takes the palette + nuclei vec4 arrays for the storage `Field` buffer. Both are filled
|
|
93
|
+
* in place (the caller owns the reused buffers so a slider drag does not churn the GC).
|
|
94
|
+
* The WebGL2 fallback path is unaffected (it keeps its `gl.uniform*` calls). The float
|
|
95
|
+
* layouts MUST mirror aurora.wgsl.ts `struct Uniforms` + `struct Field`.
|
|
96
|
+
*/
|
|
97
|
+
export declare function packGPUUniforms(cfg: AuroraConfig, timeSec: number, uniformOut: Float32Array, fieldOut: Float32Array): void;
|
|
64
98
|
/**
|
|
65
99
|
* Build the per-config uniform uploader bound to a program's uniform location
|
|
66
100
|
* 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,
|
|
@@ -39,11 +39,35 @@ export interface AuroraNucleus {
|
|
|
39
39
|
*/
|
|
40
40
|
angle?: number;
|
|
41
41
|
}
|
|
42
|
-
export type AuroraMedium = "smooth" | "pastel" | "watercolor" | "oil";
|
|
43
|
-
/** Applied only when `medium === "oil"`. Routes inside the shader. */
|
|
44
|
-
export type StrokeMode = "oil" | "knife" | "
|
|
45
|
-
export type FlowPattern = "none" | "radial" | "swirl" | "diagonal" | "multi";
|
|
42
|
+
export type AuroraMedium = "smooth" | "pastel" | "watercolor" | "oil" | "crayon" | "vangogh" | "oil-pastel";
|
|
43
|
+
/** Oil-stroke sub-mode. Applied only when `medium === "oil"`. Routes inside the shader. */
|
|
44
|
+
export type StrokeMode = "oil" | "knife" | "chunky";
|
|
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";
|
|
46
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
|
+
}
|
|
47
71
|
export interface AuroraFlow {
|
|
48
72
|
pattern: FlowPattern;
|
|
49
73
|
/** 0..1 CSS-top-origin. Runtime flips Y. */
|
|
@@ -73,6 +97,14 @@ export interface AuroraConfig {
|
|
|
73
97
|
*/
|
|
74
98
|
huePath?: AuroraHuePath;
|
|
75
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;
|
|
76
108
|
strokeAmount: number;
|
|
77
109
|
strokeScale: number;
|
|
78
110
|
strokeAnisotropy: number;
|
|
@@ -83,6 +115,23 @@ export interface AuroraConfig {
|
|
|
83
115
|
impasto: number;
|
|
84
116
|
brokenColor: number;
|
|
85
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 / AX.W11 — the relight tint (the impasto catch-light). Optional;
|
|
128
|
+
* omitted = the canonical warm-white the shared `warmCatchLight` OKLCh helper
|
|
129
|
+
* derives (the `(0.985, 0.0125, 77.5°)` anchor — perceptually the prior eyeballed
|
|
130
|
+
* warm-white, now on the OKLCh core). Author it either as a raw LINEAR-light
|
|
131
|
+
* `[r,g,b]` triple OR as an `{L,C,h}` OKLCh anchor (the bridge derives the linear
|
|
132
|
+
* triple via `warmCatchLight`). The diffuse + Blinn-specular terms modulate by it.
|
|
133
|
+
*/
|
|
134
|
+
lightColor?: [number, number, number] | OklchStop;
|
|
86
135
|
nucleiDrift: number;
|
|
87
136
|
paletteDrift: number;
|
|
88
137
|
breathDepth: number;
|
|
@@ -90,6 +139,13 @@ export interface AuroraConfig {
|
|
|
90
139
|
saturation: number;
|
|
91
140
|
paperGrain: number;
|
|
92
141
|
alpha: number;
|
|
142
|
+
/**
|
|
143
|
+
* AW.W6/W8 — the pointer/scroll interactivity opt-in (default OFF — the wispy-sky
|
|
144
|
+
* default stays static). W6 declares the SHAPE; W8 wires the cursor-as-light /
|
|
145
|
+
* velocity-reactive flow / scroll-coupling / WebGPU-wake behavior. Omitted = every
|
|
146
|
+
* axis off. Aliased on `/api` as part of the `AuroraConfig` surface.
|
|
147
|
+
*/
|
|
148
|
+
interactivity?: AuroraInteractivity;
|
|
93
149
|
}
|
|
94
150
|
/**
|
|
95
151
|
* Pointer-cursor API exposed by `createAurora`. Values in 0..1 (CSS-top-origin).
|
|
@@ -7,6 +7,34 @@
|
|
|
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";
|
|
16
|
+
/**
|
|
17
|
+
* AX.W07 — the WebGPU-parity lever (internal, NOT a consumer prop — consumers should
|
|
18
|
+
* not know the WGSL twin exists). The shared W07↔W14 switch:
|
|
19
|
+
*
|
|
20
|
+
* - W07 sets it `false`. The WGSL twin's two device-proven black-canvas defects (the
|
|
21
|
+
* int-in-float counts + the `var<uniform>` dynamic-index Metal miscompile) are FIXED
|
|
22
|
+
* at their root in this wave, but the twin is STILL reduced-parity by design even
|
|
23
|
+
* unblocked (isotropic-only nuclei, fbm-only warp, NO flow/cursor/lighting/mediums/
|
|
24
|
+
* strokes/grain, a straight-OKLab palette vs the GLSL OKLCh hue-arc). So a
|
|
25
|
+
* WebGPU-capable machine is served the TESTED universal WebGL2 path (DESIGN.md
|
|
26
|
+
* invariant 8 — the correct single-pass renderer) rather than the reduced-parity twin:
|
|
27
|
+
* `resolveRenderModeAsync` returns `{ substrate: "webgl", device: null }` while this
|
|
28
|
+
* is `false`. This is a knowingly-DEGRADED-by-design phased outcome (SPEC.md
|
|
29
|
+
* §DEGRADED).
|
|
30
|
+
* - W14 (band C · AURORA) owns the flip — and ONLY for the OPT-IN Kuwahara painterly
|
|
31
|
+
* finish over a parity-floor field, never to auto-default a capable machine (the
|
|
32
|
+
* §4 note 14 DELETE-the-auto-default disposition). W14 is the NAMED restoration wave.
|
|
33
|
+
*
|
|
34
|
+
* The gate forces the WebGPU path internally regardless of this lever (it tests the
|
|
35
|
+
* SHADER, not the default routing); only the LIVE default routing reads it.
|
|
36
|
+
*/
|
|
37
|
+
export declare const WEBGPU_PARITY = false;
|
|
10
38
|
/**
|
|
11
39
|
* Resolve `"auto"` to a concrete substrate per device tier. `"webgl"` and
|
|
12
40
|
* `"css"` pass through unchanged.
|
|
@@ -28,3 +56,7 @@ export type AuroraRenderMode = "webgl" | "css" | "auto";
|
|
|
28
56
|
* the consumer remounts to re-evaluate, matching the deferred-arm contract).
|
|
29
57
|
*/
|
|
30
58
|
export declare function resolveRenderMode(mode: AuroraRenderMode): "webgl" | "css";
|
|
59
|
+
export declare function resolveRenderModeAsync(mode: AuroraRenderMode): Promise<{
|
|
60
|
+
substrate: AuroraSubstrate;
|
|
61
|
+
device: GPUDevice | null;
|
|
62
|
+
}>;
|
|
@@ -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 uniform struct \u2014 ONLY constant-indexed scalars (safe in the uniform address\n// space). The five count/enum fields are f32 (AX.W07 1a): the WebGPU write path packs\n// them as floats and the shader i32-casts at each use, so WGSL never reads a float\n// bit-pattern as a raw i32. The dynamically-indexed arrays moved OUT to struct Field\n// below (AX.W07 1b).\nstruct Uniforms {\n time: f32,\n stopCount: f32,\n nucleiCount: f32,\n softmaxBeta: f32,\n valueVariance: f32,\n warpAmount: f32,\n warpScale: f32,\n warpDrift: f32,\n warpMode: f32,\n noiseOctaves: f32,\n medium: f32,\n breathDepth: f32,\n breathPeriod: f32,\n saturation: f32,\n paperGrain: f32,\n alpha: f32,\n // AX.W11 \u2014 the huePath enum (f32-packed, i32()-cast in samplePalette) carries the\n // shared ramp's OKLab-rectangular-vs-OKLCh-hue-arc dispatch into the WebGPU twin so\n // a huePath:'increasing' rainbow config arcs identically to the WebGL2 oracle. The\n // CPU pad keeps the buffer vec4-aligned (WGPU_UNIFORM_FLOATS rounds to 20).\n huePath: f32,\n};\n\n// The dynamically-indexed arrays \u2014 bound var<storage, read> (AX.W07 1b). A runtime\n// palette/nuclei index into a var<uniform> array<vec4f> returns [0,0,0,0] on Metal\n// (MSL forbids dynamic indexing of constant); storage is always-legal. The vec4 stride\n// is std140-identical to the uniform layout, so the move is byte-exact.\nstruct Field {\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@group(0) @binding(1) var<storage, read> F: Field;\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 interpolateHueTurns(h0: f32, h1: f32, t: f32, method: i32) -> f32 {\n let TAU = 2.0 * PI;\n var a = h0 / TAU;\n var b = h1 / TAU;\n let i = b - a;\n if (method == 0) { // shorter\n if (i > 0.5) { a = a + 1.0; } else if (i < -0.5) { b = b + 1.0; }\n } else if (method == 1) { // longer\n if (i > 0.0 && i < 0.5) { a = a + 1.0; } else if (i > -0.5 && i <= 0.0) { b = b + 1.0; }\n } else if (method == 2) { // increasing\n if (i < 0.0) { b = b + 1.0; }\n } else { // decreasing\n if (i > 0.0) { a = a + 1.0; }\n }\n let r = fract(a + t * (b - a));\n return r * TAU;\n}\n\nfn mixPaletteOklab(linA: vec3f, linB: vec3f, t: f32) -> vec3f {\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3f(1.0 / 3.0)));\n let lab = mix(labA, labB, vec3f(t));\n return oklabToLinearSrgb(lab);\n}\n\nfn mixPaletteOklchArc(linA: vec3f, linB: vec3f, t: f32, method: i32) -> vec3f {\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3f(1.0 / 3.0)));\n let lchA = oklabToOklch(labA);\n let lchB = oklabToOklch(labB);\n let L = mix(lchA.x, lchB.x, t);\n let C = mix(lchA.y, lchB.y, t);\n let H = interpolateHueTurns(lchA.z, lchB.z, t, method);\n return oklabToLinearSrgb(oklchToOklab(vec3f(L, C, H)));\n}\n\nfn samplePaletteRamp(a: vec3f, b: vec3f, tRaw: f32, huePath: i32) -> vec3f {\n let t = smoothstep(0.0, 1.0, tRaw);\n if (huePath == 2 || huePath == 3) {\n return mixPaletteOklchArc(a, b, t, huePath);\n }\n return mixPaletteOklab(a, b, t);\n}\n\n// AX.W12 \u2014 the painterly-medium organic noise basis (PCG2D integer-bit hash + 2D\n// simplex gradient noise), SPLICED from the shared procedural-color chunk \u2014 the EXACT\n// twin of the GLSL leaf aurora.frag.ts splices, so the hash can NEVER drift between\n// backends (proof:aurora-noise-hash-equivalence locks the twins to 1e-6). The WebGPU\n// single-pass fs_main below stays the smooth pole (it runs no medium body \u2014 the\n// painterly mediums are the W7c/W14 multi-pass), so the leaf is single-sourced HERE for\n// the twin-equivalence contract; the W14 painterly passes consume it.\n\nfn pcg2d(v0: vec2u) -> vec2u {\n var v = v0 * 1664525u + vec2u(1013904223u);\n v.x = v.x + v.y * 1664525u;\n v.y = v.y + v.x * 1664525u;\n v = v ^ (v >> vec2u(16u));\n v.x = v.x + v.y * 1664525u;\n v.y = v.y + v.x * 1664525u;\n v = v ^ (v >> vec2u(16u));\n return v;\n}\n\nfn pcgHash2(p: vec2f) -> f32 {\n let u = bitcast<vec2u>(p);\n return f32(pcg2d(u).x) * (1.0 / 4294967296.0);\n}\n\nfn pcgGrad2(cell: vec2i) -> vec2f {\n let h = pcg2d(bitcast<vec2u>(cell));\n let ang = f32(h.x) * (1.0 / 4294967296.0) * 6.28318530717958647692;\n return vec2f(cos(ang), sin(ang));\n}\n\nfn gnoise(p: vec2f) -> f32 {\n let F2 = 0.36602540378443864676;\n let G2 = 0.21132486540518711775;\n let s = (p.x + p.y) * F2;\n let i = floor(p + vec2f(s));\n let tt = (i.x + i.y) * G2;\n let x0 = p - (i - vec2f(tt));\n var i1 = vec2i(0, 1);\n if (x0.x > x0.y) { i1 = vec2i(1, 0); }\n let x1 = x0 - vec2f(i1) + vec2f(G2);\n let x2 = x0 - vec2f(1.0) + vec2f(2.0 * G2);\n let ii = vec2i(i);\n var n = 0.0;\n var t0 = 0.5 - dot(x0, x0);\n if (t0 > 0.0) { t0 = t0 * t0; n = n + t0 * t0 * dot(pcgGrad2(ii), x0); }\n var t1 = 0.5 - dot(x1, x1);\n if (t1 > 0.0) { t1 = t1 * t1; n = n + t1 * t1 * dot(pcgGrad2(ii + i1), x1); }\n var t2 = 0.5 - dot(x2, x2);\n if (t2 > 0.0) { t2 = t2 * t2; n = n + t2 * t2 * dot(pcgGrad2(ii + vec2i(1, 1)), x2); }\n return 70.0 * n;\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 let octaves = i32(U.noiseOctaves); // AX.W07 1a \u2014 i32()-cast the f32-packed count.\n for (var i = 0; i < 5; i = i + 1) {\n if (i >= octaves) { 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 // AX.W11 \u2014 splice the SHARED samplePaletteRamp (the smoothstep ease + the OKLab-\n // rectangular-vs-OKLCh-hue-arc huePath dispatch), the EXACT twin the GLSL oracle\n // splices. The body here only selects the bracketing stop pair + the raw inter-stop\n // t (the W07 storage-Field dynamic index), then hands them to the shared ramp.\n let n = i32(U.stopCount); // AX.W07 1a \u2014 i32()-cast the f32-packed count.\n if (n <= 1) { return F.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 // AX.W07 1b \u2014 read the palette from the storage Field (dynamic i0/i1 index);\n // AX.W11 \u2014 i32()-cast the f32-packed huePath enum (parity with the count casts).\n return samplePaletteRamp(F.palette[i0].xyz, F.palette[i1].xyz, f, i32(U.huePath));\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 = i32(U.nucleiCount); // AX.W07 1a \u2014 i32()-cast the f32-packed count.\n for (var i = 0; i < 6; i = i + 1) {\n if (i >= n) { break; }\n // AX.W07 1b \u2014 read the nuclei from the storage Field (dynamic i index).\n let pos = F.nucleiPos[i].xy;\n let rad = max(F.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 * F.nucleiPos[i].w;\n accumVal = accumVal + wi * F.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\n// 4 comma \u2014 van-Gogh atomic dab: ASYMMETRIC taper, fat near one end and\n// drawn to a fine point at the other (the divisionist crescent\n// mark). The width peak sits off-centre (~0.32) so the two ends\n// taper at DIFFERENT rates \u2014 a loaded round head + a dragged tail.\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 } else if (type == 4) {\n // comma / crescent \u2014 width peaks at ~0.32 then drags to a fine tail. The two\n // sides of the peak fall at different exponents (a fat round head, a long thin\n // tail), so the mark reads as a directional van-Gogh comma, not a symmetric oval.\n float peak = 0.32;\n float head = smoothstep(0.0, peak, t); // rounded loaded head\n float tail = pow(clamp((1.0 - t) / (1.0 - peak), 0.0, 1.0), 1.6); // dragged thin tail\n return clamp(min(head, tail) * 1.04, 0.0, 1.0);\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// The painterly STROKE mediums (those that deposit via paintStrokeLayers/paintOver):\n// oil (3), van-Gogh (5), oil-pastel (6). For these the stroke OVER-composite runs in\n// OKLab so overlapping complementaries mix through a chromatic path, not the linear-\n// RGB grey mud; the smooth/atmospheric pole never reaches paintOver so it pays nothing.\nbool isPainterlyStroke() {\n return uMedium == 3 || uMedium == 5 || uMedium == 6;\n}\n\n// OKLab OVER-composite: lerp L,a,b of the over-color toward the under-color (the\n// Ottosson matrices spliced from procedural-color.glsl.ts \u2014 no new color math). Two\n// complementary-hued strokes overlapping transition through OKLab, where the midpoint\n// holds chroma, instead of the linear mix() that desaturates toward grey (slice 8 F2).\nvec3 paintOverOklab(vec3 under, vec3 over, float alpha) {\n vec3 labU = linOklab(under);\n vec3 labO = linOklab(over);\n return max(oklabToLinearSrgb(mix(labU, labO, alpha)), vec3(0.0));\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().\n//\n// AX.W13 \u2014 the internal streak now perturbs HUE + CHROMA in OKLCh (not value-only),\n// so a single stroke carries a small hue gradient (broken color at the ATOM level \u2014\n// adjacent strokes shimmer like real impasto, not flat stamped swatches; slice 8 F5);\n// and the OVER-composite runs in OKLab on the painterly stroke mediums so overlapping\n// complementaries mix as pigment, not the linear-mix grey (slice 8 F2).\n// impastoFloor \u2014 the per-stroke height-crown floor: oil's 0.4+0.6*edgeN reads\n// floor=0.4 (the crown tapers to 40% at the stroke edge); the van-Gogh\n// profile passes floor=1.0 so each atomic dab catches a FULL-height\n// glint (scope item 1d). The crown magnitude is floor + (1-floor)*edgeN.\nvoid paintOver(inout vec3 col, inout float height, StrokeHit s,\n float streakFreq, float streakAmp,\n float impastoAmp, float hardness, float streakSeed,\n float impastoFloor) {\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\n // \u2500\u2500 WITHIN-stroke OKLCh broken color (AX.W13, slice 8 F5). The streak fBm perturbs\n // the stroke's HUE + CHROMA along alongT/crossN, seeded per-stroke, so a single\n // stroke reads a small hue gradient (the atom-level broken-color shimmer). Value is\n // carried by the same streak so the loaded-brush light/dark survives. Gated to the\n // painterly stroke mediums + uBrokenColor (the smooth pole keeps the cheap value-only\n // streak). A second decorrelated fBm drives the hue axis so hue \u2260 a scaled value.\n float hueStreak = fbm(vec2(s.alongT * streakFreq * 0.45 + streakSeed * 5.3,\n s.crossN * 1.9 + streakSeed * 1.7)) - 0.5;\n if (isPainterlyStroke() && uBrokenColor > 0.001) {\n vec3 lch = oklabToOklch(linOklab(c));\n // \u00B1~10\u00B0 within-stroke hue swing + \u00B1~9% chroma, scaled by uBrokenColor. Smaller\n // than the per-stroke cell jitter (brokenColorJitter) \u2014 this is the intra-stroke\n // gradient layered on top of the per-cell pigment patch.\n lch.z += hueStreak * (20.0 * PI / 180.0) * uBrokenColor;\n lch.y = max(lch.y * (1.0 + (streak) * 0.18 * uBrokenColor), 0.0);\n c = max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n }\n // Value streak (the loaded-brush light/dark across the bristles) \u2014 kept for every\n // medium that reaches paintOver (the smooth pole never does).\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 // OKLab OVER-composite on the painterly stroke mediums (overlap mixes as pigment,\n // not grey); the smooth pole never reaches paintOver, so it keeps the cheap path.\n col = isPainterlyStroke() ? paintOverOklab(col, c, alpha) : mix(col, c, alpha);\n\n // \u2500\u2500 Accumulate paint HEIGHT (AW.W4.2). Thickness peaks at the stroke spine and\n // falls toward the impastoFloor at the edge (edgeN: 0=edge, 1=spine), perturbed by\n // the bristle streak so ridges/grooves form. The relight reads the gradient of this\n // field. The van-Gogh profile's floor=1.0 gives every atomic dab a full-height crown.\n float ridge = 0.7 + 0.3 * (streak + 0.5);\n float crown = impastoFloor + (1.0 - impastoFloor) * s.edgeN;\n height += s.coverage * impastoAmp * crown * 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.\n// energyGrade \u2014 the SBR energy-grade magnitude (0 = none, the oil/oil-pastel\n// baseline; 1 = the full van-Gogh Starry-Night cascade \u2014 long\n// confident strokes in the lights/coherent zones, short dabs in\n// the darks/flat zones). The van-Gogh profile carries 1.0 (the\n// energy grade is a PROFILE field, not a buried uMedium==5 branch).\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 float energyGrade) {\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 // AX.W13 \u2014 the SBR energy grade is a PROFILE field (energyGrade), not a buried\n // uMedium==5 branch. The van-Gogh profile passes energyGrade=1.0 (the full\n // Starry-Night cascade); oil/oil-pastel pass 0.0 (uniform length). The grade\n // modulates stroke length by local luminance (bright \u2192 long confident strokes,\n // dark \u2192 short dabs, the Kolmogorov/Batchelor congruence) AND by coherence\n // (coherent zones \u2192 long, flat \u2192 stubby), lerped in by energyGrade so a profile\n // can dial the cascade from off (0) to full (1).\n float energy = 1.0;\n if (energyGrade > 0.001) {\n float luma = clamp(dot(sampleBase(center, t), W_LUMA), 0.0, 1.0);\n float graded = mix(0.5, 1.5, luma) * mix(0.6, 1.0, coh);\n energy = mix(1.0, graded, energyGrade);\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 (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
|
|
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 runs in perceptual space. AX.W11 \u2014 the ramp\n// itself (the smoothstep ease + the OKLab-rectangular-vs-OKLCh-hue-arc dispatch on\n// uHuePath) is the shared samplePaletteRamp from procedural-color.glsl, the SAME\n// source the WebGPU twin splices; samplePalette here only selects the bracketing\n// stop pair + the raw inter-stop t and hands them to the shared ramp.\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 return samplePaletteRamp(uPalette[i0], uPalette[i1], t, uHuePath);\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 Medium id constants (mirror uniformBridge.ts MEDIUM_ID \u2014 the uMedium ladder) \u2500\u2500\n// The StrokeProfile selector (AX.W12 + AX.W13) dispatches on the medium id: oil,\n// van-Gogh, and oil-pastel each AUTHOR a first-class profile in profileFor.\n#define MEDIUM_OIL 3\n#define MEDIUM_CRAYON 4\n#define MEDIUM_VANGOGH 5\n#define MEDIUM_OILPASTEL 6\n\n// \u2500\u2500 Crayon \u2014 DRY wax pigment on paper tooth (first-class medium, uMedium==4) \u2500\u2500\n// Crayon is not strokes. It's dry pigment crumbs dragged across paper tooth \u2014 the\n// DRY tooth-multiply model: anisotropic tooth noise multiplied into the base, a hard\n// SCUMBLE that lets the lower color show through the broken upper layer, and stable\n// OKLCh broken-color pigment patches. NO sheen, NO burnish film \u2014 that waxy gloss is\n// the OIL-PASTEL deposition's signature (mediumOilPastel), distinct from dry crayon\n// (AX.W13 slice 8 F1 \u2014 the split). Crayon shares the SUBSTRATE (the structure-tensor\n// orientation, the tooth noise, OKLCh brokenColorJitter), not the dispatch body.\nvec3 mediumCrayon(vec3 col, vec2 p, float t) {\n // Orient the deposition along the structure-tensor edge-tangent (the bridge forces\n // tensor for crayon, so read the tensor field directly).\n vec2 flow = structureTensorField(p, t, flowField(p, t)).xy;\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 dry 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 tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n\n // \u2500\u2500 DRY TOOTH-MULTIPLY \u2014 the crayon multiplies the tooth relief into the base\n // color (dark in the pits, bright on the ridges). The pressure (uStrokeAmount) sets\n // how hard the multiply bites. This is the DRY model \u2014 no pigment build-up, no sheen.\n float pressure = clamp(uStrokeAmount, 0.0, 1.0);\n float bite = mix(0.18, 0.46, pressure); // multiply strength\n vec3 result = col * (1.0 - bite * (0.5 - tooth));\n\n // \u2500\u2500 HARD SCUMBLE \u2014 a broken upper layer that lets the lower color show through the\n // gaps. A coarse mask gates a HARD (high-contrast) dragged pass so the dry crayon\n // reads as broken pigment crumbs, not a smooth wash. Drag follows the tensor flow.\n float scumbleMask = smoothstep(0.42, 0.78, vnoise(vec2(pr.x * scale * 0.55, pr.y * scale * 0.22) + 7.0));\n float scumbleCoverage = scumbleMask * pressure * 0.7; // hard broken upper layer\n vec3 paper = col * mix(0.80, 0.94, pressure); // the paper showing through\n result = mix(paper, result, scumbleCoverage);\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 // Crayon is saturation-amplified (the wax chroma punch \u2014 but DRY, no sheen).\n result = saturate3(result, 1.08);\n\n return result;\n}\n\n// \u2500\u2500 StrokeProfile \u2014 the oil-stroke parameter vector (AX.W12, slice 8 F6) \u2500\u2500\u2500\u2500\u2500\u2500\n// The SBR \"stroke = parameter vector\" canon (facet 11): the per-mode if-ladder knobs\n// become struct FIELDS \u2014 logic-as-DATA, not an imperative branch. profileFor(medium,\n// mode) populates the profile for a (medium, mode) pair; paintStrokeLayers(profile) runs\n// the four-layer bestOil/paintOver cascade off it. A new medium (the W13 van-Gogh /\n// oil-pastel profiles) AUTHORS a profile entry \u2014 it never edits a monolith.\nstruct StrokeProfile {\n int shapeType; // 0 tapered, 1 load-drag, 2 dab, 3 even, 4 comma/crescent\n float bristleAmp; // edge raggedness 0..0.5\n float streakFreq; // internal-streak spatial frequency\n float streakAmp; // internal-streak amplitude\n float impastoAmp; // paint-thickness contribution (the relight reads the height)\n float hardness; // edge-compositing crispness 0..1\n float toothScale; // canvas-tooth (linen weave) spatial frequency\n float toothAmp; // canvas-tooth amplitude\n float pigmentSat; // pigment saturation boost (OKLCh chroma scale)\n float densityBig; // layer-1 (big gestural) placement density gate\n float densityMed; // layer-2 (medium body) placement density gate\n float densitySml; // layer-3 (small dabs) placement density gate\n float energyGrade; // AX.W13 \u2014 SBR energy-grade magnitude (0 off; 1 the van-Gogh\n // Starry-Night length cascade). The energy grade is a PROFILE\n // field, NOT a buried uMedium==5 bestOil branch (slice 8 F0).\n float impastoFloor;// AX.W13 \u2014 the per-stroke height-crown floor (0.4 oil falloff;\n // 1.0 van-Gogh FULL-height crown so each dab catches its glint).\n float densityFill; // AX.W13 \u2014 layer-4 fill-dab density. Oil/oil-pastel fill the\n // bald spots (0.95, full coverage); van-Gogh sets it LOW so the\n // sparse atomic dabs keep their visible inter-stroke ground gaps\n // (the atomicity read \u2014 a 0.95 fill would close every gap to a smear).\n float groundFloor; // AX.W13 \u2014 the bare-ground darken floor. The gaps between strokes\n // (low accumulated height) multiply toward this floor, so van-Gogh's\n // sparse atomic dabs read over a visibly darker underpainting (the\n // Starry-Night visible ground). Oil/oil-pastel keep 1.0 (no darken).\n};\n\n// The (medium, mode) -> StrokeProfile selector. The if-ladder's knobs are the profile's\n// fields; a new medium adds a CASE here (never an edit to a dispatch body). mode is the\n// uStrokeMode oil-stroke sub-mode (0 oil, 1 palette-knife, 3 brushwork). crayon\n// (uMedium==4) is a PEER tooth-multiply medium that never reaches the stroke cascade.\n// The painterly STROKE mediums are oil (MEDIUM_OIL), van-Gogh (MEDIUM_VANGOGH) and\n// oil-pastel (MEDIUM_OILPASTEL) \u2014 each AUTHORS its own profile entry below.\nStrokeProfile profileFor(int medium, int mode) {\n // The oil baseline (mode 0) \u2014 balanced modern-abstract/palette-knife hybrid.\n StrokeProfile prof = StrokeProfile(\n 0, // shapeType \u2014 tapered\n 0.25, // bristleAmp\n 9.0, // streakFreq\n 0.09, // streakAmp\n 0.9, // impastoAmp\n 0.80, // hardness\n 240.0, // toothScale\n 0.09, // toothAmp\n 1.03, // pigmentSat\n 0.65, // densityBig\n 0.78, // densityMed\n 0.90, // densitySml\n 0.0, // energyGrade \u2014 oil is uniform-length (no Starry-Night cascade)\n 0.4, // impastoFloor \u2014 oil's 0.4+0.6\u00B7edgeN crown falloff\n 0.95, // densityFill \u2014 oil fills the bald spots (full coverage, no bare canvas)\n 1.0 // groundFloor \u2014 oil keeps the full base (no ground darken; fills everywhere)\n );\n\n // \u2500\u2500 Van-Gogh \u2014 first-class atomic comma/crescent dabs (slice 8 F0). NOT a length\n // modulation of oil: sparse high-contrast SEPARABLE marks with their own asymmetric\n // shape, the full Starry-Night energy cascade (energyGrade=1), and full-height\n // impasto crowns so each dab catches its own glint. The mode sub-modes don't apply\n // (van-Gogh has its own grammar), so this returns early.\n if (medium == MEDIUM_VANGOGH) {\n prof.shapeType = 4; // comma / crescent \u2014 the divisionist atomic dab\n prof.bristleAmp = 0.22; // a touch of raggedness on the loaded head\n prof.streakFreq = 16.0; // tight within-stroke streaks (the loaded comma)\n prof.streakAmp = 0.16;\n prof.impastoAmp = 1.5; // heavy paint per atomic dab\n prof.hardness = 0.92; // crisp separable edges (visible inter-stroke gaps)\n prof.toothScale = 220.0;\n prof.toothAmp = 0.06;\n prof.pigmentSat = 1.12; // the van-Gogh chroma punch\n // SPARSE placement \u2014 visible inter-stroke canvas gaps (the atomicity read). Lower\n // density gates than oil so each mark stays separable, not a coverage smear.\n prof.densityBig = 0.42;\n prof.densityMed = 0.52;\n prof.densitySml = 0.60;\n prof.energyGrade = 1.0; // the full Starry-Night length cascade (the profile field)\n prof.impastoFloor = 1.0; // FULL-height crowns \u2014 every atomic dab catches its glint\n prof.densityFill = 0.09; // SPARSE fill \u2014 the bald-spot layer stays mostly open so\n // the atomic dabs read as separable marks over a bare\n // ground, not a fill-closed coverage smear (the gap read).\n prof.groundFloor = 0.38; // darken the bare-ground gaps to the visible Starry-Night\n // underpainting so the bright impasto dabs read separable.\n return prof;\n }\n\n // \u2500\u2500 Oil-pastel \u2014 broad smeared directional strokes deposited via the brush engine\n // (slice 8 F1). Creamy soft edges (low hardness), heavy build-up, and a chroma punch.\n // Distinct from the dry-crayon tooth-multiply (mediumCrayon): oil-pastel LAYS strokes.\n if (medium == MEDIUM_OILPASTEL) {\n prof.shapeType = 2; // dab / blob \u2014 broad short smeared marks\n prof.bristleAmp = 0.10; // smooth creamy edges (waxy pastel, not bristle)\n prof.streakFreq = 6.0; // soft low-frequency internal smear\n prof.streakAmp = 0.07;\n prof.impastoAmp = 0.7; // build-up, but flatter than oil impasto\n prof.hardness = 0.42; // CREAMY \u2014 soft compositing, strokes blend on overlap\n prof.toothScale = 280.0;\n prof.toothAmp = 0.12; // the pastel tooth reads stronger\n prof.pigmentSat = 1.16; // the waxy chroma punch\n prof.densityBig = 0.80; // dense \u2014 broad coverage, pigment-on-pigment build-up\n prof.densityMed = 0.90;\n prof.densitySml = 0.95;\n prof.energyGrade = 0.0; // uniform (no Starry-Night grade)\n prof.impastoFloor = 0.55; // a softer crown than van-Gogh's full-height glint\n return prof;\n }\n\n // \u2500\u2500 Oil sub-modes (mode dispatch on the oil medium).\n if (mode == 1) { // palette knife \u2014 razor edges, heavy impasto\n prof.shapeType = 3; // flat, even\n prof.bristleAmp = 0.12;\n prof.streakFreq = 4.0; prof.streakAmp = 0.05;\n prof.impastoAmp = 1.6;\n prof.hardness = 0.95;\n prof.toothAmp = 0.04;\n prof.densityBig = 0.80; prof.densityMed = 0.88; prof.densitySml = 0.70;\n } else if (mode == 3) { // thick brushwork \u2014 heavy bristle brush\n prof.shapeType = 0; // tapered\n prof.bristleAmp = 0.32;\n prof.streakFreq = 14.0; prof.streakAmp = 0.14;\n prof.impastoAmp = 1.2;\n prof.hardness = 0.85;\n prof.toothAmp = 0.07;\n }\n return prof;\n}\n\n// The single parameterized four-layer stroke cascade (AX.W12). The four hand-unrolled\n// bestOil/paintOver invocations collapse into ONE body driven by the profile + the\n// uniform-derived per-layer scale/anisotropy multipliers (which are mode-INVARIANT \u2014 the\n// per-layer offsets, seeds, and len/wid muls are fixed across modes, so they stay here as\n// the cascade's structure; only the profile's knobs differentiate the medium). The\n// mode passes through for the two mode-special-cased shapes (knife layer-3 dabs +\n// layer-4 fill shape), which are placement details of the cascade, not profile knobs.\nvoid paintStrokeLayers(inout vec3 col, inout float height, StrokeProfile prof,\n int mode, vec2 p, float t) {\n // Scales & multipliers from uniforms (mode-invariant cascade structure).\n float baseScale = max(uStrokeScale * 0.006, 0.008);\n // Three primary 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 // Layer 1 \u2014 big gestural strokes (sparse, shaping)\n StrokeHit hBig = bestOil(p, sBig, lenMulBig, widMulBig, jitterAmt * 0.55,\n prof.densityBig, prof.shapeType, prof.bristleAmp, flow, t, 1.3,\n prof.energyGrade);\n paintOver(col, height, hBig, prof.streakFreq * 0.7, prof.streakAmp,\n uImpasto * prof.impastoAmp * uStrokeAmount, prof.hardness, 1.3, prof.impastoFloor);\n\n // Layer 2 \u2014 medium body strokes\n StrokeHit hMed = bestOil(p + vec2(11.3, 3.7), sMed, lenMulMed, widMulMed,\n jitterAmt, prof.densityMed, prof.shapeType, prof.bristleAmp, flow, t, 2.7,\n prof.energyGrade);\n paintOver(col, height, hMed, prof.streakFreq, prof.streakAmp,\n uImpasto * prof.impastoAmp * uStrokeAmount, prof.hardness, 2.7, prof.impastoFloor);\n\n // Layer 3 \u2014 small dabs (more frequent, smaller)\n int smlShape = (mode == 1) ? 2 : prof.shapeType; // knife uses dabs for sparkle\n StrokeHit hSml = bestOil(p + vec2(-5.1, 8.4), sSml, lenMulSml, widMulSml,\n jitterAmt * 1.3, prof.densitySml, smlShape,\n prof.bristleAmp * 0.85, flow, t, 4.1, prof.energyGrade);\n paintOver(col, height, hSml, prof.streakFreq * 1.4, prof.streakAmp * 0.8,\n uImpasto * prof.impastoAmp * 0.65 * uStrokeAmount, prof.hardness, 4.1, prof.impastoFloor);\n\n // Layer 4 \u2014 fill dabs (very dense, very small) \u2014 covers bald spots. The density is\n // PROFILE-driven (prof.densityFill): oil/oil-pastel fill to full coverage; van-Gogh\n // keeps it sparse so the bare-ground gaps between its atomic dabs survive.\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, prof.densityFill, fillShape,\n prof.bristleAmp * 0.6, flow, t, 8.9, prof.energyGrade);\n paintOver(col, height, hFill, prof.streakFreq * 1.8, prof.streakAmp * 0.6,\n uImpasto * prof.impastoAmp * 0.4 * uStrokeAmount, prof.hardness * 0.9, 8.9, prof.impastoFloor);\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, prof.densityMed * 0.7, prof.shapeType, prof.bristleAmp, flow2, t, 6.5,\n prof.energyGrade);\n paintOver(col, height, hX, prof.streakFreq, prof.streakAmp * 0.85,\n uImpasto * prof.impastoAmp * 0.55 * uStrokeAmount, prof.hardness, 6.5, prof.impastoFloor);\n }\n}\n\n// The shared stroke-medium SUBSTRATE finish (AX.W12 + AX.W13): paint the four-layer\n// cascade off the profile, then canvas-tooth + relight + saturation. The three painterly\n// STROKE mediums (oil / van-Gogh / oil-pastel) each AUTHOR their own profile and call\n// this \u2014 the substrate is shared, the PROFILE (not a dispatch-body fork) differentiates\n// the medium (slice 8 F6). mode passes through for the cascade's mode-special-cased shapes.\nvec3 paintStrokeMedium(vec3 col, vec2 p, float t, StrokeProfile prof, int mode) {\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 paintStrokeLayers(result, height, prof, mode, p, t);\n\n // Canvas tooth \u2014 linen weave\n float tooth1 = vnoise(p * prof.toothScale);\n float tooth2 = vnoise(p * prof.toothScale * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * prof.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 * prof.toothAmp * 0.5;\n result = relightImpasto(result, height, canvasBase);\n\n // AX.W13 \u2014 the visible ground. The bare-ground gaps (low accumulated stroke height)\n // multiply toward prof.groundFloor, so van-Gogh's sparse atomic dabs read as separable\n // marks over a darker underpainting (oil/oil-pastel keep groundFloor=1.0 \u2192 no-op). The\n // smoothstep keeps the impasto dabs (high height) at full value; only the open ground\n // between them darkens.\n result *= mix(prof.groundFloor, 1.0, smoothstep(0.0, 0.38, height));\n\n // Pigment saturation boost\n result = saturate3(result, prof.pigmentSat);\n\n return result;\n}\n\nvec3 mediumOil(vec3 col, vec2 p, float t) {\n // The oil medium is a thin body: fetch the oil profile (mode-dispatched), paint via\n // the shared stroke substrate. uStrokeMode: 0 oil (gestural), 1 knife, 3 brushwork.\n int mode = uStrokeMode;\n StrokeProfile prof = profileFor(MEDIUM_OIL, mode);\n return paintStrokeMedium(col, p, t, prof, mode);\n}\n\n// AX.W13 \u2014 the van-Gogh atomic-stroke medium is now a FIRST-CLASS body, NOT a\n// mediumOil passthrough (slice 8 F0). It composes its OWN vangogh StrokeProfile (the\n// comma/crescent shapeType, sparse high-contrast atomic placement with visible\n// inter-stroke gaps, the full Starry-Night energy-grade cascade as a PROFILE field,\n// and full-height impasto crowns) through the shared stroke substrate. The bridge forces\n// the ETF (tensor) orientation so the swirl-rows queue along the color field's tangent;\n// the within-stroke OKLCh broken color (paintOver) + the OKLab stroke OVER-composite\n// give pigment-true atom-level shimmer. No subject matter \u2014 the \"source image\" is the\n// generated nuclei field, so dabs trace its iso-bands (the Starry-Night swirl-rows).\nvec3 mediumVangogh(vec3 col, vec2 p, float t) {\n StrokeProfile prof = profileFor(MEDIUM_VANGOGH, 0);\n return paintStrokeMedium(col, p, t, prof, 0);\n}\n\n// AX.W13 \u2014 the oil-pastel medium (uMedium==6) is a DISTINCT stroke-deposition body,\n// split out of the dry-crayon tooth-multiply (mediumCrayon) (slice 8 F1). It DEPOSITS\n// broad smeared directional strokes via the same brush engine with a creamy soft\n// hardness, heavy pigment build-up where strokes overlap, and a chroma punch \u2014 the\n// stroke-deposition model that gives oil-pastel its depth and less-uniform read.\n// Shares the SUBSTRATE (the stroke cascade + tooth + relight) with oil/van-Gogh, NOT\n// the dispatch body of dry crayon. The bridge forces tensor orientation.\nvec3 mediumOilPastel(vec3 col, vec2 p, float t) {\n StrokeProfile prof = profileFor(MEDIUM_OILPASTEL, 0);\n return paintStrokeMedium(col, p, t, prof, 0);\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,7 +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, nucleiPrior, DEFAULT_ATOMS, type AuroraAtoms, type AuroraMotionAtom, type AuroraZones, type AuroraZoneArrangement, type AuroraMediumAtom, type AuroraInteractivityAtom, } from "./composables/atoms";
|
|
10
|
+
export { cssToOklch, deriveAurora, flattenPalette, hexToOklchStop, oklchStopToHex, oklchToLinear, paletteToCssGradient, } from "./composables/color";
|
|
11
|
+
export type { AuroraHarmony, DeriveAuroraOptions, DeriveEasing, } from "./composables/color";
|
|
11
12
|
export type { AuroraHuePath } from "./constants/presets";
|
|
@@ -12,9 +12,16 @@ import { type ConstellationField } from "./constellationField";
|
|
|
12
12
|
* neutral passes and receives the live field so it can pin itself to a real
|
|
13
13
|
* node. Zero deck-domain content lives in the component.
|
|
14
14
|
*
|
|
15
|
-
* Palette reads
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Palette reads the FULL `--constellation-*` legibility set off the canvas (the
|
|
16
|
+
* node/node-dim/line colors + the edge-alpha multipliers + the field-yields-to-
|
|
17
|
+
* type `--constellation-alpha` knob; neutral fallbacks), so a consumer override
|
|
18
|
+
* or a dark-mode flip re-tints AND re-weights the lattice (AX.W17).
|
|
19
|
+
*
|
|
20
|
+
* The focal node + click-to-warp (AX.W17) is a first-class engine concept: with
|
|
21
|
+
* `warpOnClick`, a click warps the focal node to the nearest drifting node via a
|
|
22
|
+
* critically-damped spring stepped INSIDE the substrate's single rAF (no
|
|
23
|
+
* `useSpring`, no second rAF). The consumer paints the focal mark at
|
|
24
|
+
* `field.warp.{x,y}` in its `drawOverlay`.
|
|
18
25
|
*/
|
|
19
26
|
type __VLS_Props = {
|
|
20
27
|
/** Node count. Default 64. */
|
|
@@ -30,16 +37,44 @@ type __VLS_Props = {
|
|
|
30
37
|
seed?: number | string;
|
|
31
38
|
/** Steer-toward-cursor + tap ripples. Default true; auto-off under reduced-motion. */
|
|
32
39
|
pointerReactive?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Click-to-warp (AX.W17). A click warps the focal node to the nearest
|
|
42
|
+
* drifting node and SPRINGS it there (a critically-damped path, chasing a
|
|
43
|
+
* LIVE drifting target). INDEPENDENT of `pointerReactive` — warp works on a
|
|
44
|
+
* static (non-ripple) lattice, and ripples work without warp. Default false;
|
|
45
|
+
* auto-off under reduced-motion (the click does NOT warp — the focal mark
|
|
46
|
+
* stays put; the stated PRM policy). The low-level `warpTo(...)` imperative
|
|
47
|
+
* method (via `defineExpose`) is the seam this prop sugars.
|
|
48
|
+
*/
|
|
49
|
+
warpOnClick?: boolean;
|
|
33
50
|
class?: HTMLAttributes["class"];
|
|
34
51
|
/**
|
|
35
52
|
* 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
|
|
37
|
-
*
|
|
53
|
+
* consumer paints its own focal mark pinned to a real node — read
|
|
54
|
+
* `field.warp.{x,y}` for the spring-eased focal position when `warpOnClick`
|
|
55
|
+
* is on. The branded content (a domain accent, a callout label) lives HERE,
|
|
56
|
+
* in the consumer.
|
|
38
57
|
*/
|
|
39
58
|
drawOverlay?: (ctx: CanvasRenderingContext2D, field: ConstellationField, now: number) => void;
|
|
40
59
|
};
|
|
41
60
|
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
61
|
+
/** The live field (the low-level imperative seam — for a custom overlay). */
|
|
42
62
|
field: ConstellationField;
|
|
63
|
+
/**
|
|
64
|
+
* Imperative warp (AX.W17). Two call shapes:
|
|
65
|
+
* `warpTo(localPoint)` — `{x, y}` already in canvas-local px
|
|
66
|
+
* (the lower primitive).
|
|
67
|
+
* `warpTo(clientX, clientY)` — client coords, mapped via the
|
|
68
|
+
* deck-scale `toLocal` (the sugar
|
|
69
|
+
* `warpOnClick` calls internally).
|
|
70
|
+
* The focal node re-points to the nearest drifting node + springs there.
|
|
71
|
+
* Returns the chosen node index, or -1 on a degenerate no-op. No-op under
|
|
72
|
+
* reduced-motion (the spring does not advance while the substrate is frozen).
|
|
73
|
+
*/
|
|
74
|
+
warpTo(a: {
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
} | number, b?: number): number;
|
|
43
78
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
44
79
|
declare const _default: typeof __VLS_export;
|
|
45
80
|
export default _default;
|