@mkbabb/glass-ui 3.5.1 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CardAction-D8nSE62a.js +163 -0
- package/dist/CollapsibleContent-D7UNsXQr.js +63 -0
- package/dist/{CommandShortcut-BiVyqipe.js → CommandShortcut-CY0aQTz5.js} +4 -4
- package/dist/{ContextMenuContent-De01_83g.js → ContextMenuContent-DGKZX1Du.js} +1 -1
- package/dist/{ContextMenuSubContent-DrWkXKQP.js → ContextMenuSubContent-BeDz7G6f.js} +4 -4
- package/dist/{DataTable-DyKc_26a.js → DataTable-gtPOLZvl.js} +14 -7
- package/dist/{DialogContent-IQ8_BRrC.js → DialogContent-DG1YMR-P.js} +24 -24
- package/dist/{DialogFooter-Er0wA3K6.js → DialogFooter-BCIaDP15.js} +2 -2
- package/dist/{DiscoGlyph-BaZ8OawK.js → DiscoGlyph-ZoBYpoxI.js} +1 -1
- package/dist/{GlyphFace-B_7vOmYn.js → GlyphFace-Ce_oe3tM.js} +1 -1
- package/dist/{HoverCardContent-ICfIMZX1.js → HoverCardContent-Bn0UBMY9.js} +1 -1
- package/dist/{HoverPopover-DTSYkJtw.js → HoverPopover-DXpOEw1P.js} +1 -1
- package/dist/{IconTooltip-BTyYn4mr.js → IconTooltip-Cu86JR4e.js} +1 -1
- package/dist/{Input-CU9CNKmo.js → Input-DWQQIOC4.js} +4 -1
- package/dist/{Label-CZk-3nTc.js → Label-DBc7bKzZ.js} +1 -1
- package/dist/{MetricBadge-BlrdbOGN.js → MetricBadge-D-bRaQUD.js} +1 -1
- package/dist/{Notification-DP_ApJLo.js → Notification-B5WHP3_1.js} +5 -4
- package/dist/{NumberFieldContent-jO-zGOHf.js → NumberFieldContent-CLNVR1iY.js} +5 -5
- package/dist/{PopoverContent-B8WtJECb.js → PopoverContent-F1bWnYoh.js} +2 -2
- package/dist/{Progress-DjM86vfb.js → Progress-c4vo4ALi.js} +6 -6
- package/dist/{ScrollingText-P9o_DuMn.js → ScrollingText-BEp377U5.js} +2 -2
- package/dist/{SelectScrollDownButton-BGn3rjs9.js → SelectScrollDownButton-DAo5NmY_.js} +8 -7
- package/dist/{Separator-C2XtAXRp.js → Separator-DZyqsCum.js} +1 -1
- package/dist/{Switch-x8n6husW.js → Switch-2rsAtDfA.js} +8 -2
- package/dist/TabsList-COqIdW7i.js +101 -0
- package/dist/{Toaster-DdhMKfus.js → Toaster-BMaeziJJ.js} +54 -60
- package/dist/{ToggleGroupItem-gyXj998A.js → ToggleGroupItem-C-AIkBUw.js} +13 -5
- package/dist/{TooltipProvider-D-JrSqDu.js → TooltipProvider-CD2IXfRv.js} +1 -1
- package/dist/{UnderlineTabs-Cq_AD03t.js → UnderlineTabs-D_c1iYo-.js} +1 -1
- package/dist/animated-digit.js +2 -2
- package/dist/api/index.d.ts +2 -1
- package/dist/api.js +1 -1
- package/dist/aurora.js +845 -204
- package/dist/{badge-Cl6JZ1B7.js → badge-DHQrc_GQ.js} +13 -9
- package/dist/badge.js +1 -1
- package/dist/{button-i8Ek5vvg.js → button-CAywcrhO.js} +8 -5
- package/dist/button.js +1 -1
- package/dist/card.js +2 -2
- package/dist/carousel.js +5 -5
- package/dist/{check-CdkxGxXJ.js → check-BBKKKSTf.js} +1 -1
- package/dist/{chevron-down-pBY8sYfV.js → chevron-down-BSkTP2aL.js} +1 -1
- package/dist/{chevron-right-BjeKC22V.js → chevron-right-sDM0Yvrf.js} +1 -1
- package/dist/{chevron-up-DBeNHUm1.js → chevron-up-CCOD_FTz.js} +1 -1
- package/dist/collapsible.js +1 -1
- package/dist/color-DjJCAQ6f.js +76 -0
- package/dist/color.js +2 -2
- package/dist/command.js +1 -1
- package/dist/compile-iM4IUB66.js +158 -0
- package/dist/components/custom/aurora/Aurora.vue.d.ts +1 -0
- package/dist/components/custom/aurora/composables/atoms.d.ts +60 -0
- package/dist/components/custom/aurora/composables/color.d.ts +36 -9
- package/dist/components/custom/aurora/composables/cursorModel.d.ts +35 -2
- package/dist/components/custom/aurora/composables/glSetup.d.ts +1 -1
- package/dist/components/custom/aurora/composables/gpuRuntime.d.ts +16 -0
- package/dist/components/custom/aurora/composables/runtime.d.ts +18 -0
- package/dist/components/custom/aurora/composables/uniformBridge.d.ts +29 -0
- package/dist/components/custom/aurora/composables/useAurora.d.ts +6 -0
- package/dist/components/custom/aurora/composables/useCursorInteraction.d.ts +8 -0
- package/dist/components/custom/aurora/constants/presets.d.ts +64 -2
- package/dist/components/custom/aurora/constants/renderMode.d.ts +10 -0
- package/dist/components/custom/aurora/constants/shaders/aurora.wgsl.d.ts +7 -0
- package/dist/components/custom/aurora/constants/shaders/brush.glsl.d.ts +1 -1
- package/dist/components/custom/aurora/constants/shaders/composition.glsl.d.ts +1 -1
- package/dist/components/custom/aurora/constants/shaders/flow.glsl.d.ts +1 -1
- package/dist/components/custom/aurora/constants/shaders/mediums.glsl.d.ts +2 -2
- package/dist/components/custom/aurora/constants/shaders/painterly.wgsl.d.ts +7 -0
- package/dist/components/custom/aurora/constants/shaders/wake.wgsl.d.ts +4 -0
- package/dist/components/custom/aurora/index.d.ts +5 -3
- package/dist/components/custom/constellation/Constellation.vue.d.ts +45 -0
- package/dist/components/custom/constellation/constellationField.d.ts +85 -0
- package/dist/components/custom/constellation/index.d.ts +3 -0
- package/dist/components/custom/dock/GlassDock.vue.d.ts +34 -3
- package/dist/components/custom/fourier-field/FourierField.vue.d.ts +38 -0
- package/dist/components/custom/fourier-field/index.d.ts +2 -0
- package/dist/components/custom/fourier-field/math.d.ts +57 -0
- package/dist/components/custom/goo-blob/GooBlob.vue.d.ts +2 -0
- package/dist/components/custom/goo-blob/composables/useBlobMood.d.ts +14 -2
- package/dist/components/custom/goo-blob/composables/useBlobPointer.d.ts +45 -4
- package/dist/components/custom/goo-blob/composables/useMetaballRenderer.d.ts +1 -1
- package/dist/components/custom/goo-blob/index.d.ts +1 -1
- package/dist/components/custom/goo-blob/shaders/sdf-body.glsl.d.ts +1 -1
- package/dist/components/custom/goo-blob/shaders/watercolor-edges.glsl.d.ts +2 -2
- package/dist/components/custom/goo-blob/types.d.ts +40 -0
- package/dist/components/custom/metric-cell/MetricCell.vue.d.ts +1 -1
- package/dist/components/custom/metric-stack/MetricStack.vue.d.ts +1 -1
- package/dist/components/ui/_shared/ModalOverlay.vue.d.ts +1 -1
- package/dist/components/ui/alert/index.d.ts +1 -1
- package/dist/components/ui/card/Card.vue.d.ts +10 -3
- package/dist/components/ui/card/CardAction.vue.d.ts +24 -0
- package/dist/components/ui/card/index.d.ts +1 -0
- package/dist/components/ui/collapsible/CollapsibleTrigger.vue.d.ts +5 -1
- package/dist/components/ui/tabs/TabsList.vue.d.ts +13 -3
- package/dist/components/ui/toast/Toast.vue.d.ts +5 -5
- package/dist/components/ui/toast/use-toast.d.ts +1 -1
- package/dist/composables/color/index.d.ts +50 -0
- package/dist/composables/glass/canvas2d/index.d.ts +2 -0
- package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +48 -0
- package/dist/composables/glass/createGPUCanvas.d.ts +40 -0
- package/dist/composables/glass/index.d.ts +2 -0
- package/dist/composables/glass/webgl/createCanvasLifecycle.d.ts +45 -0
- package/dist/composables/glass/webgl/shaders/procedural-color.glsl.d.ts +3 -0
- package/dist/composables/glass/webgl/useWebGLCanvas.d.ts +4 -4
- package/dist/configurator.js +1 -1
- package/dist/confirm-dialog.js +3 -3
- package/dist/constellation.d.ts +1 -0
- package/dist/constellation.js +182 -0
- package/dist/context-menu.js +2 -2
- package/dist/controls.js +2 -2
- package/dist/dark.js +1 -1
- package/dist/data-table.js +1 -1
- package/dist/dialog.js +2 -2
- package/dist/disco-glyph.js +1 -1
- package/dist/dock.js +129 -110
- package/dist/{dockContext-spUj_-E5.js → dockContext-DLgrVWsU.js} +1 -1
- package/dist/dom.js +5 -5
- package/dist/{dropdown-menu-EFjl5iKo.js → dropdown-menu-BWpwutt0.js} +5 -5
- package/dist/dropdown-menu.js +1 -1
- package/dist/expandable-container.js +2 -2
- package/dist/forms.js +10 -7
- package/dist/fourier-field.d.ts +1 -0
- package/dist/fourier-field.js +166 -0
- package/dist/glass-carousel.js +1 -1
- package/dist/glass-panel.js +2 -2
- package/dist/glass-ui.css +1 -1
- package/dist/glass-ui.js +207 -191
- package/dist/glyph-face.js +2 -2
- package/dist/goo-blob.js +336 -234
- package/dist/header-ribbon.js +1 -1
- package/dist/hover-card.js +1 -1
- package/dist/hover-popover.js +1 -1
- package/dist/icon-tooltip.js +1 -1
- package/dist/instrument-chassis.js +1 -1
- package/dist/instrument-rail.js +1 -1
- package/dist/keyboard.js +1 -1
- package/dist/{keys-BFoma9vm.js → keys-Dyf_uTs5.js} +1 -1
- package/dist/label.js +1 -1
- package/dist/labeled-field.js +6 -6
- package/dist/metric-badge.js +1 -1
- package/dist/metric-stack.js +1 -1
- package/dist/{minimize-2-BP27-qBY.js → minimize-2-lcXOuc6V.js} +1 -1
- package/dist/motion-core.js +76 -106
- package/dist/motion.js +3 -3
- package/dist/notification.js +1 -1
- package/dist/number-field.js +1 -1
- package/dist/paper-backdrop.js +1 -1
- package/dist/popover.js +1 -1
- package/dist/progress.js +1 -1
- package/dist/pulse.js +1 -1
- package/dist/reactive.js +2 -2
- package/dist/responsive-tabs.js +2 -2
- package/dist/scrolling-text.js +1 -1
- package/dist/{search-DBG8qaRs.js → search-nbkvr2dC.js} +1 -1
- package/dist/search.js +8 -8
- package/dist/select.js +3 -3
- package/dist/separator.js +1 -1
- package/dist/{sheet-CQYYrkr9.js → sheet-Cv1uO-Gq.js} +4 -4
- package/dist/sheet.js +1 -1
- package/dist/{slider-CgNzRSh6.js → slider-BA9wIs5e.js} +5 -4
- package/dist/slider.js +1 -1
- package/dist/sortable-list.js +3 -3
- package/dist/styles/components.css +1 -1
- package/dist/styles/dock.css +185 -1
- package/dist/styles/glass-refract.css +60 -0
- package/dist/styles/glass-specular-track.css +43 -130
- package/dist/styles/glass.css +243 -27
- package/dist/styles/index.css +1 -0
- package/dist/styles/theme.css +55 -0
- package/dist/styles/tokens.css +33 -0
- package/dist/styles/typography.css +3 -3
- package/dist/styles/utilities.css +17 -13
- package/dist/supportsCssTimeline-C5oe0lnD.js +12 -0
- package/dist/switch.js +1 -1
- package/dist/tabs.js +4 -4
- package/dist/timeline.js +2 -2
- package/dist/toast.js +1 -1
- package/dist/toggle-group.js +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/typewriter.js +1 -1
- package/dist/{useAnimatedNumber-BF6r64lA.js → useAnimatedNumber-C5I3wWd8.js} +1 -1
- package/dist/useCanvas2D-C31yfUGY.js +95 -0
- package/dist/{useConfiguratorState-DUtC1jxr.js → useConfiguratorState-B24TcgIn.js} +5 -5
- package/dist/useScrollProgress-CAH8Ofwt.js +35 -0
- package/dist/{useTouchGate-CS5Csc2h.js → useTouchGate-YQRwyUGU.js} +1 -1
- package/dist/{vReveal-u2wSG2El.js → vReveal-BE2TsPoR.js} +6 -17
- package/dist/watercolor-dot.js +2 -2
- package/dist/{x-C4pz9nbD.js → x-Dfsegz9Y.js} +1 -1
- package/package.json +49 -1
- package/src/styles/dock.css +185 -1
- package/src/styles/glass-refract.css +60 -0
- package/src/styles/glass-specular-track.css +43 -130
- package/src/styles/glass.css +243 -27
- package/src/styles/index.css +1 -0
- package/src/styles/theme.css +55 -0
- package/src/styles/tokens.css +33 -0
- package/src/styles/typography.css +3 -3
- package/src/styles/utilities.css +17 -13
- package/dist/CardFooter-C390imy7.js +0 -129
- package/dist/CollapsibleContent-cauTbZEM.js +0 -52
- package/dist/TabsIndicator-DA0x9gPr.js +0 -97
- package/dist/color-rkK4RMx2.js +0 -33
- package/dist/compile-DVgAxagk.js +0 -106
- /package/dist/{InstrumentChassis-DaHIZCqy.js → InstrumentChassis-BLJBl1BW.js} +0 -0
- /package/dist/{InstrumentRail-B0qqLFN0.js → InstrumentRail-BElFwKyt.js} +0 -0
- /package/dist/{ModalOverlay-B_CBtqcE.js → ModalOverlay-D1VrWyoi.js} +0 -0
- /package/dist/{PaperBackdrop-D_YZW47j.js → PaperBackdrop-DCGoGatv.js} +0 -0
- /package/dist/{SelectGroup-CMdoCjRE.js → SelectGroup-BcaFjvP4.js} +0 -0
- /package/dist/{SelectSeparator-CaJnPF3_.js → SelectSeparator-COFMJGng.js} +0 -0
- /package/dist/{_plugin-vue_export-helper-C1je1s0u.js → _plugin-vue_export-helper-gLzyemvl.js} +0 -0
- /package/dist/{constants-DsCdlK9I.js → constants-CS75AWHG.js} +0 -0
- /package/dist/{createContext-DBMGRlx4.js → createContext-CvNyBk4o.js} +0 -0
- /package/dist/{createLucideIcon-DuDoe_ra.js → createLucideIcon-B6uNAU9G.js} +0 -0
- /package/dist/{menuItemVariants-C2QlXqT3.js → menuItemVariants-B4eFR5Ix.js} +0 -0
- /package/dist/{presets-1OhFpaIC.js → presets-BKVyQfbt.js} +0 -0
- /package/dist/{prng-Bz_1Tydc.js → prng-wHuHlNHE.js} +0 -0
- /package/dist/{useGlassRenderer-Dn3WpfG-.js → useGlassRenderer-HebgSEgN.js} +0 -0
- /package/dist/{useGlobalDark-CWiaCoEw.js → useGlobalDark-yhL4Wp-s.js} +0 -0
- /package/dist/{useIntersectionPause-DAdVPVp4.js → useIntersectionPause-Cc-H9ees.js} +0 -0
- /package/dist/{useInterval-CHVYFpXV.js → useInterval-D3USn0yc.js} +0 -0
- /package/dist/{useKeyboardShortcuts-BQfnAHHW.js → useKeyboardShortcuts-Be3w_Rwk.js} +0 -0
- /package/dist/{useResizeObserver-DX-STszm.js → useResizeObserver-CSyxGxIX.js} +0 -0
- /package/dist/{useSortable-DnyGXKKY.js → useSortable-C8ReQ50P.js} +0 -0
- /package/dist/{useSpringMount-CnizvZGm.js → useSpringMount-LRZqJTaF.js} +0 -0
- /package/dist/{useTextHighlight-Dmtofpk2.js → useTextHighlight-aMPdEpkq.js} +0 -0
- /package/dist/{useTimer-DGgoxTXL.js → useTimer-DEJonOZa.js} +0 -0
- /package/dist/{useUserInvalidAria-DmvZ_6Dx.js → useUserInvalidAria-CQKjEfwP.js} +0 -0
- /package/dist/{useViewTransition-CJnqg6wl.js → useViewTransition-DaN5Zrox.js} +0 -0
- /package/dist/{useViewportReady-Dcvf0c4p.js → useViewportReady-K96m-Mdv.js} +0 -0
package/dist/aurora.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { t as e } from "./_plugin-vue_export-helper-
|
|
2
|
-
import { a as t,
|
|
3
|
-
import { n as
|
|
4
|
-
import { a as
|
|
5
|
-
import { t as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { t as e } from "./_plugin-vue_export-helper-gLzyemvl.js";
|
|
2
|
+
import { a as t, c as n, d as r, f as i, i as a, l as o, n as s, o as c, r as l, s as u, t as d, u as f } from "./compile-iM4IUB66.js";
|
|
3
|
+
import { n as p, r as m, t as h } from "./presets-BKVyQfbt.js";
|
|
4
|
+
import { a as g, c as _, i as v, o as y, t as b } from "./color-DjJCAQ6f.js";
|
|
5
|
+
import { t as x } from "./useIntersectionPause-Cc-H9ees.js";
|
|
6
|
+
import { t as ee } from "./useScrollProgress-CAH8Ofwt.js";
|
|
7
|
+
import { computed as S, createElementBlock as C, createElementVNode as w, defineComponent as te, normalizeClass as ne, normalizeStyle as T, onBeforeUnmount as E, onMounted as D, openBlock as re, readonly as ie, ref as O, unref as ae, watch as k } from "vue";
|
|
8
|
+
import { rawOklabToOklch as oe, srgbToOKLab as se } from "@mkbabb/value.js";
|
|
8
9
|
//#region src/components/custom/aurora/constants/shaders/aurora.vert.ts
|
|
9
|
-
var ne = "#version 300 es\nin vec2 aPos;\nout vec2 vUv;\nvoid main() {\n vUv = aPos * 0.5 + 0.5;\n gl_Position = vec4(aPos, 0.0, 1.0);\n}", re = "// ── Palette LUT ───────────────────────────────────────────────────────────\nvec3 samplePalette(float id) {\n id = clamp(id, 0.0, 1.0);\n float scaled = id * float(uStopCount - 1);\n int i0 = int(floor(scaled));\n int i1 = min(i0 + 1, uStopCount - 1);\n float t = fract(scaled);\n t = smoothstep(0.0, 1.0, t);\n return mix(uPalette[i0], uPalette[i1], t);\n}\n\n// ── Nuclei field ──────────────────────────────────────────────────────────\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 — slow global paletteId breathe between adjacent stops. The\n // rate rides K_PAL (perceptible ~30–60s 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", ie = "// ── Flow field ────────────────────────────────────────────────────────────\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 — 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 — 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 — 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 — 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", ae = "// ACES approximation\nvec3 aces(vec3 x) {\n float a = 2.51;\n float b = 0.03;\n float c = 2.43;\n float d = 0.59;\n float e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);\n}\n", oe = "// ── Curved swept brushstroke primitive ────────────────────────────────────\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·K·along0·(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 — 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 — thin-fat-thin (classic brush)\n// 1 load-drag — fat start, tapers to a point\n// 2 dab — ellipse-like, round center\n// 3 even — 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 — endpoints\n// halfW — base half-width (world units)\n// bulge — signed perpendicular midpoint offset, in world units\n// shapeType — 0..3\n// bristleFreq — spatial frequency of edge raggedness\n// bristleAmp — 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed — uniqueness seed for internal streaks\n// colAtMid — 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 — 0..0.2 how much internal streaks darken/lighten\n// impastoAmp — 0..1 edge catch-light strength\n// hardness — 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 — 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 — 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 — 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", j = "// ── Medium overlays ───────────────────────────────────────────────────────\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 — 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 — 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 — 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", M = "// ── Crayon / oil-pastel — paper tooth × wax pigment (PEER medium) ─────────\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 — 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 — 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 — 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 — 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) — oil-stroke modes ONLY (crayon is a peer medium,\n // uMedium==4, dispatched at main()):\n // 0 oil — balanced modern-abstract/palette-knife hybrid\n // 1 knife — palette-knife impasto: razor edges, heavy bristle/shadow\n // 3 brushwork — 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 — no grid\n vec2 flow = flowField(p, t);\n\n vec3 result = col;\n\n // Layer 1 — 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 — 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 — 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 — fill dabs (very dense, very small) — 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 — 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}", N = "\n", P = `#version 300 es
|
|
10
|
+
var ce = "#version 300 es\nin vec2 aPos;\nout vec2 vUv;\nvoid main() {\n vUv = aPos * 0.5 + 0.5;\n gl_Position = vec4(aPos, 0.0, 1.0);\n}", le = "// ── Palette LUT (W5 — OKLCh/OKLab interpolation) ──────────────────────────────\n// The endpoints stay CPU-baked to LINEAR sRGB (the Aras precompute pattern); only\n// the BETWEEN-endpoint interpolation moves to perceptual space. OKLab-rectangular\n// for the default ramp (holds chroma, no grey midpoint); the OKLCh hue-arc only on\n// uHuePath increasing/decreasing (deliberate rainbow travel). The prior linear\n// mix() (the muddy-midtone source) is GONE.\nvec3 samplePalette(float id) {\n id = clamp(id, 0.0, 1.0);\n float scaled = id * float(uStopCount - 1);\n int i0 = int(floor(scaled));\n int i1 = min(i0 + 1, uStopCount - 1);\n float t = fract(scaled);\n t = smoothstep(0.0, 1.0, t);\n vec3 a = uPalette[i0];\n vec3 b = uPalette[i1];\n // increasing(2)/decreasing(3) request the OKLCh hue-arc (rainbow sweep); every\n // other method takes the OKLab-rectangular straight line (no hue detour).\n if (uHuePath == 2 || uHuePath == 3) {\n return mixPaletteOklchArc(a, b, t, uHuePath);\n }\n return mixPaletteOklab(a, b, t);\n}\n\n// ── Nuclei field ──────────────────────────────────────────────────────────\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 — slow global paletteId breathe between adjacent stops. The\n // rate rides K_PAL (perceptible ~30–60s 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", ue = "// ── Flow field ────────────────────────────────────────────────────────────\n// Structure-tensor prototype (body defined post-sampleBase in the mediums block).\n// Returns vec3(dir.x, dir.y, coherence) — 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 — 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 — 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 — 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 — 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 — 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 — 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 — 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", de = "// ACES approximation\nvec3 aces(vec3 x) {\n float a = 2.51;\n float b = 0.03;\n float c = 2.43;\n float d = 0.59;\n float e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);\n}\n", fe = "// ── Curved swept brushstroke primitive ────────────────────────────────────\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·K·along0·(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 — 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 — thin-fat-thin (classic brush)\n// 1 load-drag — fat start, tapers to a point\n// 2 dab — ellipse-like, round center\n// 3 even — 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 — endpoints\n// halfW — base half-width (world units)\n// bulge — signed perpendicular midpoint offset, in world units\n// shapeType — 0..3\n// bristleFreq — spatial frequency of edge raggedness\n// bristleAmp — 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed — uniqueness seed for internal streaks\n// colAtMid — pre-sampled color at midpoint\nStrokeHit curvedStroke(vec2 p, vec2 a, vec2 b, float halfW,\n float bulge, int shapeType,\n float bristleFreq, float bristleAmp,\n float streakSeed, vec3 colAtMid) {\n vec2 ab = b - a;\n float L = length(ab);\n if (L < 1e-5) return noHit();\n vec2 tang = ab / L;\n vec2 norm = vec2(-tang.y, tang.x);\n\n vec2 rel = p - a;\n float along0 = dot(rel, tang) / L; // straight projection 0..1 along AB\n float cross0 = dot(rel, norm); // signed cross\n\n // Spine sideways offset due to bulge (parabolic)\n float bend = 4.0 * bulge * along0 * (1.0 - along0);\n float cross1 = cross0 - bend;\n\n // One refinement step: as the spine bends, the closest-point along shifts.\n // Spine tangent differs from straight tangent; first-order correction:\n float dBend = 4.0 * bulge * (1.0 - 2.0 * along0); // d(bend)/d(along)\n float along1 = along0 + (cross1 * dBend) / (L * (1.0 + dBend * dBend / (L * L)));\n\n // Bristle-ragged edge: half-width gets chewed by 1D noise along the spine\n float edgeNoise = fbm(vec2(along1 * bristleFreq, streakSeed * 7.3)) - 0.5;\n float edgeNoise2 = fbm(vec2(along1 * bristleFreq * 2.3, streakSeed * 3.1 + 17.0)) - 0.5;\n float edgeMod = 1.0 - bristleAmp * (0.6 + 0.4 * edgeNoise) * (0.5 + edgeNoise2);\n\n // Width profile along the stroke\n float shape = strokeShape(along1, shapeType);\n float halfWNow = halfW * shape * edgeMod;\n\n // Inside-segment coverage\n float cov = 0.0;\n if (along1 >= 0.0 && along1 <= 1.0 && halfWNow > 1e-6) {\n float cn = abs(cross1) / halfWNow;\n // fwidth-derived AA: half-width from the field's screen-space gradient so the\n // edge holds ~1px regardless of zoom/DPR (mirrors metaball.frag.ts:252-255).\n float aaC = max(fwidth(cn), 1e-6);\n cov = 1.0 - smoothstep(1.0 - aaC, 1.0 + aaC, cn);\n }\n\n // End-cap blobs (rounded tips, not perpendicular cuts)\n float capA = 0.0, capB = 0.0;\n {\n vec2 capCenterA = a + norm * (bulge * 0.0); // at a\n vec2 capCenterB = a + tang * L + norm * bend * 0.0 + norm * 0.0; // at b; bend is 0 at endpoints\n capCenterB = b;\n float endShapeA = strokeShape(0.02, shapeType);\n float endShapeB = strokeShape(0.98, shapeType);\n float rA = halfW * endShapeA * (0.9 + 0.2 * fbm(vec2(streakSeed * 13.0)));\n float rB = halfW * endShapeB * (0.9 + 0.2 * fbm(vec2(streakSeed * 19.0 + 5.0)));\n float dA = length(p - capCenterA);\n float dB = length(p - capCenterB);\n // fwidth-derived AA on the clean Euclidean cap distances (mirrors metaball.frag.ts:252-255).\n float aaA = max(fwidth(dA), 1e-6);\n float aaB = max(fwidth(dB), 1e-6);\n capA = 1.0 - smoothstep(rA - aaA, rA + aaA, dA);\n capB = 1.0 - smoothstep(rB - aaB, rB + aaB, dB);\n // Only apply caps if we're BEYOND the segment; otherwise the segment wins.\n capA *= (along1 < 0.05) ? 1.0 : 0.0;\n capB *= (along1 > 0.95) ? 1.0 : 0.0;\n }\n\n float coverage = max(cov, max(capA, capB));\n if (coverage < 0.002) return noHit();\n\n // Edge distance (for impasto later): 0 at edge, 1 at spine\n float edgeDist = halfWNow > 1e-6 ? clamp(1.0 - abs(cross1) / halfWNow, 0.0, 1.0) : 0.0;\n\n return StrokeHit(\n coverage,\n colAtMid,\n clamp(along1, 0.0, 1.0),\n halfWNow > 1e-6 ? cross1 / halfWNow : 0.0,\n edgeDist\n );\n}\n\n// Composite stroke over col with internal streaking + ACCUMULATE paint HEIGHT.\n// streakAmp — 0..0.2 how much internal streaks darken/lighten\n// impastoAmp — 0..1 paint-thickness contribution (the height the relight reads)\n// hardness — 0..1 how crisp the compositing transition is (1 = razor, 0 = creamy)\n//\n// AW.W4.2 — the fixed-RGB edge rim (the phantom upper-left light) is RETIRED. Each\n// stroke now deposits PAINT HEIGHT into the height accumulator (coverage x per-stroke\n// thickness, perturbed by the bristle/streak fbm for ridges/grooves); mediumOil\n// derives a normal from the accumulated height gradient (dFdx/dFdy) and relights it\n// once with the movable uLightDir source (relightImpasto), in linear before aces().\nvoid paintOver(inout vec3 col, inout float height, StrokeHit s,\n float streakFreq, float streakAmp,\n float impastoAmp, float hardness, float streakSeed) {\n if (s.coverage < 0.002) return;\n float strokeOpacity = clamp(uStrokeAmount, 0.0, 1.0);\n if (strokeOpacity <= 0.001) return;\n vec3 c = s.color;\n\n // Internal streaks — fbm along spine, modulated by cross position. Gives\n // the loaded-brush look: some bristles carry more pigment than others.\n float streakA = fbm(vec2(s.alongT * streakFreq, s.crossN * 2.7 + streakSeed));\n float streakB = fbm(vec2(s.alongT * streakFreq * 0.6 + streakSeed * 3.7, s.crossN * 4.1));\n float streak = 0.6 * (streakA - 0.5) + 0.4 * (streakB - 0.5);\n c *= 1.0 + streak * streakAmp * 2.0;\n\n // Subtle value variance across width (hollow-center catch-light)\n float crossShade = smoothstep(0.0, 0.4, s.edgeN) * (1.0 - smoothstep(0.65, 1.0, s.edgeN));\n c *= 1.0 + crossShade * 0.05;\n\n float softLimit = mix(0.35, 0.98, hardness);\n float alpha = smoothstep(0.0, 1.0 - softLimit, s.coverage) * strokeOpacity;\n col = mix(col, c, alpha);\n\n // ── Accumulate paint HEIGHT (AW.W4.2). Thickness peaks at the stroke spine and\n // falls to zero at the edge (edgeN: 0=edge, 1=spine), perturbed by the bristle\n // streak so ridges/grooves form. The relight reads the gradient of this field.\n float ridge = 0.7 + 0.3 * (streak + 0.5);\n height += s.coverage * impastoAmp * (0.4 + 0.6 * s.edgeN) * ridge * alpha;\n}\n\n// AW.W4.2 — relight the accumulated paint height. Derive a normal from the height\n// gradient (dFdx/dFdy — 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 → high shininess (sharp glint); thin paint → broad soft sheen.\n float shininess = mix(8.0, 64.0, clamp(height * 2.0, 0.0, 1.0));\n float spec = pow(max(dot(N, H), 0.0), shininess);\n // Modulate the lit terms by how much paint sits here (no relief on bare ground).\n float relief = clamp(h * 1.5, 0.0, 1.0);\n vec3 lit = col * (1.0 + uImpasto * relief * (diff - 0.5) * 0.5 * uLightColor);\n lit += uImpasto * relief * spec * 0.6 * uLightColor;\n return lit;\n}\n\n// Best-of-9-neighbor placement: sample 3x3 surrounding cells and take the\n// stroke that covers this pixel most. Breaks the grid by per-cell jitter and\n// sparse density via noise thresholding.\nStrokeHit bestOil(vec2 p, float cellSize, float lenMul, float halfWMul,\n float jitterAmt, float density, int shapeType,\n float bristleAmp, vec2 flow, float t, float seed) {\n vec2 cell = floor(p / cellSize);\n\n StrokeHit best = noHit();\n // 3x3 neighborhood\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cc = cell + vec2(float(dx), float(dy));\n vec2 hh = hash22(cc + seed) - 0.5;\n // Density gate — 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 — the AW.W4.1 strokeOrient switch:\n // uStrokeOrient==0 (flow) — the layer-provided hand-authored flowField.\n // uStrokeOrient==1 (tensor) — the structure-tensor minor eigenvector at the\n // cell center (the color field's edge-tangent),\n // so strokes hug the color zones (van Gogh).\n // coh carries the per-cell coherence A for the W4.3 energy grading (long\n // confident strokes where structure is coherent, stubby dabs in flat zones).\n vec2 f;\n float coh = 1.0;\n if (uStrokeOrient == 1) {\n vec3 tf = structureTensorField(center, t, safeDir(flow));\n f = safeDir(tf.xy);\n coh = tf.z;\n } else {\n f = safeDir(flow);\n }\n // Per-stroke deterministic local perturbation so alternate stroke layers stay\n // live. Tensor strokes get LESS angular jitter (the field already orients them).\n float jScale = (uStrokeOrient == 1) ? 0.4 : 1.0;\n float angJ = (hash21(cc + seed + 11.0) - 0.5) * 0.9 * jScale; // +/- 0.45/0.18 rad\n float localCurl = (fbm(center * (2.6 + seed * 0.11) + seed * 1.9) - 0.5) * 0.55 * uFlowCurl;\n vec2 dir = rotateDir(f, angJ + localCurl);\n\n // AW.W4.3 — energy-graded length/density for the van-Gogh cascade: in vangogh\n // mode (uMedium==5) modulate the stroke length by local luminance (bright →\n // long confident strokes, dark → short dabs, the Starry-Night Kolmogorov/\n // Batchelor congruence) AND by coherence (coherent zones → long, flat → stubby).\n float energy = 1.0;\n if (uMedium == 5) {\n float luma = clamp(dot(sampleBase(center, t), W_LUMA), 0.0, 1.0);\n // long in the lights + coherent zones; short dabs in the darks + flat zones.\n energy = mix(0.5, 1.5, luma) * mix(0.6, 1.0, coh);\n }\n\n float lenV = cellSize * lenMul * (0.65 + 0.55 * hash21(cc + seed + 23.0)) * energy;\n float halfW = cellSize * halfWMul * (0.70 + 0.55 * hash21(cc + seed + 41.0));\n float bulge = (hash21(cc + seed + 53.0) - 0.5) * lenV * 0.35;\n\n vec2 a = center - dir * (lenV * 0.5);\n vec2 b = center + dir * (lenV * 0.5);\n\n vec3 colMid = brokenColorJitter(\n sampleBase(center, t),\n hash21(cc + seed + 89.0),\n hash21(cc * 2.3 + seed + 97.0),\n 1.0\n );\n\n StrokeHit h = curvedStroke(p, a, b, halfW, bulge, shapeType,\n 7.0, bristleAmp,\n hash21(cc + seed + 67.0), colMid);\n if (h.coverage > best.coverage) best = h;\n }\n }\n return best;\n}\n", pe = "// ── Medium overlays ───────────────────────────────────────────────────────\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// ── Structure-tensor / edge-tangent-flow (AW.W4.1) ─────────────────────────\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², GxGy],[GxGy, Gy²]] eigen-decomposes closed-form into the MINOR\n// eigenvector (the edge-TANGENT — 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 = (λ1-λ2)/(λ1+λ2). 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: λ = 0.5(tr ± sqrt((Jxx-Jyy)² + 4Jxy²)).\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 — the edge TANGENT (the stroke direction). The principal\n // angle is θ = 0.5·atan2(2·Jxy, Jxx-Jyy); this form is robust at Jxy≈0 (where the\n // (λ-Jyy, Jxy) eigenvector formula collapses to a zero vector). The tangent is at\n // θ + 90°. Guard the fully-isotropic (no-gradient) degenerate case → 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] — 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° 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 — 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 — 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 — 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", me = "// ── Crayon / oil-pastel — paper tooth × wax pigment (PEER medium) ─────────\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 — never a branch inside mediumOil().\n// AW.W4.4 — GENUINE oil pastel: pigment DEPOSITED on the paper tooth, a broken\n// SCUMBLE upper layer letting the paper/lower color show through, and a waxy BURNISH\n// film whose sheen grows with layer count. Reworked from the old tooth-multiply\n// gradient into a material-truth deposition model. Oriented along the ETF field\n// (W4.1); OKLCh broken color (W5). The oil-pastel first-class medium (uMedium==6)\n// AND the legacy crayon peer (uMedium==4) both dispatch this single body.\nvec3 mediumCrayon(vec3 col, vec2 p, float t) {\n // Orient the deposition streaks along the structure-tensor edge-tangent. The\n // painterly mediums force uStrokeOrient==tensor, so read the tensor field directly;\n // fall back to flowField for the legacy crayon strokeMode route.\n vec2 flow = (uStrokeOrient == 1)\n ? structureTensorField(p, t, flowField(p, t)).xy\n : flowField(p, t);\n float ang = atan(flow.y, flow.x);\n float ca = cos(-ang), sa = sin(-ang);\n vec2 pr = vec2(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n float aniso = mix(0.45, 0.95, uStrokeAnisotropy);\n float scale = max(uStrokeScale * 1.6, 180.0);\n\n // ── Paper TOOTH height field — the relief the pigment rides. Anisotropic\n // (squished along flow, coarse cross-flow). Peaks = ridges, valleys = paper pits.\n float t1 = vnoise(vec2(pr.x * scale * aniso, pr.y * scale));\n float t2 = vnoise(vec2(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + 11.0);\n float t3 = vnoise(vec2(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + 23.0);\n float paperHeight = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n\n // ── DEPOSITION — pigment lands on the tooth PEAKS and skips the valleys. Light\n // pressure (low uStrokeAmount) shows paper through the valleys; heavy pressure\n // fills them. deposit in [0,1] is the per-pixel pigment coverage.\n float pressure = clamp(uStrokeAmount, 0.0, 1.0);\n float toothFloor = mix(0.62, 0.18, pressure); // heavy pressure lowers the floor\n float deposit = smoothstep(toothFloor, toothFloor + 0.35, paperHeight);\n\n // ── SCUMBLE — a broken UPPER layer at coverage < 1 (the signature oil-pastel\n // move). A coarse mask gates a lighter dragged stroke over the deposition so the\n // LOWER color shows through the gaps. Drag direction follows the tensor flow.\n float scumbleMask = smoothstep(0.35, 0.85, vnoise(vec2(pr.x * scale * 0.55, pr.y * scale * 0.22) + 7.0));\n float scumbleCoverage = scumbleMask * pressure * 0.6; // < 1 — paper/lower shows\n\n // The lower color is the base; the deposited pigment is the base lifted into the\n // tooth. Mix the base toward the deposited layer by the deposition coverage, then\n // let the scumble break the upper layer so the lower reads through the gaps.\n vec3 paper = col * 0.92; // the paper-showing-through tone\n vec3 deposited = col; // the pigment-on-tooth tone\n vec3 result = mix(paper, deposited, deposit);\n // Scumble: a broken pass of the deposited color at < 1 coverage.\n result = mix(result, deposited * (0.96 + 0.08 * t1), scumbleCoverage);\n\n // ── WAXY BURNISH FILM — a low-roughness BROAD specular lobe whose sheen grows\n // with the pigment build-up (burnish), distinct from oil's sharp glint. Reads the\n // paper-height gradient as a soft normal and lights it with the movable uLightDir.\n float waxNormalZ = 2.4;\n vec3 N = normalize(vec3(-dFdx(paperHeight) * 30.0, -dFdy(paperHeight) * 30.0, waxNormalZ));\n vec3 L = normalize(uLightDir);\n vec3 V = vec3(0.0, 0.0, 1.0);\n vec3 H = normalize(L + V);\n float burnish = deposit * (0.5 + 0.5 * scumbleMask); // sheen grows with build-up\n float sheen = pow(max(dot(N, H), 0.0), 6.0); // broad waxy lobe\n result += burnish * sheen * 0.10 * uLightColor;\n\n // ── Broken-color pigment: stable wax/pigment patches in OKLCh (W5), not flicker.\n vec2 pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n float pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + 19.0));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + 17.0),\n hash21(pigmentCell * 2.1 + 31.0),\n 0.45 + 0.55 * pigmentMask\n );\n\n // Oil pastel is saturation-amplified (the waxy chroma punch).\n result = saturate3(result, 1.12);\n\n return result;\n}\n\nvec3 mediumOil(vec3 col, vec2 p, float t) {\n // Mode knobs (uStrokeMode) — oil-stroke modes ONLY (crayon is a peer medium,\n // uMedium==4, dispatched at main()):\n // 0 oil — balanced modern-abstract/palette-knife hybrid\n // 1 knife — palette-knife impasto: razor edges, heavy bristle/shadow\n // 3 brushwork — 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 — no grid\n vec2 flow = flowField(p, t);\n\n vec3 result = col;\n // AW.W4.2 — accumulated paint HEIGHT across the stroke layers. The relight reads\n // its gradient for the normal; the canvas tooth seeds the base relief.\n float height = 0.0;\n\n // Layer 1 — big gestural strokes (sparse, shaping)\n StrokeHit hBig = bestOil(p, sBig, lenMulBig, widMulBig, jitterAmt * 0.55,\n densityBig, shapeType, bristleAmp, flow, t, 1.3);\n paintOver(result, height, hBig, streakFreq * 0.7, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 1.3);\n\n // Layer 2 — medium body strokes\n StrokeHit hMed = bestOil(p + vec2(11.3, 3.7), sMed, lenMulMed, widMulMed,\n jitterAmt, densityMed, shapeType, bristleAmp, flow, t, 2.7);\n paintOver(result, height, hMed, streakFreq, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 2.7);\n\n // Layer 3 — small dabs (more frequent, smaller)\n int smlShape = (mode == 1) ? 2 : shapeType; // knife uses dabs for sparkle\n StrokeHit hSml = bestOil(p + vec2(-5.1, 8.4), sSml, lenMulSml, widMulSml,\n jitterAmt * 1.3, densitySml, smlShape,\n bristleAmp * 0.85, flow, t, 4.1);\n paintOver(result, height, hSml, streakFreq * 1.4, streakAmp * 0.8,\n uImpasto * impastoAmp * 0.65 * uStrokeAmount, hardness, 4.1);\n\n // Layer 4 — fill dabs (very dense, very small) — covers bald spots\n float sFill = baseScale * 0.22;\n float lenMulFill = mix(1.4, 2.0, uStrokeAnisotropy);\n float widMulFill = mix(0.50, 0.38, uStrokeAnisotropy);\n int fillShape = (mode == 1) ? 3 : 2; // knife=even, others=dab (round fills)\n StrokeHit hFill = bestOil(p + vec2(3.9, -6.2), sFill, lenMulFill, widMulFill,\n jitterAmt * 1.5, 0.95, fillShape,\n bristleAmp * 0.6, flow, t, 8.9);\n paintOver(result, height, hFill, streakFreq * 1.8, streakAmp * 0.6,\n uImpasto * impastoAmp * 0.4 * uStrokeAmount, hardness * 0.9, 8.9);\n\n // Optional crosshatch layer\n if (uStrokeLayers == 2) {\n vec2 flow2 = vec2(-flow.y, flow.x);\n StrokeHit hX = bestOil(p + vec2(7.3, -2.1), sMed, lenMulMed * 0.9, widMulMed,\n jitterAmt, densityMed * 0.7, shapeType, bristleAmp, flow2, t, 6.5);\n paintOver(result, height, hX, streakFreq, streakAmp * 0.85,\n uImpasto * impastoAmp * 0.55 * uStrokeAmount, hardness, 6.5);\n }\n\n // Canvas tooth — linen weave\n float tooth1 = vnoise(p * toothScale);\n float tooth2 = vnoise(p * toothScale * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * toothAmp * uCanvasGrain;\n\n // AW.W4.2 — relight the accumulated paint height with the movable uLightDir\n // source (diffuse + Blinn specular, in LINEAR before aces()). The canvas tooth\n // is the base relief term so the weave also catches the raking light.\n float canvasBase = tooth * toothAmp * 0.5;\n result = relightImpasto(result, height, canvasBase);\n\n // Pigment saturation boost\n result = saturate3(result, pigmentSat);\n\n return result;\n}\n\n// AW.W4.3 — the van-Gogh atomic-stroke medium. Composes the oil stroke engine\n// (mediumOil) with the W4.1 ETF orientation (forced tensor via the bridge), the\n// W4.3 energy grading (gated by uMedium==5 inside bestOil — long confident strokes\n// in the lights/coherent zones, short dabs in the darks/flat zones, the Starry-Night\n// Kolmogorov/Batchelor cascade), the W5 OKLCh per-stroke pigment jitter (already in\n// bestOil's brokenColorJitter), and the W4.2 real impasto relight. No subject matter\n// — the \"source image\" is the generated nuclei field, so strokes trace its iso-bands.\nvec3 mediumVangogh(vec3 col, vec2 p, float t) {\n return mediumOil(col, p, t);\n}", A = "\n", he = `#version 300 es
|
|
10
11
|
precision highp float;
|
|
11
12
|
|
|
12
13
|
in vec2 vUv;
|
|
@@ -48,24 +49,45 @@ uniform int uNoiseOctaves;
|
|
|
48
49
|
// 0 smooth, 1 pastel, 2 watercolor, 3 oil, 4 crayon (peer medium — wax pigment
|
|
49
50
|
// on paper tooth; NOT an oil-stroke sub-mode, so it dispatches at main() level).
|
|
50
51
|
uniform int uMedium;
|
|
51
|
-
|
|
52
|
+
// Hue-arc method for the OKLCh palette interpolation (W5). Mirrors value.js's
|
|
53
|
+
// HueInterpolationMethod: 0 shorter, 1 longer, 2 increasing, 3 decreasing. Only
|
|
54
|
+
// consulted when the hue-arc path is requested; OKLab-rectangular ramps (the
|
|
55
|
+
// default for adjacent-hue stops) ignore it.
|
|
56
|
+
uniform int uHuePath;
|
|
57
|
+
uniform int uFlowPattern; // 0 none, 1 radial, 2 swirl, 3 diagonal, 4 multi, 5 tensor/ETF (W4.1)
|
|
52
58
|
uniform vec2 uFlowFocal;
|
|
53
59
|
uniform float uFlowAngle;
|
|
54
60
|
uniform float uFlowCurl;
|
|
55
61
|
uniform vec2 uCursor; // in 0..1 screen space (matches pN)
|
|
56
62
|
uniform float uCursorStrength; // 0..1 attraction amount
|
|
57
63
|
uniform float uCursorRadius; // radius of influence (0.05..0.5)
|
|
64
|
+
// AW.W8.1 — the velocity-reactive flow: a fast flick injects a transient swirl-burst
|
|
65
|
+
// (uCursorBurst, decaying over ~1s) along the pointer velocity (uCursorVelocity),
|
|
66
|
+
// distinct from the steady uCursorStrength attraction. Both are PRM-gated (the cursor
|
|
67
|
+
// write-path early-outs on reduced-motion, and the master tempo scalar zeroes them).
|
|
68
|
+
uniform vec2 uCursorVelocity; // smoothed pointer velocity (normalized-screen/move)
|
|
69
|
+
uniform float uCursorBurst; // 0..1 transient flick burst
|
|
58
70
|
uniform float uStrokeAmount;
|
|
59
71
|
uniform float uStrokeScale;
|
|
60
72
|
uniform float uStrokeAnisotropy;
|
|
61
73
|
uniform int uStrokeLayers; // 1 or 2 (crosshatch)
|
|
62
74
|
uniform int uStrokeMode; // 0 oil (modern gestural), 1 palette-knife, 3 modern-chunky (crayon is uMedium==4, a peer medium)
|
|
75
|
+
// AW.W4.1 — the stroke-orientation source: 0 flow (hand-authored flowField), 1
|
|
76
|
+
// tensor (the structure-tensor minor eigenvector — the color field's edge-tangent).
|
|
77
|
+
uniform int uStrokeOrient;
|
|
63
78
|
uniform float uWetEdge;
|
|
64
79
|
uniform float uGranulation;
|
|
65
80
|
uniform float uImpasto;
|
|
66
81
|
uniform float uBrokenColor;
|
|
67
82
|
uniform float uCanvasGrain;
|
|
68
83
|
|
|
84
|
+
// AW.W4.2 — the impasto relight axis. A movable directional source lighting the
|
|
85
|
+
// accumulated paint-height field (diffuse + Blinn specular, in LINEAR before aces()).
|
|
86
|
+
// AW.W8 drives uLightDir from the cursor (cursor-as-light). Default = upper-left so
|
|
87
|
+
// the still default reads identically to the prior fixed rim.
|
|
88
|
+
uniform vec3 uLightDir; // unit direction (x,y in screen space, z toward viewer)
|
|
89
|
+
uniform vec3 uLightColor; // warm-white tint (linear)
|
|
90
|
+
|
|
69
91
|
// Motion
|
|
70
92
|
uniform float uNucleiDrift;
|
|
71
93
|
uniform float uPaletteDrift;
|
|
@@ -120,7 +142,7 @@ float vnoise(vec2 p) {
|
|
|
120
142
|
// The rotated-octave FBM rotation constant — spliced from the shared chunk (AV.W2
|
|
121
143
|
// — the one FBM_ROT). The loop below stays aurora-local (2.02 lacunarity +
|
|
122
144
|
// uniform-driven octaves, per §3a — only the rotation constant converges).
|
|
123
|
-
${
|
|
145
|
+
${u}
|
|
124
146
|
|
|
125
147
|
float fbm(vec2 p) {
|
|
126
148
|
float v = 0.0;
|
|
@@ -138,7 +160,65 @@ float fbm(vec2 p) {
|
|
|
138
160
|
// (AV.W2 — the single OETF source; the AV.W1 local copy is deleted here). Aurora's
|
|
139
161
|
// linear pipeline closes the seam with this transfer before fragColor (the
|
|
140
162
|
// proof:aurora-space-gamma seam).
|
|
141
|
-
${
|
|
163
|
+
${o}
|
|
164
|
+
|
|
165
|
+
// W5 — the four Ottosson OKLab/OKLCh matrices + their space fns, spliced from the
|
|
166
|
+
// SAME shared chunk the goo-blob uses (zero new payload, 1e-6-verified). PI is
|
|
167
|
+
// defined first because the chunk's oklabToOklch folds the hue into [0, 2pi).
|
|
168
|
+
const float PI = 3.141592653589793;
|
|
169
|
+
${r}
|
|
170
|
+
|
|
171
|
+
// ── OKLCh palette interpolation (W5) ─────────────────────────────────────────
|
|
172
|
+
// The muddy-midtone kill: distant-hue stops interpolated by a plain linear mix()
|
|
173
|
+
// desaturate toward grey at the midpoint. The OKLab-rectangular lerp (L,a,b) holds
|
|
174
|
+
// chroma across the ramp; the OKLCh hue-arc form (used only for deliberate rainbow
|
|
175
|
+
// travel via uHuePath increasing/decreasing) sweeps the hue wheel without flipping.
|
|
176
|
+
|
|
177
|
+
// value.js's interpolateHue, transcribed in the NORMALIZED-TURNS domain (h_rad/TAU
|
|
178
|
+
// → turns, the exact .5/+1.0 branch per method, fract() wrap, *TAU back). A
|
|
179
|
+
// radians-native port (PI thresholds / +TAU wrap) diverges 180° at the antipode.
|
|
180
|
+
// methods: 0 shorter, 1 longer, 2 increasing, 3 decreasing.
|
|
181
|
+
float interpolateHueTurns(float h0, float h1, float t, int method) {
|
|
182
|
+
// h0/h1 are radians in [0, 2pi); convert to turns [0,1).
|
|
183
|
+
float TAU = 2.0 * PI;
|
|
184
|
+
float a = h0 / TAU;
|
|
185
|
+
float b = h1 / TAU;
|
|
186
|
+
float i = b - a;
|
|
187
|
+
if (method == 0) { // shorter
|
|
188
|
+
if (i > 0.5) a += 1.0; else if (i < -0.5) b += 1.0;
|
|
189
|
+
} else if (method == 1) { // longer
|
|
190
|
+
if (i > 0.0 && i < 0.5) a += 1.0; else if (i > -0.5 && i <= 0.0) b += 1.0;
|
|
191
|
+
} else if (method == 2) { // increasing
|
|
192
|
+
if (i < 0.0) b += 1.0;
|
|
193
|
+
} else { // decreasing
|
|
194
|
+
if (i > 0.0) a += 1.0;
|
|
195
|
+
}
|
|
196
|
+
float r = a + t * (b - a);
|
|
197
|
+
r = fract(r); // (r % 1 + 1) % 1 — fract is non-negative in GLSL
|
|
198
|
+
return r * TAU; // back to radians
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Linear-sRGB endpoint pair → OKLab-rectangular interpolation → linear sRGB. Holds
|
|
202
|
+
// chroma across the ramp (no grey midpoint). Used for the default/shorter ramp path.
|
|
203
|
+
vec3 mixPaletteOklab(vec3 linA, vec3 linB, float t) {
|
|
204
|
+
vec3 labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3(1.0 / 3.0)));
|
|
205
|
+
vec3 labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3(1.0 / 3.0)));
|
|
206
|
+
vec3 lab = mix(labA, labB, t);
|
|
207
|
+
return oklabToLinearSrgb(lab);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Linear-sRGB endpoint pair → OKLCh interpolation along the uHuePath arc → linear
|
|
211
|
+
// sRGB. L and C lerp; H walks the chosen arc. The deliberate-rainbow path.
|
|
212
|
+
vec3 mixPaletteOklchArc(vec3 linA, vec3 linB, float t, int method) {
|
|
213
|
+
vec3 labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3(1.0 / 3.0)));
|
|
214
|
+
vec3 labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3(1.0 / 3.0)));
|
|
215
|
+
vec3 lchA = oklabToOklch(labA);
|
|
216
|
+
vec3 lchB = oklabToOklch(labB);
|
|
217
|
+
float L = mix(lchA.x, lchB.x, t);
|
|
218
|
+
float C = mix(lchA.y, lchB.y, t);
|
|
219
|
+
float H = interpolateHueTurns(lchA.z, lchB.z, t, method);
|
|
220
|
+
return oklabToLinearSrgb(oklchToOklab(vec3(L, C, H)));
|
|
221
|
+
}
|
|
142
222
|
|
|
143
223
|
// Interleaved Gradient Noise (Jimenez) — a 1-LSB triangular dither applied in
|
|
144
224
|
// DISPLAY space AFTER the OETF, so 8-bit mid-tone banding on the soft gradient is
|
|
@@ -207,14 +287,63 @@ vec2 domainWarp(vec2 p, float t) {
|
|
|
207
287
|
return warped;
|
|
208
288
|
}
|
|
209
289
|
|
|
210
|
-
` +
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
let
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
290
|
+
` + le + A + ue + A + "// ── Color utils (OKLCh — W5; the sRGB YIQ hueShift matrix is DELETED) ──────────\nconst vec3 W_LUMA = vec3(0.2126, 0.7152, 0.0722);\n\n// Linear sRGB → OKLab (the cbrt-LMS path; the goo-blob's srgbToOklab takes gamma,\n// here the color field is already linear so we skip the OETF inverse).\nvec3 linOklab(vec3 lin) {\n vec3 lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * (sign(lms) * pow(abs(lms), vec3(1.0 / 3.0)));\n}\n\n// Broken color (W5) — hue + chroma jitter at fixed PERCEPTUAL lightness, in OKLCh.\n// Broken color is hue variation at constant VALUE, which only OKLCh makes true (the\n// prior YIQ-style sRGB rotation muddied value). This is the seam AW.W4's van-Gogh /\n// oil-pastel per-stroke pigment jitter consumes (the pigment-variation read).\nvec3 brokenColorJitter(vec3 c, float hueSeed, float valueSeed, float strength) {\n float amt = clamp(uBrokenColor * strength, 0.0, 1.0);\n if (amt <= 0.001) return c;\n vec3 lch = oklabToOklch(linOklab(c));\n // ±~16° hue swing + ±~12% chroma swing; L held so value reads constant.\n lch.z += (hueSeed - 0.5) * (32.0 * PI / 180.0) * amt;\n lch.y = max(lch.y * (1.0 + (valueSeed - 0.5) * 0.28 * amt), 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n}\n\n// Saturation (W5) — chroma scale at fixed L/H in OKLCh (the perceptual saturation,\n// replacing the luma-mix sRGB form). amt=1 is identity; amt<1 desaturates toward\n// the achromatic axis with NO lightness shift (the muddy-luma-mix grey is gone).\nvec3 saturate3(vec3 c, float amt) {\n vec3 lch = oklabToOklch(linOklab(c));\n lch.y = max(lch.y * amt, 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n}\n\n" + de + A + pe + A + fe + A + me + A + "\n// ── Main ──────────────────────────────────────────────────────────────────\nvoid main() {\n // Normalized 0..1 coordinates for nuclei, domain warp, and medium sampling.\n vec2 uv = vUv;\n vec2 pN = uv;\n\n float t = uTime;\n\n // Warp in pN space\n vec2 p_warp = domainWarp(pN, t);\n\n // Composition\n float paletteId; float valueMod;\n nucleiField(p_warp, t, paletteId, valueMod);\n vec3 col = samplePalette(paletteId);\n col *= 1.0 + uValueVariance * valueMod;\n\n // Breath — slow global luminance wobble\n float breath = sin(t * 6.2831 / max(uBreathPeriod, 1.0));\n col *= 1.0 + uBreathDepth * breath * 0.5;\n\n // Medium — crayon (4) is a peer dispatched here, not a mediumOil sub-mode.\n // AW.W4.3/W4.4 — vangogh (5) is the energy-graded atomic-stroke medium;\n // oil-pastel (6) is the reworked deposition+scumble+waxy model. The legacy crayon\n // peer (4) and oil-pastel (6) share the reworked mediumCrayon body (no duplicate).\n if (uMedium == 1) col = mediumPastel(col, pN, t);\n else if (uMedium == 2) col = mediumWatercolor(col, pN, t);\n else if (uMedium == 3) col = mediumOil(col, pN, t);\n else if (uMedium == 4) col = mediumCrayon(col, pN, t);\n else if (uMedium == 5) col = mediumVangogh(col, pN, t);\n else if (uMedium == 6) col = mediumCrayon(col, pN, t);\n\n // Saturation trim\n col = saturate3(col, uSaturation);\n\n // Tonemap + film grain\n col = aces(col);\n float grain = hash21(gl_FragCoord.xy + t * 17.0);\n col += (grain - 0.5) * uPaperGrain;\n\n col = clamp(col * 0.985 + 0.008, 0.0, 1.0);\n\n // MANDATORY OETF — closes the seam (mirrors metaball.frag.ts:278). The whole\n // pipeline above (linear palette LUT, ACES tonemap, grain) runs in LINEAR; without\n // this transfer the aurora ships ~2.2× too dark (linear 0.5 → ~0.215, not ~0.735).\n col = linearToSrgb(col);\n\n // 1-LSB triangular IGN dither in DISPLAY space, AFTER the OETF — breaks 8-bit\n // mid-tone banding on the soft gradient. Texture-free (the canonical Jimenez IGN).\n col += (1.0 / 255.0) * (ign(gl_FragCoord.xy) - 0.5);\n\n // Premultiply operates on DISPLAY-space color (matches the blob: OETF, then premul).\n fragColor = vec4(col * uAlpha, uAlpha);\n}\n";
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region src/composables/glass/createGPUCanvas.ts
|
|
293
|
+
function ge(e, t) {
|
|
294
|
+
let { device: n, setup: r } = t, i = typeof navigator < "u" && navigator.gpu ? navigator.gpu.getPreferredCanvasFormat() : "bgra8unorm", o = null, s = null, c = n;
|
|
295
|
+
function l() {
|
|
296
|
+
let a = e.getContext("webgpu");
|
|
297
|
+
if (!a) throw Error("[createGPUCanvas] WebGPU canvas context unavailable");
|
|
298
|
+
return o = a, o.configure({
|
|
299
|
+
device: n,
|
|
300
|
+
format: i,
|
|
301
|
+
alphaMode: t.alphaMode ?? "premultiplied"
|
|
302
|
+
}), s = r({
|
|
303
|
+
device: n,
|
|
304
|
+
context: o,
|
|
305
|
+
format: i
|
|
306
|
+
}), {
|
|
307
|
+
frame: s.frame,
|
|
308
|
+
shouldContinue: s.shouldContinue,
|
|
309
|
+
time: s.time,
|
|
310
|
+
teardown: () => {
|
|
311
|
+
s?.teardown?.(), o?.unconfigure(), o = null, s = null, c = null;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
let u = a({
|
|
316
|
+
canvas: e,
|
|
317
|
+
mode: t.mode,
|
|
318
|
+
respectReducedMotion: t.respectReducedMotion,
|
|
319
|
+
buildContext: l,
|
|
320
|
+
resize: () => s?.resize()
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
arm: u.arm,
|
|
324
|
+
suspend: u.suspend,
|
|
325
|
+
resume: u.resume,
|
|
326
|
+
wake: u.wake,
|
|
327
|
+
renderAt: u.renderAt,
|
|
328
|
+
dispose: u.dispose,
|
|
329
|
+
get device() {
|
|
330
|
+
return c;
|
|
331
|
+
},
|
|
332
|
+
get reducedMotion() {
|
|
333
|
+
return u.reducedMotion;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/components/custom/aurora/composables/glSetup.ts
|
|
339
|
+
var j = "[Aurora]", _e = /* @__PURE__ */ "uTime.uPalette.uStopCount.uNucleiCount.uNucleiPos.uNucleiRadius.uNucleiPaletteBias.uNucleiValueBias.uNucleiDriftRadius.uNucleiDriftPhase.uNucleiElong.uNucleiAngle.uSoftmaxBeta.uValueVariance.uWarpAmount.uWarpScale.uWarpDrift.uWarpMode.uNoiseOctaves.uMedium.uHuePath.uFlowPattern.uFlowFocal.uFlowAngle.uFlowCurl.uCursor.uCursorStrength.uCursorRadius.uCursorVelocity.uCursorBurst.uStrokeAmount.uStrokeScale.uStrokeAnisotropy.uStrokeLayers.uStrokeMode.uStrokeOrient.uLightDir.uLightColor.uWetEdge.uGranulation.uImpasto.uBrokenColor.uCanvasGrain.uNucleiDrift.uPaletteDrift.uBreathDepth.uBreathPeriod.uSaturation.uPaperGrain.uAlpha".split(".");
|
|
340
|
+
function M(e, t, n) {
|
|
341
|
+
let r = d(e, e.VERTEX_SHADER, t, j), i = d(e, e.FRAGMENT_SHADER, n, j), a = s(e, r, i, j);
|
|
342
|
+
e.useProgram(a);
|
|
343
|
+
let o = e.createVertexArray();
|
|
344
|
+
e.bindVertexArray(o);
|
|
345
|
+
let c = e.createBuffer();
|
|
346
|
+
e.bindBuffer(e.ARRAY_BUFFER, c), e.bufferData(e.ARRAY_BUFFER, new Float32Array([
|
|
218
347
|
-1,
|
|
219
348
|
-1,
|
|
220
349
|
3,
|
|
@@ -222,116 +351,227 @@ function L(e, t, n) {
|
|
|
222
351
|
-1,
|
|
223
352
|
3
|
|
224
353
|
]), e.STATIC_DRAW);
|
|
225
|
-
let
|
|
226
|
-
e.enableVertexAttribArray(
|
|
227
|
-
let
|
|
228
|
-
for (let t of
|
|
354
|
+
let l = e.getAttribLocation(a, "aPos");
|
|
355
|
+
e.enableVertexAttribArray(l), e.vertexAttribPointer(l, 2, e.FLOAT, !1, 0, 0);
|
|
356
|
+
let u = {};
|
|
357
|
+
for (let t of _e) u[t] = e.getUniformLocation(a, t);
|
|
229
358
|
return {
|
|
230
|
-
program:
|
|
231
|
-
vs:
|
|
232
|
-
fs:
|
|
233
|
-
uniforms:
|
|
359
|
+
program: a,
|
|
360
|
+
vs: r,
|
|
361
|
+
fs: i,
|
|
362
|
+
uniforms: u,
|
|
234
363
|
geometry: {
|
|
235
|
-
vao:
|
|
236
|
-
buf:
|
|
364
|
+
vao: o,
|
|
365
|
+
buf: c
|
|
237
366
|
}
|
|
238
367
|
};
|
|
239
368
|
}
|
|
240
369
|
//#endregion
|
|
241
370
|
//#region src/components/custom/aurora/composables/color.ts
|
|
242
|
-
function
|
|
371
|
+
function N(e, t = 8, n) {
|
|
243
372
|
let r = n ?? new Float32Array(t * 3), i = Math.min(e.length, t);
|
|
244
373
|
for (let t = 0; t < i; t++) {
|
|
245
|
-
let [n, i, a] =
|
|
374
|
+
let [n, i, a] = _(e[t]);
|
|
246
375
|
r[t * 3 + 0] = n, r[t * 3 + 1] = i, r[t * 3 + 2] = a;
|
|
247
376
|
}
|
|
248
377
|
for (let e = i; e < t; e++) r[e * 3 + 0] = 0, r[e * 3 + 1] = 0, r[e * 3 + 2] = 0;
|
|
249
378
|
return r;
|
|
250
379
|
}
|
|
251
|
-
function
|
|
380
|
+
function P(e) {
|
|
252
381
|
if (e.length === 0) return "transparent";
|
|
253
|
-
if (e.length === 1) return
|
|
382
|
+
if (e.length === 1) return y(e[0]);
|
|
254
383
|
let t = e.length;
|
|
255
384
|
return `linear-gradient(135deg, ${e.map((e, n) => {
|
|
256
385
|
let r = Math.round(n / (t - 1) * 100);
|
|
257
|
-
return `${
|
|
386
|
+
return `${y(e)} ${r}%`;
|
|
258
387
|
}).join(", ")})`;
|
|
259
388
|
}
|
|
260
|
-
function
|
|
261
|
-
let t = e.replace("#", ""), n = parseInt(t.slice(0, 2), 16), r = parseInt(t.slice(2, 4), 16), i = parseInt(t.slice(4, 6), 16), [a, o, s] =
|
|
389
|
+
function F(e) {
|
|
390
|
+
let t = e.replace("#", ""), n = parseInt(t.slice(0, 2), 16), r = parseInt(t.slice(2, 4), 16), i = parseInt(t.slice(4, 6), 16), [a, o, s] = se(n / 255, r / 255, i / 255), [c, l, u] = oe(a, o, s);
|
|
262
391
|
return {
|
|
263
392
|
L: c,
|
|
264
393
|
C: l,
|
|
265
394
|
h: u
|
|
266
395
|
};
|
|
267
396
|
}
|
|
268
|
-
var
|
|
269
|
-
function
|
|
270
|
-
let n = typeof e == "string" ?
|
|
271
|
-
|
|
272
|
-
let
|
|
273
|
-
for (let e = 0; e <
|
|
274
|
-
let t =
|
|
275
|
-
|
|
397
|
+
var I = [.35, .95];
|
|
398
|
+
function L(e, t = {}) {
|
|
399
|
+
let n = typeof e == "string" ? b(e) : e, { stopCount: r = 4, harmony: i = "analogous", lightnessSpread: a = .32, chromaFalloff: o = .85, hueSpread: s = 28, lightnessEasing: c = "linear", chromaEasing: l = "bell", temperatureShift: u = 0 } = t, d = Math.max(2, Math.min(8, Math.round(r))), [f, p] = I, m = n.L - a / 2, h = n.L + a / 2;
|
|
400
|
+
m < f && (h += f - m, m = f), h > p && (m -= h - p, h = p), m = Math.max(f, m), h = Math.min(p, h);
|
|
401
|
+
let _ = [];
|
|
402
|
+
for (let e = 0; e < d; e++) {
|
|
403
|
+
let t = d === 1 ? 0 : e / (d - 1), r = m + (h - m) * R(t, c), a = n.C * o, f;
|
|
404
|
+
if (l === "bell") f = a + (n.C - a) * ve(t);
|
|
405
|
+
else {
|
|
406
|
+
let e = l === "sine" ? R(t, "sine") : t;
|
|
407
|
+
f = n.C - (n.C - a) * e;
|
|
408
|
+
}
|
|
409
|
+
let p = v(n.h, i, s, t);
|
|
410
|
+
u > 0 && (p = ye(p, t, u)), _.push(g({
|
|
276
411
|
L: r,
|
|
277
|
-
C:
|
|
278
|
-
h:
|
|
412
|
+
C: f,
|
|
413
|
+
h: p
|
|
279
414
|
}));
|
|
280
415
|
}
|
|
281
|
-
return
|
|
416
|
+
return _;
|
|
282
417
|
}
|
|
283
|
-
function
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
case "triad": return i(e + 240 * r);
|
|
289
|
-
default: return i(e + n * (r - .5) * 2);
|
|
290
|
-
}
|
|
418
|
+
function R(e, t) {
|
|
419
|
+
return t === "sine" ? .5 - .5 * Math.cos(Math.PI * e) : e;
|
|
420
|
+
}
|
|
421
|
+
function ve(e) {
|
|
422
|
+
return Math.sin(Math.PI * e);
|
|
291
423
|
}
|
|
292
|
-
function
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
424
|
+
function ye(e, t, n) {
|
|
425
|
+
return ((e) => (e % 360 + 360) % 360)(e + (t - .5) * 2 * n * 22);
|
|
426
|
+
}
|
|
427
|
+
function be(e) {
|
|
428
|
+
let t = [.33, .67], n = [
|
|
429
|
+
[t[0], t[0]],
|
|
430
|
+
[t[1], t[1]],
|
|
431
|
+
[t[1], t[0]],
|
|
432
|
+
[t[0], t[1]],
|
|
433
|
+
[.5, .5],
|
|
434
|
+
[.5, t[0]]
|
|
435
|
+
], r = Math.max(1, Math.min(n.length, e));
|
|
436
|
+
return Array.from({ length: r }, (e, t) => {
|
|
437
|
+
let [i, a] = n[t];
|
|
438
|
+
return {
|
|
439
|
+
x: i,
|
|
440
|
+
y: a,
|
|
441
|
+
radius: .5,
|
|
442
|
+
paletteBias: r === 1 ? 0 : t / (r - 1),
|
|
443
|
+
valueBias: (t % 2 == 0 ? 1 : -1) * .05,
|
|
444
|
+
driftRadius: .045,
|
|
445
|
+
driftPhase: t * 2.4 % (Math.PI * 2)
|
|
446
|
+
};
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
var xe = {
|
|
450
|
+
atmospheric: {
|
|
451
|
+
harmony: "analogous",
|
|
452
|
+
medium: "smooth",
|
|
453
|
+
temperatureShift: .3,
|
|
454
|
+
chromaEasing: "bell",
|
|
455
|
+
saturation: .95,
|
|
456
|
+
warpAmount: .55,
|
|
457
|
+
valueVariance: .08,
|
|
458
|
+
breathDepth: .06,
|
|
459
|
+
zones: 2
|
|
460
|
+
},
|
|
461
|
+
painterly: {
|
|
462
|
+
harmony: "split-complementary",
|
|
463
|
+
medium: "oil",
|
|
464
|
+
temperatureShift: .6,
|
|
465
|
+
chromaEasing: "bell",
|
|
466
|
+
saturation: 1.05,
|
|
467
|
+
warpAmount: .45,
|
|
468
|
+
valueVariance: .12,
|
|
469
|
+
breathDepth: .04,
|
|
470
|
+
zones: 3
|
|
471
|
+
},
|
|
472
|
+
vivid: {
|
|
473
|
+
harmony: "triad",
|
|
474
|
+
medium: "watercolor",
|
|
475
|
+
temperatureShift: .2,
|
|
476
|
+
chromaEasing: "linear",
|
|
477
|
+
saturation: 1.2,
|
|
478
|
+
warpAmount: .5,
|
|
479
|
+
valueVariance: .14,
|
|
480
|
+
breathDepth: .05,
|
|
481
|
+
zones: 3
|
|
482
|
+
},
|
|
483
|
+
muted: {
|
|
484
|
+
harmony: "monochrome",
|
|
485
|
+
medium: "pastel",
|
|
486
|
+
temperatureShift: .15,
|
|
487
|
+
chromaEasing: "bell",
|
|
488
|
+
saturation: .78,
|
|
489
|
+
warpAmount: .4,
|
|
490
|
+
valueVariance: .06,
|
|
491
|
+
breathDepth: .03,
|
|
492
|
+
zones: 2
|
|
298
493
|
}
|
|
494
|
+
};
|
|
495
|
+
function Se(e, t = "atmospheric") {
|
|
496
|
+
let n = xe[t], r = L(e, {
|
|
497
|
+
harmony: n.harmony,
|
|
498
|
+
temperatureShift: n.temperatureShift,
|
|
499
|
+
chromaEasing: n.chromaEasing,
|
|
500
|
+
stopCount: 4
|
|
501
|
+
});
|
|
299
502
|
return {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
503
|
+
...h,
|
|
504
|
+
palette: r,
|
|
505
|
+
nuclei: be(n.zones),
|
|
506
|
+
medium: n.medium,
|
|
507
|
+
saturation: n.saturation,
|
|
508
|
+
warpAmount: n.warpAmount,
|
|
509
|
+
valueVariance: n.valueVariance,
|
|
510
|
+
breathDepth: n.breathDepth
|
|
303
511
|
};
|
|
304
512
|
}
|
|
305
513
|
//#endregion
|
|
306
514
|
//#region src/components/custom/aurora/composables/uniformBridge.ts
|
|
307
|
-
var
|
|
515
|
+
var z = {
|
|
308
516
|
smooth: 0,
|
|
309
517
|
pastel: 1,
|
|
310
518
|
watercolor: 2,
|
|
311
519
|
oil: 3,
|
|
312
|
-
crayon: 4
|
|
313
|
-
|
|
520
|
+
crayon: 4,
|
|
521
|
+
vangogh: 5,
|
|
522
|
+
"oil-pastel": 6
|
|
523
|
+
}, Ce = {
|
|
524
|
+
shorter: 0,
|
|
525
|
+
longer: 1,
|
|
526
|
+
increasing: 2,
|
|
527
|
+
decreasing: 3
|
|
528
|
+
}, we = {
|
|
314
529
|
none: 0,
|
|
315
530
|
radial: 1,
|
|
316
531
|
swirl: 2,
|
|
317
532
|
diagonal: 3,
|
|
318
|
-
multi: 4
|
|
319
|
-
|
|
533
|
+
multi: 4,
|
|
534
|
+
tensor: 5
|
|
535
|
+
}, B = {
|
|
536
|
+
flow: 0,
|
|
537
|
+
tensor: 1
|
|
538
|
+
}, V = {
|
|
320
539
|
fbm: 0,
|
|
321
540
|
cellular: 1,
|
|
322
541
|
hybrid: 2
|
|
323
|
-
},
|
|
542
|
+
}, H = {
|
|
324
543
|
oil: 0,
|
|
325
544
|
knife: 1,
|
|
326
545
|
chunky: 3
|
|
327
546
|
};
|
|
328
|
-
function
|
|
329
|
-
return e.medium === "oil" && e.strokeMode === "crayon" ?
|
|
547
|
+
function U(e) {
|
|
548
|
+
return e.medium === "oil" && e.strokeMode === "crayon" ? z.crayon : z[e.medium];
|
|
549
|
+
}
|
|
550
|
+
function Te(e) {
|
|
551
|
+
return e.strokeMode === "crayon" ? H.oil : H[e.strokeMode];
|
|
330
552
|
}
|
|
331
|
-
function
|
|
332
|
-
return e.
|
|
553
|
+
function Ee(e) {
|
|
554
|
+
return e.medium === "vangogh" || e.medium === "oil-pastel" ? B.tensor : B[e.strokeOrient ?? "flow"];
|
|
333
555
|
}
|
|
334
|
-
|
|
556
|
+
var W = 16, De = W + 32 + 48;
|
|
557
|
+
function Oe(e, t, n) {
|
|
558
|
+
n.fill(0), n[0] = t, n[1] = Math.min(e.palette.length, 8), n[2] = Math.min(e.nuclei.length, 6), n[3] = e.softmaxBeta, n[4] = e.valueVariance, n[5] = e.warpAmount, n[6] = e.warpScale, n[7] = e.warpDrift, n[8] = V[e.warpMode], n[9] = e.noiseOctaves, n[10] = U(e), n[11] = e.breathDepth, n[12] = e.breathPeriod, n[13] = e.saturation, n[14] = e.paperGrain, n[15] = e.alpha;
|
|
559
|
+
let r = N(e.palette, 8), i = W;
|
|
560
|
+
for (let e = 0; e < 8; e++) n[i + e * 4 + 0] = r[e * 3 + 0], n[i + e * 4 + 1] = r[e * 3 + 1], n[i + e * 4 + 2] = r[e * 3 + 2];
|
|
561
|
+
i = W + 32;
|
|
562
|
+
let a = Math.min(e.nuclei.length, 6);
|
|
563
|
+
for (let t = 0; t < a; t++) {
|
|
564
|
+
let r = e.nuclei[t];
|
|
565
|
+
n[i + t * 4 + 0] = r.x, n[i + t * 4 + 1] = 1 - r.y, n[i + t * 4 + 2] = r.radius, n[i + t * 4 + 3] = r.paletteBias;
|
|
566
|
+
}
|
|
567
|
+
i = W + 32 + 24;
|
|
568
|
+
for (let t = 0; t < a; t++) {
|
|
569
|
+
let r = e.nuclei[t];
|
|
570
|
+
n[i + t * 4 + 0] = r.valueBias, n[i + t * 4 + 1] = r.elongation ?? 1, n[i + t * 4 + 2] = -(r.angle ?? 0) * Math.PI / 180, n[i + t * 4 + 3] = r.driftRadius;
|
|
571
|
+
}
|
|
572
|
+
return n;
|
|
573
|
+
}
|
|
574
|
+
function G(e, t, n, r) {
|
|
335
575
|
let i = (e) => 1 - e, a = {
|
|
336
576
|
palette: new Float32Array(24),
|
|
337
577
|
pos: new Float32Array(12),
|
|
@@ -344,7 +584,7 @@ function ue(e, t, n, r) {
|
|
|
344
584
|
angle: new Float32Array(6)
|
|
345
585
|
};
|
|
346
586
|
return function(o) {
|
|
347
|
-
e.useProgram(t),
|
|
587
|
+
e.useProgram(t), N(o.palette, 8, a.palette), e.uniform3fv(n.uPalette, a.palette), e.uniform1i(n.uStopCount, Math.min(o.palette.length, 8));
|
|
348
588
|
let s = Math.min(o.nuclei.length, 6);
|
|
349
589
|
e.uniform1i(n.uNucleiCount, s);
|
|
350
590
|
for (let e = 0; e < s; e++) {
|
|
@@ -352,13 +592,26 @@ function ue(e, t, n, r) {
|
|
|
352
592
|
a.pos[e * 2 + 0] = t.x, a.pos[e * 2 + 1] = i(t.y), a.rad[e] = t.radius, a.pb[e] = t.paletteBias, a.vb[e] = t.valueBias, a.dr[e] = t.driftRadius, a.dp[e] = t.driftPhase, a.elong[e] = t.elongation ?? 1, a.angle[e] = -(t.angle ?? 0) * Math.PI / 180;
|
|
353
593
|
}
|
|
354
594
|
for (let e = s; e < 6; e++) a.pos[e * 2 + 0] = 0, a.pos[e * 2 + 1] = 0, a.rad[e] = 0, a.pb[e] = 0, a.vb[e] = 0, a.dr[e] = 0, a.dp[e] = 0, a.elong[e] = 1, a.angle[e] = 0;
|
|
355
|
-
e.uniform2fv(n.uNucleiPos, a.pos), e.uniform1fv(n.uNucleiRadius, a.rad), e.uniform1fv(n.uNucleiPaletteBias, a.pb), e.uniform1fv(n.uNucleiValueBias, a.vb), e.uniform1fv(n.uNucleiDriftRadius, a.dr), e.uniform1fv(n.uNucleiDriftPhase, a.dp), e.uniform1fv(n.uNucleiElong, a.elong), e.uniform1fv(n.uNucleiAngle, a.angle), e.uniform1f(n.uSoftmaxBeta, o.softmaxBeta), e.uniform1f(n.uValueVariance, o.valueVariance), e.uniform1f(n.uWarpAmount, o.warpAmount), e.uniform1f(n.uWarpScale, o.warpScale), e.uniform1f(n.uWarpDrift, o.warpDrift), e.uniform1i(n.uWarpMode,
|
|
595
|
+
e.uniform2fv(n.uNucleiPos, a.pos), e.uniform1fv(n.uNucleiRadius, a.rad), e.uniform1fv(n.uNucleiPaletteBias, a.pb), e.uniform1fv(n.uNucleiValueBias, a.vb), e.uniform1fv(n.uNucleiDriftRadius, a.dr), e.uniform1fv(n.uNucleiDriftPhase, a.dp), e.uniform1fv(n.uNucleiElong, a.elong), e.uniform1fv(n.uNucleiAngle, a.angle), e.uniform1f(n.uSoftmaxBeta, o.softmaxBeta), e.uniform1f(n.uValueVariance, o.valueVariance), e.uniform1f(n.uWarpAmount, o.warpAmount), e.uniform1f(n.uWarpScale, o.warpScale), e.uniform1f(n.uWarpDrift, o.warpDrift), e.uniform1i(n.uWarpMode, V[o.warpMode]), e.uniform1i(n.uNoiseOctaves, o.noiseOctaves), e.uniform1i(n.uMedium, U(o)), e.uniform1i(n.uHuePath, Ce[o.huePath ?? "shorter"]), e.uniform1i(n.uFlowPattern, we[o.flow.pattern]), e.uniform2f(n.uFlowFocal, o.flow.focalX, i(o.flow.focalY)), e.uniform1f(n.uFlowAngle, o.flow.angle), e.uniform1f(n.uFlowCurl, o.flow.curl), e.uniform2f(n.uCursor, r.x, i(r.y)), e.uniform1f(n.uCursorStrength, r.strength), e.uniform1f(n.uCursorRadius, r.radius), e.uniform2f(n.uCursorVelocity, r.velX, -r.velY), e.uniform1f(n.uCursorBurst, r.burst), e.uniform1f(n.uStrokeAmount, o.strokeAmount), e.uniform1f(n.uStrokeScale, o.strokeScale), e.uniform1f(n.uStrokeAnisotropy, o.strokeAnisotropy), e.uniform1i(n.uStrokeLayers, o.strokeLayers), e.uniform1i(n.uStrokeMode, Te(o)), e.uniform1i(n.uStrokeOrient, Ee(o));
|
|
596
|
+
{
|
|
597
|
+
let t = o.lightDir ?? [
|
|
598
|
+
-.5,
|
|
599
|
+
.6,
|
|
600
|
+
.62
|
|
601
|
+
], r = o.lightColor ?? [
|
|
602
|
+
1,
|
|
603
|
+
.95,
|
|
604
|
+
.88
|
|
605
|
+
];
|
|
606
|
+
e.uniform3f(n.uLightDir, t[0], t[1], t[2]), e.uniform3f(n.uLightColor, r[0], r[1], r[2]);
|
|
607
|
+
}
|
|
608
|
+
e.uniform1f(n.uWetEdge, o.wetEdge), e.uniform1f(n.uGranulation, o.granulation), e.uniform1f(n.uImpasto, o.impasto), e.uniform1f(n.uBrokenColor, o.brokenColor), e.uniform1f(n.uCanvasGrain, o.canvasGrain), e.uniform1f(n.uNucleiDrift, o.nucleiDrift), e.uniform1f(n.uPaletteDrift, o.paletteDrift), e.uniform1f(n.uBreathDepth, o.breathDepth), e.uniform1f(n.uBreathPeriod, o.breathPeriod), e.uniform1f(n.uSaturation, o.saturation), e.uniform1f(n.uPaperGrain, o.paperGrain), e.uniform1f(n.uAlpha, o.alpha);
|
|
356
609
|
};
|
|
357
610
|
}
|
|
358
611
|
//#endregion
|
|
359
612
|
//#region src/components/custom/aurora/composables/cursorModel.ts
|
|
360
|
-
var
|
|
361
|
-
function
|
|
613
|
+
var K = .22, ke = .18, Ae = .992, je = .96, q = .3;
|
|
614
|
+
function Me() {
|
|
362
615
|
return {
|
|
363
616
|
x: .5,
|
|
364
617
|
y: .5,
|
|
@@ -366,115 +619,394 @@ function pe() {
|
|
|
366
619
|
targetY: .5,
|
|
367
620
|
strength: 0,
|
|
368
621
|
targetStrength: 0,
|
|
369
|
-
radius: .25
|
|
622
|
+
radius: .25,
|
|
623
|
+
velX: 0,
|
|
624
|
+
velY: 0,
|
|
625
|
+
burst: 0
|
|
370
626
|
};
|
|
371
627
|
}
|
|
372
|
-
function
|
|
373
|
-
|
|
628
|
+
function J(e, t = 1) {
|
|
629
|
+
let n = Math.max(0, Math.min(1, t));
|
|
630
|
+
e.x += (e.targetX - e.x) * K, e.y += (e.targetY - e.y) * K, e.strength += (e.targetStrength - e.strength) * ke, e.targetStrength *= Ae, e.velX *= (1 - q) * n, e.velY *= (1 - q) * n, e.burst *= je * n;
|
|
374
631
|
}
|
|
375
|
-
function
|
|
632
|
+
function Ne(e, t, n) {
|
|
633
|
+
let r = .12, i = Math.max(-r, Math.min(r, t)), a = Math.max(-r, Math.min(r, n));
|
|
634
|
+
e.velX += (i - e.velX) * q, e.velY += (a - e.velY) * q;
|
|
635
|
+
let o = Math.hypot(i, a);
|
|
636
|
+
e.burst = Math.min(1, e.burst + o * 4);
|
|
637
|
+
}
|
|
638
|
+
function Pe(e) {
|
|
376
639
|
return e.targetStrength > .001 || e.strength > .001 || Math.abs(e.x - e.targetX) > .001 || Math.abs(e.y - e.targetY) > .001;
|
|
377
640
|
}
|
|
378
641
|
//#endregion
|
|
379
642
|
//#region src/components/custom/aurora/composables/frameLoop.ts
|
|
380
|
-
function
|
|
643
|
+
function Fe(e) {
|
|
381
644
|
let { gl: t, prog: n, uniforms: r, cursor: i, getConfig: a, getReducedMotion: o } = e, s = (e) => 1 - e;
|
|
382
|
-
function c(
|
|
383
|
-
|
|
645
|
+
function c() {
|
|
646
|
+
return +!o();
|
|
384
647
|
}
|
|
385
|
-
function l() {
|
|
648
|
+
function l(e) {
|
|
649
|
+
if (t.useProgram(n), t.uniform2f(r.uCursor, i.x, s(i.y)), t.uniform1f(r.uCursorStrength, i.strength), t.uniform1f(r.uCursorRadius, i.radius), t.uniform2f(r.uCursorVelocity, i.velX, -i.velY), t.uniform1f(r.uCursorBurst, i.burst), t.uniform1f(r.uTime, e), a().interactivity?.light) {
|
|
650
|
+
let n = c(), a = e * .25, o = Math.cos(a) * .5, s = Math.sin(a) * .5, l = (i.x - .5) * 2, u = (.5 - i.y) * 2, d = i.strength * n;
|
|
651
|
+
o = o * (1 - d) + l * d, s = s * (1 - d) + u * d, t.uniform3f(r.uLightDir, o * n + (1 - n) * -.5, s * n + (1 - n) * .6, .62);
|
|
652
|
+
}
|
|
653
|
+
t.clear(t.COLOR_BUFFER_BIT), t.drawArrays(t.TRIANGLES, 0, 3);
|
|
654
|
+
}
|
|
655
|
+
function u() {
|
|
386
656
|
if (o()) return !1;
|
|
387
657
|
let e = a();
|
|
388
|
-
return e.nucleiDrift !== 0 || e.paletteDrift !== 0 || e.breathDepth !== 0 || e.warpDrift !== 0 ? !0 :
|
|
658
|
+
return e.nucleiDrift !== 0 || e.paletteDrift !== 0 || e.breathDepth !== 0 || e.warpDrift !== 0 || e.interactivity?.light && i.burst > .001 ? !0 : Pe(i);
|
|
389
659
|
}
|
|
390
660
|
return {
|
|
391
661
|
frame: (e) => {
|
|
392
|
-
|
|
662
|
+
J(i, c()), l(e);
|
|
393
663
|
},
|
|
394
|
-
needsAnimation:
|
|
664
|
+
needsAnimation: u
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
var Ie = "\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}", Le = `
|
|
668
|
+
const PI: f32 = 3.141592653589793;
|
|
669
|
+
const W_LUMA: vec3f = vec3f(0.2126, 0.7152, 0.0722);
|
|
670
|
+
const MAX_NUCLEI: u32 = 6u;
|
|
671
|
+
const MAX_STOPS: u32 = 8u;
|
|
672
|
+
|
|
673
|
+
// The std140 uniform struct — the WebGPU write path packs an AuroraConfig here.
|
|
674
|
+
struct Uniforms {
|
|
675
|
+
time: f32,
|
|
676
|
+
stopCount: i32,
|
|
677
|
+
nucleiCount: i32,
|
|
678
|
+
softmaxBeta: f32,
|
|
679
|
+
valueVariance: f32,
|
|
680
|
+
warpAmount: f32,
|
|
681
|
+
warpScale: f32,
|
|
682
|
+
warpDrift: f32,
|
|
683
|
+
warpMode: i32,
|
|
684
|
+
noiseOctaves: i32,
|
|
685
|
+
medium: i32,
|
|
686
|
+
breathDepth: f32,
|
|
687
|
+
breathPeriod: f32,
|
|
688
|
+
saturation: f32,
|
|
689
|
+
paperGrain: f32,
|
|
690
|
+
alpha: f32,
|
|
691
|
+
palette: array<vec4f, 8>, // .xyz = linear-sRGB stop
|
|
692
|
+
nucleiPos: array<vec4f, 6>, // .xy pos, .z radius, .w paletteBias
|
|
693
|
+
nucleiMod: array<vec4f, 6>, // .x valueBias, .y elong, .z angle, .w driftRadius
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
@group(0) @binding(0) var<uniform> U: Uniforms;
|
|
697
|
+
|
|
698
|
+
${f}
|
|
699
|
+
${n}
|
|
700
|
+
${i}
|
|
701
|
+
|
|
702
|
+
fn hash21(p0: vec2f) -> f32 {
|
|
703
|
+
var p = fract(p0 * vec2f(123.34, 456.21));
|
|
704
|
+
p = p + dot(p, p + 45.32);
|
|
705
|
+
return fract(p.x * p.y);
|
|
706
|
+
}
|
|
707
|
+
fn vnoise(p: vec2f) -> f32 {
|
|
708
|
+
let i = floor(p);
|
|
709
|
+
let f = fract(p);
|
|
710
|
+
let u = f * f * (3.0 - 2.0 * f);
|
|
711
|
+
let a = hash21(i);
|
|
712
|
+
let b = hash21(i + vec2f(1.0, 0.0));
|
|
713
|
+
let c = hash21(i + vec2f(0.0, 1.0));
|
|
714
|
+
let d = hash21(i + vec2f(1.0, 1.0));
|
|
715
|
+
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
|
716
|
+
}
|
|
717
|
+
fn fbm(p0: vec2f) -> f32 {
|
|
718
|
+
var v = 0.0;
|
|
719
|
+
var a = 0.5;
|
|
720
|
+
var p = p0;
|
|
721
|
+
for (var i = 0; i < 5; i = i + 1) {
|
|
722
|
+
if (i >= U.noiseOctaves) { break; }
|
|
723
|
+
v = v + a * vnoise(p);
|
|
724
|
+
p = FBM_ROT * p * 2.02;
|
|
725
|
+
a = a * 0.5;
|
|
726
|
+
}
|
|
727
|
+
return v;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
fn samplePalette(id: f32) -> vec3f {
|
|
731
|
+
// Nearest-pair OKLab interpolation across the stops (mirrors the GLSL default ramp).
|
|
732
|
+
let n = U.stopCount;
|
|
733
|
+
if (n <= 1) { return U.palette[0].xyz; }
|
|
734
|
+
let t = clamp(id, 0.0, 1.0) * f32(n - 1);
|
|
735
|
+
let i0 = i32(floor(t));
|
|
736
|
+
let i1 = min(i0 + 1, n - 1);
|
|
737
|
+
let f = t - f32(i0);
|
|
738
|
+
let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * U.palette[i0].xyz) * pow(abs(LINEAR_SRGB_TO_LMS * U.palette[i0].xyz), vec3f(1.0 / 3.0)));
|
|
739
|
+
let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * U.palette[i1].xyz) * pow(abs(LINEAR_SRGB_TO_LMS * U.palette[i1].xyz), vec3f(1.0 / 3.0)));
|
|
740
|
+
return oklabToLinearSrgb(mix(labA, labB, f));
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
fn domainWarp(p: vec2f, t: f32) -> vec2f {
|
|
744
|
+
let drift = t * U.warpDrift * 5.0;
|
|
745
|
+
let q = vec2f(
|
|
746
|
+
fbm(p * U.warpScale + vec2f(drift, 0.0)),
|
|
747
|
+
fbm(p * U.warpScale + vec2f(5.2, 1.3 + drift))
|
|
748
|
+
);
|
|
749
|
+
let r = vec2f(
|
|
750
|
+
fbm(p * U.warpScale + 4.0 * q + vec2f(1.7, 9.2)),
|
|
751
|
+
fbm(p * U.warpScale + 4.0 * q + vec2f(8.3, 2.8))
|
|
752
|
+
);
|
|
753
|
+
return p + U.warpAmount * r;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
fn nucleiField(p: vec2f, t: f32) -> vec2f {
|
|
757
|
+
// Returns (paletteId, valueMod) — softmax over the nuclei (isotropic; the WebGPU
|
|
758
|
+
// twin keeps the parity-floor isotropic Gaussian).
|
|
759
|
+
var accumId = 0.0;
|
|
760
|
+
var accumVal = 0.0;
|
|
761
|
+
var w = 0.0;
|
|
762
|
+
let n = U.nucleiCount;
|
|
763
|
+
for (var i = 0; i < 6; i = i + 1) {
|
|
764
|
+
if (i >= n) { break; }
|
|
765
|
+
let pos = U.nucleiPos[i].xy;
|
|
766
|
+
let rad = max(U.nucleiPos[i].z, 0.01);
|
|
767
|
+
let d2 = dot(p - pos, p - pos);
|
|
768
|
+
let wi = exp((-U.softmaxBeta * d2) / (rad * rad));
|
|
769
|
+
accumId = accumId + wi * U.nucleiPos[i].w;
|
|
770
|
+
accumVal = accumVal + wi * U.nucleiMod[i].x;
|
|
771
|
+
w = w + wi;
|
|
772
|
+
}
|
|
773
|
+
if (w < 1e-4) { return vec2f(0.5, 0.0); }
|
|
774
|
+
return vec2f(accumId / w, accumVal / w);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// ACES (Narkowicz) — matches the GLSL tonemap.
|
|
778
|
+
fn aces(x: vec3f) -> vec3f {
|
|
779
|
+
let a = 2.51; let b = 0.03; let c = 2.43; let d = 0.59; let e = 0.14;
|
|
780
|
+
return clamp((x * (a * x + b)) / (x * (c * x + d) + e), vec3f(0.0), vec3f(1.0));
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
@fragment
|
|
784
|
+
fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
|
|
785
|
+
let t = U.time;
|
|
786
|
+
let pw = domainWarp(uv, t);
|
|
787
|
+
let nf = nucleiField(pw, t);
|
|
788
|
+
var col = samplePalette(nf.x);
|
|
789
|
+
col = col * (1.0 + U.valueVariance * nf.y);
|
|
790
|
+
|
|
791
|
+
// Breath.
|
|
792
|
+
let breath = sin(t * 6.2831 / max(U.breathPeriod, 1.0));
|
|
793
|
+
col = col * (1.0 + U.breathDepth * breath * 0.5);
|
|
794
|
+
|
|
795
|
+
// (The WebGPU smooth pole; the painterly mediums run the W7c multi-pass passes.)
|
|
796
|
+
col = aces(col);
|
|
797
|
+
col = linearToSrgb(col); // the mandatory OETF (linear → display).
|
|
798
|
+
return vec4f(col * U.alpha, U.alpha); // premultiply AFTER the OETF (parity).
|
|
799
|
+
}`, Re = Ie + "\n" + Le;
|
|
800
|
+
//#endregion
|
|
801
|
+
//#region src/components/custom/aurora/composables/gpuRuntime.ts
|
|
802
|
+
function ze(e) {
|
|
803
|
+
let { canvas: t, getConfig: n, cursor: r, getReducedMotion: i, frozenOffset: a } = e;
|
|
804
|
+
return function({ device: e, context: o, format: s }) {
|
|
805
|
+
let l = e.createShaderModule({ code: Re }), u = e.createRenderPipeline({
|
|
806
|
+
layout: "auto",
|
|
807
|
+
vertex: {
|
|
808
|
+
module: l,
|
|
809
|
+
entryPoint: "vs_main"
|
|
810
|
+
},
|
|
811
|
+
fragment: {
|
|
812
|
+
module: l,
|
|
813
|
+
entryPoint: "fs_main",
|
|
814
|
+
targets: [{
|
|
815
|
+
format: s,
|
|
816
|
+
blend: {
|
|
817
|
+
color: {
|
|
818
|
+
srcFactor: "one",
|
|
819
|
+
dstFactor: "one-minus-src-alpha"
|
|
820
|
+
},
|
|
821
|
+
alpha: {
|
|
822
|
+
srcFactor: "one",
|
|
823
|
+
dstFactor: "one-minus-src-alpha"
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}]
|
|
827
|
+
},
|
|
828
|
+
primitive: { topology: "triangle-list" }
|
|
829
|
+
}), d = new Float32Array(De), f = e.createBuffer({
|
|
830
|
+
size: d.byteLength,
|
|
831
|
+
usage: 72
|
|
832
|
+
}), p = e.createBindGroup({
|
|
833
|
+
layout: u.getBindGroupLayout(0),
|
|
834
|
+
entries: [{
|
|
835
|
+
binding: 0,
|
|
836
|
+
resource: { buffer: f }
|
|
837
|
+
}]
|
|
838
|
+
});
|
|
839
|
+
function m() {
|
|
840
|
+
let e = c(), n = t.clientWidth || t.parentElement?.clientWidth || 1, r = t.clientHeight || t.parentElement?.clientHeight || 1;
|
|
841
|
+
t.width = Math.max(1, Math.floor(n * e)), t.height = Math.max(1, Math.floor(r * e));
|
|
842
|
+
}
|
|
843
|
+
function h(t) {
|
|
844
|
+
J(r), Oe(n(), t, d), e.queue.writeBuffer(f, 0, d);
|
|
845
|
+
let i = e.createCommandEncoder(), a = o.getCurrentTexture().createView(), s = i.beginRenderPass({ colorAttachments: [{
|
|
846
|
+
view: a,
|
|
847
|
+
clearValue: {
|
|
848
|
+
r: 0,
|
|
849
|
+
g: 0,
|
|
850
|
+
b: 0,
|
|
851
|
+
a: 0
|
|
852
|
+
},
|
|
853
|
+
loadOp: "clear",
|
|
854
|
+
storeOp: "store"
|
|
855
|
+
}] });
|
|
856
|
+
s.setPipeline(u), s.setBindGroup(0, p), s.draw(3), s.end(), e.queue.submit([i.finish()]);
|
|
857
|
+
}
|
|
858
|
+
function g() {
|
|
859
|
+
if (i()) return !1;
|
|
860
|
+
let e = n();
|
|
861
|
+
return e.nucleiDrift !== 0 || e.paletteDrift !== 0 || e.breathDepth !== 0 || e.warpDrift !== 0;
|
|
862
|
+
}
|
|
863
|
+
return {
|
|
864
|
+
frame: h,
|
|
865
|
+
shouldContinue: g,
|
|
866
|
+
resize: m,
|
|
867
|
+
time: (e) => i() ? a : e,
|
|
868
|
+
teardown: () => {
|
|
869
|
+
f.destroy();
|
|
870
|
+
}
|
|
871
|
+
};
|
|
395
872
|
};
|
|
396
873
|
}
|
|
397
874
|
//#endregion
|
|
398
875
|
//#region src/components/custom/aurora/composables/runtime.ts
|
|
399
|
-
function
|
|
876
|
+
function Be(e) {
|
|
400
877
|
return e.mode === "capture" ? !0 : e.initStrategy === "eager";
|
|
401
878
|
}
|
|
402
|
-
function
|
|
879
|
+
function Ve(e) {
|
|
403
880
|
return typeof e.preserveDrawingBuffer == "boolean" ? e.preserveDrawingBuffer : e.mode === "capture";
|
|
404
881
|
}
|
|
405
|
-
function
|
|
406
|
-
let
|
|
407
|
-
|
|
882
|
+
function Y(e, t, n = {}) {
|
|
883
|
+
let r = Ve(n), i = t, a = 3.7, o = Me(), s = null, u = n.gpuDevice ? ge(e, {
|
|
884
|
+
device: n.gpuDevice,
|
|
885
|
+
mode: n.mode === "capture" ? "capture" : "live",
|
|
886
|
+
alphaMode: "premultiplied",
|
|
887
|
+
setup: ze({
|
|
888
|
+
canvas: e,
|
|
889
|
+
getConfig: () => i,
|
|
890
|
+
cursor: o,
|
|
891
|
+
getReducedMotion: () => u.reducedMotion,
|
|
892
|
+
frozenOffset: a
|
|
893
|
+
})
|
|
894
|
+
}) : l(e, {
|
|
895
|
+
mode: n.mode === "capture" ? "capture" : "live",
|
|
408
896
|
contextAttrs: {
|
|
409
897
|
antialias: !1,
|
|
410
898
|
alpha: !0,
|
|
411
899
|
premultipliedAlpha: !0,
|
|
412
|
-
preserveDrawingBuffer:
|
|
900
|
+
preserveDrawingBuffer: r
|
|
413
901
|
},
|
|
414
902
|
setup: (t) => {
|
|
415
|
-
let { program:
|
|
416
|
-
function
|
|
417
|
-
let
|
|
418
|
-
e.width =
|
|
903
|
+
let { program: n, vs: r, fs: l, uniforms: d, geometry: f } = M(t, ce, he);
|
|
904
|
+
function p() {
|
|
905
|
+
let r = c(), i = e.clientWidth || e.parentElement?.clientWidth || 1, a = e.clientHeight || e.parentElement?.clientHeight || 1, o = Math.max(1, Math.floor(i * r)), s = Math.max(1, Math.floor(a * r));
|
|
906
|
+
e.width = o, e.height = s, t.viewport(0, 0, o, s), t.useProgram(n);
|
|
419
907
|
}
|
|
420
|
-
let
|
|
908
|
+
let m = G(t, n, d, o), h = Fe({
|
|
421
909
|
gl: t,
|
|
422
|
-
prog:
|
|
423
|
-
uniforms:
|
|
424
|
-
cursor:
|
|
425
|
-
getConfig: () =>
|
|
426
|
-
getReducedMotion: () =>
|
|
910
|
+
prog: n,
|
|
911
|
+
uniforms: d,
|
|
912
|
+
cursor: o,
|
|
913
|
+
getConfig: () => i,
|
|
914
|
+
getReducedMotion: () => u.reducedMotion
|
|
427
915
|
});
|
|
428
|
-
return t.clearColor(0, 0, 0, 0), t.disable(t.DEPTH_TEST), t.enable(t.BLEND), t.blendFunc(t.ONE, t.ONE_MINUS_SRC_ALPHA),
|
|
429
|
-
|
|
430
|
-
},
|
|
431
|
-
|
|
916
|
+
return t.clearColor(0, 0, 0, 0), t.disable(t.DEPTH_TEST), t.enable(t.BLEND), t.blendFunc(t.ONE, t.ONE_MINUS_SRC_ALPHA), s = (e) => {
|
|
917
|
+
i = e, m(e);
|
|
918
|
+
}, s(i), requestAnimationFrame(() => {
|
|
919
|
+
p(), requestAnimationFrame(p);
|
|
432
920
|
}), {
|
|
433
|
-
frame:
|
|
434
|
-
shouldContinue:
|
|
435
|
-
resize:
|
|
436
|
-
time: (e) =>
|
|
921
|
+
frame: h.frame,
|
|
922
|
+
shouldContinue: h.needsAnimation,
|
|
923
|
+
resize: p,
|
|
924
|
+
time: (e) => u.reducedMotion ? a : e,
|
|
437
925
|
teardown: () => {
|
|
438
|
-
t.deleteProgram(
|
|
926
|
+
t.deleteProgram(n), t.deleteShader(r), t.deleteShader(l), t.deleteBuffer(f.buf), t.deleteVertexArray(f.vao);
|
|
439
927
|
}
|
|
440
928
|
};
|
|
441
929
|
}
|
|
442
930
|
});
|
|
443
|
-
function
|
|
444
|
-
|
|
931
|
+
function d(e, t, n = .8) {
|
|
932
|
+
o.targetX = e, o.targetY = t, o.targetStrength = n, u.wake();
|
|
933
|
+
}
|
|
934
|
+
function f(e, t) {
|
|
935
|
+
u.reducedMotion || (Ne(o, e, t), u.wake());
|
|
445
936
|
}
|
|
446
|
-
function
|
|
447
|
-
|
|
937
|
+
function p() {
|
|
938
|
+
o.targetStrength = 0, u.wake();
|
|
448
939
|
}
|
|
449
|
-
function
|
|
450
|
-
|
|
940
|
+
function m(e) {
|
|
941
|
+
o.radius = e, u.wake();
|
|
451
942
|
}
|
|
452
|
-
function
|
|
453
|
-
|
|
943
|
+
function h(e) {
|
|
944
|
+
u.wake();
|
|
454
945
|
}
|
|
455
|
-
return
|
|
456
|
-
arm: () =>
|
|
946
|
+
return n.mode !== "capture" && Be(n) && u.arm(), {
|
|
947
|
+
arm: () => u.arm(),
|
|
457
948
|
update: (e) => {
|
|
458
|
-
|
|
949
|
+
i = e, s?.(e), u.wake();
|
|
950
|
+
},
|
|
951
|
+
setCursor: d,
|
|
952
|
+
clearCursor: p,
|
|
953
|
+
setCursorRadius: m,
|
|
954
|
+
setReducedMotion: h,
|
|
955
|
+
injectCursorVelocity: f,
|
|
956
|
+
get reducedMotion() {
|
|
957
|
+
return u.reducedMotion;
|
|
459
958
|
},
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
pause: (e = "manual") => l.suspend(e),
|
|
465
|
-
resume: (e = "manual") => l.resume(e),
|
|
466
|
-
renderAt: (e) => l.renderAt(e),
|
|
467
|
-
dispose: () => l.dispose()
|
|
959
|
+
pause: (e = "manual") => u.suspend(e),
|
|
960
|
+
resume: (e = "manual") => u.resume(e),
|
|
961
|
+
renderAt: (e) => u.renderAt(e),
|
|
962
|
+
dispose: () => u.dispose()
|
|
468
963
|
};
|
|
469
964
|
}
|
|
470
965
|
//#endregion
|
|
471
966
|
//#region src/components/custom/aurora/composables/configSource.ts
|
|
472
|
-
function
|
|
967
|
+
function X(e) {
|
|
473
968
|
return typeof e == "function" ? e : e && typeof e == "object" && "value" in e ? () => e.value : () => e;
|
|
474
969
|
}
|
|
475
970
|
//#endregion
|
|
971
|
+
//#region src/components/custom/aurora/constants/renderMode.ts
|
|
972
|
+
function Z(e) {
|
|
973
|
+
if (e !== "auto") return e;
|
|
974
|
+
if (typeof navigator > "u" || typeof window > "u") return "webgl";
|
|
975
|
+
let t = typeof navigator.hardwareConcurrency == "number" && navigator.hardwareConcurrency <= 4, n = typeof window.matchMedia == "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches, r = navigator.connection?.saveData === !0;
|
|
976
|
+
return t || n || r ? "css" : "webgl";
|
|
977
|
+
}
|
|
978
|
+
function He(e) {
|
|
979
|
+
let t = e;
|
|
980
|
+
return typeof t.isFallbackAdapter == "boolean" ? t.isFallbackAdapter : e.info?.isFallbackAdapter === !0;
|
|
981
|
+
}
|
|
982
|
+
async function Ue(e) {
|
|
983
|
+
if (Z(e) === "css") return {
|
|
984
|
+
substrate: "css",
|
|
985
|
+
device: null
|
|
986
|
+
};
|
|
987
|
+
if (typeof navigator > "u" || !navigator.gpu) return {
|
|
988
|
+
substrate: "webgl",
|
|
989
|
+
device: null
|
|
990
|
+
};
|
|
991
|
+
try {
|
|
992
|
+
let e = await navigator.gpu.requestAdapter({ powerPreference: "high-performance" });
|
|
993
|
+
return !e || He(e) ? {
|
|
994
|
+
substrate: "webgl",
|
|
995
|
+
device: null
|
|
996
|
+
} : {
|
|
997
|
+
substrate: "webgpu",
|
|
998
|
+
device: await e.requestDevice()
|
|
999
|
+
};
|
|
1000
|
+
} catch {
|
|
1001
|
+
return {
|
|
1002
|
+
substrate: "webgl",
|
|
1003
|
+
device: null
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
//#endregion
|
|
476
1008
|
//#region src/components/custom/aurora/composables/useAurora.ts
|
|
477
|
-
function
|
|
1009
|
+
function We(e) {
|
|
478
1010
|
if (typeof window > "u") return () => {};
|
|
479
1011
|
if (typeof window.requestIdleCallback == "function") {
|
|
480
1012
|
let t = window.requestIdleCallback(() => e(), { timeout: 2e3 });
|
|
@@ -492,9 +1024,9 @@ function ve(e) {
|
|
|
492
1024
|
};
|
|
493
1025
|
}
|
|
494
1026
|
function Q(e, t, n = {}, r = {}) {
|
|
495
|
-
let i =
|
|
1027
|
+
let i = X(t), a = r.renderMode === "css", o = null, s = null, c = null, l = null, u = null, d = null, f = null, p = !1, m = O(!1), h = n.mode === "capture" || n.initStrategy === "eager";
|
|
496
1028
|
function g() {
|
|
497
|
-
|
|
1029
|
+
f && o && o.setReducedMotion(f.matches);
|
|
498
1030
|
}
|
|
499
1031
|
function _(e) {
|
|
500
1032
|
let t = e instanceof Error ? e : Error(String(e));
|
|
@@ -505,22 +1037,27 @@ function Q(e, t, n = {}, r = {}) {
|
|
|
505
1037
|
Promise.reject(t);
|
|
506
1038
|
}
|
|
507
1039
|
function v() {
|
|
508
|
-
if (!(
|
|
509
|
-
|
|
1040
|
+
if (!(p || !o)) {
|
|
1041
|
+
p = !0;
|
|
510
1042
|
try {
|
|
511
1043
|
o.arm();
|
|
512
1044
|
} catch (e) {
|
|
513
1045
|
_(e);
|
|
514
1046
|
return;
|
|
515
1047
|
}
|
|
516
|
-
m.value = !0,
|
|
1048
|
+
m.value = !0, f = window.matchMedia("(prefers-reduced-motion: reduce)"), f.addEventListener("change", g), s = k(i, (e) => o?.update(e), { deep: !0 }), i().interactivity?.scroll && e.value && (l = k(ee({
|
|
1049
|
+
target: e,
|
|
1050
|
+
trackExit: !0
|
|
1051
|
+
}), (e) => {
|
|
1052
|
+
!o || o.reducedMotion || o.setCursorRadius(.2 + e * .25);
|
|
1053
|
+
}));
|
|
517
1054
|
}
|
|
518
1055
|
}
|
|
519
|
-
return
|
|
1056
|
+
return D(() => {
|
|
520
1057
|
let t = e.value;
|
|
521
1058
|
if (!t || a) return;
|
|
522
1059
|
try {
|
|
523
|
-
o =
|
|
1060
|
+
o = Y(t, i(), {
|
|
524
1061
|
initStrategy: "deferred",
|
|
525
1062
|
...n
|
|
526
1063
|
});
|
|
@@ -532,150 +1069,152 @@ function Q(e, t, n = {}, r = {}) {
|
|
|
532
1069
|
v();
|
|
533
1070
|
return;
|
|
534
1071
|
}
|
|
535
|
-
|
|
1072
|
+
d = x(e, {
|
|
536
1073
|
pause: () => o?.pause("off-screen"),
|
|
537
1074
|
resume: () => o?.resume("off-screen")
|
|
538
1075
|
}, { pauseWhenHidden: !1 });
|
|
539
|
-
let r =
|
|
540
|
-
c =
|
|
541
|
-
!
|
|
542
|
-
|
|
1076
|
+
let r = d.isIntersecting;
|
|
1077
|
+
c = k(r, (t) => {
|
|
1078
|
+
!t || p || u || (u = We(() => {
|
|
1079
|
+
u = null, r.value && Ue("webgl").then(({ substrate: t, device: a }) => {
|
|
1080
|
+
p || (t === "webgpu" && a && o && e.value && (o.dispose(), o = Y(e.value, i(), {
|
|
1081
|
+
initStrategy: "deferred",
|
|
1082
|
+
...n,
|
|
1083
|
+
gpuDevice: a
|
|
1084
|
+
})), r.value && v());
|
|
1085
|
+
}).catch(() => {
|
|
1086
|
+
r.value && v();
|
|
1087
|
+
});
|
|
543
1088
|
}));
|
|
544
1089
|
}, { immediate: !0 });
|
|
545
|
-
}),
|
|
546
|
-
|
|
1090
|
+
}), E(() => {
|
|
1091
|
+
u?.(), c?.(), s?.(), l?.(), d?.dispose(), f?.removeEventListener("change", g), o?.dispose(), o = null;
|
|
547
1092
|
}), {
|
|
548
1093
|
setCursor: (e, t, n) => o?.setCursor(e, t, n),
|
|
549
1094
|
clearCursor: () => o?.clearCursor(),
|
|
550
1095
|
setCursorRadius: (e) => o?.setCursorRadius(e),
|
|
1096
|
+
injectCursorVelocity: (e, t) => o?.injectCursorVelocity(e, t),
|
|
551
1097
|
renderAt: (e) => o?.renderAt(e),
|
|
552
1098
|
pause: () => o?.pause(),
|
|
553
1099
|
resume: () => o?.resume(),
|
|
554
|
-
isArmed:
|
|
1100
|
+
isArmed: ie(m)
|
|
555
1101
|
};
|
|
556
1102
|
}
|
|
557
1103
|
//#endregion
|
|
558
|
-
//#region src/components/custom/aurora/constants/renderMode.ts
|
|
559
|
-
function $(e) {
|
|
560
|
-
if (e !== "auto") return e;
|
|
561
|
-
if (typeof navigator > "u" || typeof window > "u") return "webgl";
|
|
562
|
-
let t = typeof navigator.hardwareConcurrency == "number" && navigator.hardwareConcurrency <= 4, n = typeof window.matchMedia == "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches, r = navigator.connection?.saveData === !0;
|
|
563
|
-
return t || n || r ? "css" : "webgl";
|
|
564
|
-
}
|
|
565
|
-
//#endregion
|
|
566
1104
|
//#region src/components/custom/aurora/Aurora.vue
|
|
567
|
-
var
|
|
1105
|
+
var Ge = /* @__PURE__ */ e(/* @__PURE__ */ te({
|
|
568
1106
|
__name: "Aurora",
|
|
569
1107
|
props: {
|
|
570
|
-
config: { default: () =>
|
|
1108
|
+
config: { default: () => h },
|
|
571
1109
|
runtimeOptions: {},
|
|
572
1110
|
onInitError: {},
|
|
573
1111
|
renderMode: { default: "auto" },
|
|
574
1112
|
opacityCeiling: { default: 1 }
|
|
575
1113
|
},
|
|
576
1114
|
setup(e, { expose: t }) {
|
|
577
|
-
let n = e, r =
|
|
1115
|
+
let n = e, r = Z(n.renderMode), i = S(() => Math.max(0, Math.min(1, n.opacityCeiling))), a = O(null), o = Q(a, () => n.config, S(() => ({
|
|
578
1116
|
...n.runtimeOptions ?? {},
|
|
579
1117
|
...n.onInitError ? { onInitError: n.onInitError } : {}
|
|
580
|
-
})).value, { renderMode: r }), s =
|
|
1118
|
+
})).value, { renderMode: r }), s = S(() => P(n.config.palette));
|
|
581
1119
|
return t({
|
|
582
1120
|
config: n.config,
|
|
583
1121
|
canvasRef: a,
|
|
584
1122
|
setCursor: o.setCursor,
|
|
585
1123
|
clearCursor: o.clearCursor,
|
|
586
1124
|
setCursorRadius: o.setCursorRadius,
|
|
1125
|
+
injectCursorVelocity: o.injectCursorVelocity,
|
|
587
1126
|
renderAt: o.renderAt,
|
|
588
1127
|
pause: o.pause,
|
|
589
1128
|
resume: o.resume,
|
|
590
1129
|
isArmed: o.isArmed
|
|
591
|
-
}), (e, t) => (
|
|
1130
|
+
}), (e, t) => (re(), C("div", {
|
|
592
1131
|
class: "aurora-root block h-full w-full overflow-hidden",
|
|
593
|
-
style:
|
|
594
|
-
}, [
|
|
1132
|
+
style: T({ "--aurora-opacity-ceiling": i.value })
|
|
1133
|
+
}, [w("div", {
|
|
595
1134
|
class: "aurora-placeholder h-full w-full",
|
|
596
1135
|
"aria-hidden": "true",
|
|
597
|
-
style:
|
|
598
|
-
}, null, 4),
|
|
1136
|
+
style: T({ backgroundImage: s.value })
|
|
1137
|
+
}, null, 4), w("canvas", {
|
|
599
1138
|
ref_key: "canvasRef",
|
|
600
1139
|
ref: a,
|
|
601
1140
|
"aria-hidden": "true",
|
|
602
|
-
class:
|
|
1141
|
+
class: ne(["aurora-canvas block h-full w-full", { "aurora-canvas--armed": ae(o).isArmed.value }])
|
|
603
1142
|
}, null, 2)], 4));
|
|
604
1143
|
}
|
|
605
|
-
}), [["__scopeId", "data-v-
|
|
1144
|
+
}), [["__scopeId", "data-v-59e84382"]]);
|
|
606
1145
|
//#endregion
|
|
607
1146
|
//#region src/components/custom/aurora/composables/useCursorInteraction.ts
|
|
608
|
-
function
|
|
609
|
-
let r =
|
|
610
|
-
function
|
|
1147
|
+
function Ke(e, t, n) {
|
|
1148
|
+
let r = X(t), i = n.hitFraction ?? .35, a = null, o = null, s = null, c = null;
|
|
1149
|
+
function l(e, t) {
|
|
611
1150
|
let n = t.getBoundingClientRect();
|
|
612
1151
|
return {
|
|
613
1152
|
x: (e.clientX - n.left) / Math.max(n.width, 1),
|
|
614
1153
|
y: (e.clientY - n.top) / Math.max(n.height, 1)
|
|
615
1154
|
};
|
|
616
1155
|
}
|
|
617
|
-
function
|
|
1156
|
+
function u(e, t, n) {
|
|
618
1157
|
let r = t - e.x, i = n - e.y, a = (e.angle ?? 0) * Math.PI / 180, o = Math.cos(a), s = Math.sin(a), c = o * r + s * i, l = -s * r + o * i, u = c / Math.max(e.elongation ?? 1, .01), d = l;
|
|
619
1158
|
return u * u + d * d;
|
|
620
1159
|
}
|
|
621
|
-
function
|
|
1160
|
+
function d(e, t) {
|
|
622
1161
|
let n = null, a = Infinity;
|
|
623
1162
|
for (let o = 0; o < r().nuclei.length; o++) {
|
|
624
|
-
let s = r().nuclei[o],
|
|
625
|
-
|
|
1163
|
+
let s = r().nuclei[o], c = u(s, e, t), l = s.radius * i;
|
|
1164
|
+
c < l * l && c < a && (a = c, n = o);
|
|
626
1165
|
}
|
|
627
1166
|
return n;
|
|
628
1167
|
}
|
|
629
|
-
function
|
|
1168
|
+
function f(e, t) {
|
|
630
1169
|
let n = 0, i = 0;
|
|
631
1170
|
for (let a of r().nuclei) {
|
|
632
|
-
let o =
|
|
633
|
-
n +=
|
|
1171
|
+
let o = u(a, e, t), s = Math.max(a.radius, .01), c = Math.exp(-r().softmaxBeta * o / (s * s));
|
|
1172
|
+
n += c * a.paletteBias, i += c;
|
|
634
1173
|
}
|
|
635
1174
|
return i > 1e-4 ? n / i : .5;
|
|
636
1175
|
}
|
|
637
|
-
function
|
|
1176
|
+
function p() {
|
|
638
1177
|
if (r().nuclei.length === 0) return .5;
|
|
639
1178
|
let e = r().nuclei.map((e) => e.radius).sort((e, t) => e - t);
|
|
640
1179
|
return e[Math.floor(e.length / 2)];
|
|
641
1180
|
}
|
|
642
|
-
function
|
|
1181
|
+
function m(e, t) {
|
|
643
1182
|
return {
|
|
644
1183
|
x: e,
|
|
645
1184
|
y: t,
|
|
646
|
-
radius:
|
|
647
|
-
paletteBias:
|
|
1185
|
+
radius: p(),
|
|
1186
|
+
paletteBias: f(e, t),
|
|
648
1187
|
valueBias: 0,
|
|
649
1188
|
driftRadius: .012,
|
|
650
1189
|
driftPhase: Math.random() * Math.PI * 2
|
|
651
1190
|
};
|
|
652
1191
|
}
|
|
653
|
-
function
|
|
1192
|
+
function h(t) {
|
|
654
1193
|
let i = e.value;
|
|
655
1194
|
if (!i) return;
|
|
656
|
-
let { x: o, y:
|
|
657
|
-
a !== null && (r().nuclei[a].x = Math.max(0, Math.min(1, o)), r().nuclei[a].y = Math.max(0, Math.min(1,
|
|
1195
|
+
let { x: o, y: u } = l(t, i);
|
|
1196
|
+
a !== null && (r().nuclei[a].x = Math.max(0, Math.min(1, o)), r().nuclei[a].y = Math.max(0, Math.min(1, u))), n.setCursor(o, u, 1), n.injectVelocity && s !== null && c !== null && n.injectVelocity(o - s, u - c), s = o, c = u;
|
|
658
1197
|
}
|
|
659
|
-
function
|
|
660
|
-
a === null && n.clearCursor();
|
|
1198
|
+
function g() {
|
|
1199
|
+
s = null, c = null, a === null && n.clearCursor();
|
|
661
1200
|
}
|
|
662
|
-
function
|
|
1201
|
+
function _(t) {
|
|
663
1202
|
let n = e.value;
|
|
664
1203
|
if (!n) return;
|
|
665
|
-
let { x: i, y:
|
|
666
|
-
if (t.altKey &&
|
|
1204
|
+
let { x: i, y: s } = l(t, n), c = d(i, s);
|
|
1205
|
+
if (t.altKey && c === null) {
|
|
667
1206
|
if (r().nuclei.length >= 6) return;
|
|
668
|
-
r().nuclei.push(
|
|
1207
|
+
r().nuclei.push(m(i, s)), t.preventDefault();
|
|
669
1208
|
return;
|
|
670
1209
|
}
|
|
671
|
-
if ((t.shiftKey || t.button === 2) &&
|
|
1210
|
+
if ((t.shiftKey || t.button === 2) && c !== null) {
|
|
672
1211
|
if (r().nuclei.length <= 1) return;
|
|
673
|
-
r().nuclei.splice(
|
|
1212
|
+
r().nuclei.splice(c, 1), t.preventDefault();
|
|
674
1213
|
return;
|
|
675
1214
|
}
|
|
676
|
-
|
|
1215
|
+
c !== null && t.button === 0 && (a = c, o = t.pointerId, n.setPointerCapture(t.pointerId), t.preventDefault());
|
|
677
1216
|
}
|
|
678
|
-
function
|
|
1217
|
+
function v(t) {
|
|
679
1218
|
let n = e.value;
|
|
680
1219
|
if (n && o !== null && t.pointerId === o) {
|
|
681
1220
|
try {
|
|
@@ -684,22 +1223,124 @@ function be(e, t, n) {
|
|
|
684
1223
|
o = null, a = null;
|
|
685
1224
|
}
|
|
686
1225
|
}
|
|
687
|
-
function
|
|
1226
|
+
function y(t) {
|
|
688
1227
|
let n = e.value;
|
|
689
1228
|
if (!n) return;
|
|
690
1229
|
let i = {
|
|
691
1230
|
clientX: t.clientX,
|
|
692
1231
|
clientY: t.clientY
|
|
693
|
-
}, a = n.getBoundingClientRect(), o =
|
|
1232
|
+
}, a = n.getBoundingClientRect(), o = d((i.clientX - a.left) / Math.max(a.width, 1), (i.clientY - a.top) / Math.max(a.height, 1));
|
|
694
1233
|
o !== null && r().nuclei.length > 1 && (r().nuclei.splice(o, 1), t.preventDefault());
|
|
695
1234
|
}
|
|
696
|
-
return
|
|
1235
|
+
return D(() => {
|
|
697
1236
|
let t = e.value;
|
|
698
|
-
t && (t.addEventListener("pointermove",
|
|
699
|
-
}),
|
|
1237
|
+
t && (t.addEventListener("pointermove", h), t.addEventListener("pointerleave", g), t.addEventListener("pointerdown", _), t.addEventListener("pointerup", v), t.addEventListener("pointercancel", v), t.addEventListener("contextmenu", y));
|
|
1238
|
+
}), E(() => {
|
|
700
1239
|
let t = e.value;
|
|
701
|
-
t && (t.removeEventListener("pointermove",
|
|
1240
|
+
t && (t.removeEventListener("pointermove", h), t.removeEventListener("pointerleave", g), t.removeEventListener("pointerdown", _), t.removeEventListener("pointerup", v), t.removeEventListener("pointercancel", v), t.removeEventListener("contextmenu", y));
|
|
702
1241
|
}), { isDragging: () => a };
|
|
703
1242
|
}
|
|
704
1243
|
//#endregion
|
|
705
|
-
|
|
1244
|
+
//#region src/components/custom/aurora/composables/atoms.ts
|
|
1245
|
+
var $ = {}, qe = {
|
|
1246
|
+
calm: {
|
|
1247
|
+
saturation: .85,
|
|
1248
|
+
warpAmount: .4,
|
|
1249
|
+
valueVariance: .06,
|
|
1250
|
+
breathDepth: .03
|
|
1251
|
+
},
|
|
1252
|
+
balanced: {
|
|
1253
|
+
saturation: 1,
|
|
1254
|
+
warpAmount: .5,
|
|
1255
|
+
valueVariance: .08,
|
|
1256
|
+
breathDepth: .05
|
|
1257
|
+
},
|
|
1258
|
+
vivid: {
|
|
1259
|
+
saturation: 1.2,
|
|
1260
|
+
warpAmount: .58,
|
|
1261
|
+
valueVariance: .14,
|
|
1262
|
+
breathDepth: .08
|
|
1263
|
+
}
|
|
1264
|
+
}, Je = {
|
|
1265
|
+
still: {
|
|
1266
|
+
nucleiDrift: 0,
|
|
1267
|
+
paletteDrift: 0,
|
|
1268
|
+
warpDrift: 0,
|
|
1269
|
+
breathDepth: 0
|
|
1270
|
+
},
|
|
1271
|
+
breathing: {
|
|
1272
|
+
nucleiDrift: 0,
|
|
1273
|
+
paletteDrift: 0,
|
|
1274
|
+
warpDrift: 0,
|
|
1275
|
+
breathDepth: .05
|
|
1276
|
+
},
|
|
1277
|
+
drifting: {
|
|
1278
|
+
nucleiDrift: .015,
|
|
1279
|
+
paletteDrift: .015,
|
|
1280
|
+
warpDrift: .008,
|
|
1281
|
+
breathDepth: .05
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
function Ye(e) {
|
|
1285
|
+
let t = [.33, .67], n = [
|
|
1286
|
+
[t[0], t[0]],
|
|
1287
|
+
[t[1], t[1]],
|
|
1288
|
+
[t[1], t[0]],
|
|
1289
|
+
[t[0], t[1]],
|
|
1290
|
+
[.5, .5],
|
|
1291
|
+
[.5, t[0]]
|
|
1292
|
+
], r = Math.max(1, Math.min(6, n.length, Math.round(e)));
|
|
1293
|
+
return Array.from({ length: r }, (e, t) => {
|
|
1294
|
+
let [i, a] = n[t];
|
|
1295
|
+
return {
|
|
1296
|
+
x: i,
|
|
1297
|
+
y: a,
|
|
1298
|
+
radius: .5,
|
|
1299
|
+
paletteBias: r === 1 ? 0 : t / (r - 1),
|
|
1300
|
+
valueBias: (t % 2 == 0 ? 1 : -1) * .05,
|
|
1301
|
+
driftRadius: .045,
|
|
1302
|
+
driftPhase: t * 2.4 % (Math.PI * 2)
|
|
1303
|
+
};
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
function Xe(e, n, r) {
|
|
1307
|
+
let i = t(r, 0, 1);
|
|
1308
|
+
switch (n) {
|
|
1309
|
+
case "watercolor":
|
|
1310
|
+
e.wetEdge = i, e.granulation = i * .8;
|
|
1311
|
+
break;
|
|
1312
|
+
case "pastel":
|
|
1313
|
+
e.strokeAmount = i, e.canvasGrain = i * .05;
|
|
1314
|
+
break;
|
|
1315
|
+
case "oil":
|
|
1316
|
+
case "vangogh":
|
|
1317
|
+
e.strokeAmount = i, e.impasto = i, e.canvasGrain = i * .06;
|
|
1318
|
+
break;
|
|
1319
|
+
case "oil-pastel":
|
|
1320
|
+
e.strokeAmount = i, e.canvasGrain = i * .05;
|
|
1321
|
+
break;
|
|
1322
|
+
default: break;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
function Ze(e = $) {
|
|
1326
|
+
let n = {
|
|
1327
|
+
...h,
|
|
1328
|
+
palette: h.palette.map((e) => ({ ...e })),
|
|
1329
|
+
nuclei: h.nuclei.map((e) => ({ ...e })),
|
|
1330
|
+
flow: { ...h.flow }
|
|
1331
|
+
};
|
|
1332
|
+
if (e.seed !== void 0 && (n.palette = L(e.seed, {
|
|
1333
|
+
harmony: e.harmony ?? "analogous",
|
|
1334
|
+
stopCount: 4
|
|
1335
|
+
})), e.mood !== void 0) {
|
|
1336
|
+
let r = qe[e.mood];
|
|
1337
|
+
n.saturation = t(r.saturation, .6, 1.3), n.warpAmount = t(r.warpAmount, 0, .6), n.valueVariance = t(r.valueVariance, 0, .3), n.breathDepth = t(r.breathDepth, 0, .15);
|
|
1338
|
+
}
|
|
1339
|
+
if (e.medium !== void 0 && (n.medium = e.medium, (e.medium === "vangogh" || e.medium === "oil-pastel") && (n.strokeOrient = "tensor")), e.textureAmount !== void 0 && Xe(n, n.medium, e.textureAmount), e.motion !== void 0) {
|
|
1340
|
+
let r = Je[e.motion];
|
|
1341
|
+
n.nucleiDrift = t(r.nucleiDrift, 0, .05), n.paletteDrift = t(r.paletteDrift, 0, .04), n.warpDrift = t(r.warpDrift, 0, .015), n.breathDepth = t(e.motion === "still" ? 0 : Math.max(n.breathDepth, r.breathDepth), 0, .15);
|
|
1342
|
+
}
|
|
1343
|
+
return e.zones !== void 0 && (n.nuclei = Ye(e.zones)), e.interactivity !== void 0 && (n.interactivity = { ...e.interactivity }), n;
|
|
1344
|
+
}
|
|
1345
|
+
//#endregion
|
|
1346
|
+
export { Ge as Aurora, $ as DEFAULT_ATOMS, h as DEFAULT_AURORA_CONFIG, p as MAX_NUCLEI, m as MAX_STOPS, Y as createAurora, b as cssToOklch, L as deriveAurora, Se as deriveScene, N as flattenPalette, F as hexToOklchStop, y as oklchStopToHex, _ as oklchToLinear, P as paletteToCssGradient, Ze as resolveAtoms, Z as resolveRenderMode, Q as useAurora, Ke as useCursorInteraction };
|