@mkbabb/glass-ui 4.1.0 → 4.2.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-8whJrLWt.js → CardAction-DlVP2evz.js} +58 -54
- package/dist/{CommandShortcut-D3yxgHNn.js → CommandShortcut-By-VQcLO.js} +3 -3
- package/dist/{ConfiguratorRow-CaP7U3ZA.js → ConfiguratorRow-YMXdINHx.js} +3 -3
- package/dist/{ContextMenuContent-B3ZsheLV.js → ContextMenuContent-CMWn6huB.js} +1 -1
- package/dist/{ContextMenuSubContent-DDRID3qC.js → ContextMenuSubContent-DvwWCUc0.js} +1 -1
- package/dist/{DataTable-Bc9kUtu_.js → DataTable-C4tG3eIJ.js} +4 -4
- package/dist/DialogContent-D8U9ZfWS.js +121 -0
- package/dist/{DialogScrollContent-TTuovQst.js → DialogScrollContent-BMekN7gq.js} +1 -1
- package/dist/{FadingScroll-DLRbH5Xr.js → FadingScroll-P8B30rAD.js} +1 -1
- package/dist/{HoverCardContent-Bel1gdLZ.js → HoverCardContent-DAmGYglo.js} +1 -1
- package/dist/{HoverPopover-BcIxvyqZ.js → HoverPopover-DYpBOzzF.js} +1 -1
- package/dist/{IconChip-DhbdGdFQ.js → IconChip-CQa7XZYc.js} +7 -5
- package/dist/{IconTooltip-B0zimd2H.js → IconTooltip-_df8M3xm.js} +1 -1
- package/dist/{Input-Cg9DF62r.js → Input-CyU4_hIC.js} +2 -2
- package/dist/{MetricBadge-RHdBP72w.js → MetricBadge-DQt_dGah.js} +1 -1
- package/dist/{Notification-CphK7AAm.js → Notification-BlSPJhtZ.js} +1 -1
- package/dist/{NumberFieldContent-D8cXeyO4.js → NumberFieldContent-Z3VxGu_6.js} +3 -3
- package/dist/PagerDots-CYlqVFd4.js +196 -0
- package/dist/PaperBackdrop-DYWgrnh8.js +42 -0
- package/dist/{PopoverContent-DL7E6vJP.js → PopoverContent-BuKyHZSz.js} +2 -2
- package/dist/{Progress-CnqB7gLm.js → Progress-C9SsdwIZ.js} +1 -1
- package/dist/{ScrollingText-CTZbA_cG.js → ScrollingText-DY3W1Jfh.js} +2 -2
- package/dist/{SelectScrollDownButton-DAKCBEdW.js → SelectScrollDownButton-3mFHX_R-.js} +15 -12
- package/dist/{Switch-B8RscmRu.js → Switch-uf-UKPA2.js} +1 -1
- package/dist/{Toaster-D9qIHjBI.js → Toaster-ozrsR4mP.js} +1 -1
- package/dist/{ToggleGroupItem-BLtr3TJq.js → ToggleGroupItem-BpzaVzNm.js} +4 -4
- package/dist/{TooltipProvider-BJ_Y5EGX.js → TooltipProvider-QQVM_80r.js} +1 -1
- package/dist/animated-digit.js +2 -2
- package/dist/api/index.d.ts +1 -0
- package/dist/api/types-extra.d.ts +3 -1
- package/dist/api.js +2 -2
- package/dist/aurora.js +163 -131
- package/dist/badge-Bt14yURi.js +72 -0
- package/dist/badge.js +1 -1
- package/dist/border-progress.js +1 -1
- package/dist/button-D1W9PrzI.js +130 -0
- package/dist/button.js +1 -1
- package/dist/canvas.js +1 -1
- package/dist/card.js +1 -1
- package/dist/carousel.js +227 -98
- package/dist/chipVariants-BJQ0I2kB.js +18 -0
- package/dist/collapsible.js +1 -1
- package/dist/color.js +1 -1
- package/dist/command.js +1 -1
- package/dist/components/custom/aurora/composables/glSetup.d.ts +1 -1
- package/dist/components/custom/aurora/composables/uniformBridgeWGPU.d.ts +1 -1
- package/dist/components/custom/aurora/constants/presets.d.ts +29 -0
- package/dist/components/custom/aurora/constants/shaders/aurora.wgsl.d.ts +1 -1
- package/dist/components/custom/aurora/index.d.ts +1 -1
- package/dist/components/custom/concentric/Concentric.vue.d.ts +15 -12
- package/dist/components/custom/concentric/composables/concentricGLSetup.d.ts +8 -3
- package/dist/components/custom/concentric/composables/concentricWGPUSetup.d.ts +8 -4
- package/dist/components/custom/concentric/composables/levelField.d.ts +50 -0
- package/dist/components/custom/concentric/composables/uniformBridgeWGPU.d.ts +7 -4
- package/dist/components/custom/concentric/composables/useConcentric.d.ts +3 -3
- package/dist/components/custom/concentric/constants.d.ts +62 -41
- package/dist/components/custom/concentric/index.d.ts +2 -2
- package/dist/components/custom/concentric/shaders/concentric.glsl.d.ts +2 -2
- package/dist/components/custom/concentric/shaders/concentric.wgsl.d.ts +1 -1
- package/dist/components/custom/configurator/Configurator.vue.d.ts +23 -2
- package/dist/components/custom/configurator/index.d.ts +1 -1
- package/dist/components/custom/dock/DockDropdownTrigger.vue.d.ts +4 -3
- package/dist/components/custom/dock/DockIconButton.vue.d.ts +10 -0
- package/dist/components/custom/dock/DockPopoverTrigger.vue.d.ts +19 -0
- package/dist/components/custom/dock/DockStack.vue.d.ts +35 -22
- package/dist/components/custom/dock/GlassDock.vue.d.ts +19 -3
- package/dist/components/custom/dock/composables/dockMorphContext.d.ts +18 -32
- package/dist/components/custom/dock/composables/dockMorphMeasure.d.ts +76 -145
- package/dist/components/custom/dock/composables/index.d.ts +1 -0
- package/dist/components/custom/dock/composables/railProjection.d.ts +45 -0
- package/dist/components/custom/dock/composables/useDockContextSilhouette.d.ts +192 -0
- package/dist/components/custom/dock/composables/useDockFission.d.ts +158 -0
- package/dist/components/custom/dock/composables/useDockItemDrag.d.ts +18 -0
- package/dist/components/custom/dock/composables/useDockShellProps.d.ts +38 -0
- package/dist/components/custom/dock/constants.d.ts +14 -3
- package/dist/components/custom/dock/index.d.ts +3 -0
- package/dist/components/custom/dot-flow-field/composables/flowField.d.ts +21 -0
- package/dist/components/custom/dot-flow-field/composables/flowGLProgram.d.ts +8 -0
- package/dist/components/custom/dot-flow-field/composables/flowSetupGL.d.ts +9 -0
- package/dist/components/custom/dot-flow-field/composables/flowSetupGLFlow.d.ts +12 -0
- package/dist/components/custom/dot-flow-field/composables/flowSetupWGPU.d.ts +7 -0
- package/dist/components/custom/dot-flow-field/composables/uniformBridgeWGPU.d.ts +28 -2
- package/dist/components/custom/dot-flow-field/composables/useFlowParticles.d.ts +18 -17
- package/dist/components/custom/dot-flow-field/constants.d.ts +51 -0
- package/dist/components/custom/dot-flow-field/index.d.ts +1 -1
- package/dist/components/custom/dot-flow-field/shaders/flow-field.compute.wgsl.d.ts +1 -1
- package/dist/components/custom/dot-flow-field/shaders/flow-field.glsl.d.ts +29 -0
- package/dist/components/custom/dot-flow-field/shaders/flow-field.render.wgsl.d.ts +2 -1
- package/dist/components/custom/dot-matrix/composables/uniformBridgeWGPU.d.ts +10 -1
- package/dist/components/custom/dot-matrix/composables/useDotSphere.d.ts +7 -0
- package/dist/components/custom/dot-matrix/constants.d.ts +14 -0
- package/dist/components/custom/dot-matrix/index.d.ts +1 -1
- package/dist/components/custom/dot-matrix/shaders/dot-matrix.glsl.d.ts +2 -2
- package/dist/components/custom/dot-matrix/shaders/dot-matrix.wgsl.d.ts +1 -1
- package/dist/components/custom/fourier-field/FourierField.vue.d.ts +4 -0
- package/dist/components/custom/fourier-field/composables/fourierFieldGLSetup.d.ts +9 -0
- package/dist/components/custom/fourier-field/composables/fourierFieldWGPUSetup.d.ts +11 -0
- package/dist/components/custom/fourier-field/composables/uniformBridgeWGPU.d.ts +16 -1
- package/dist/components/custom/fourier-field/composables/useFourierField.d.ts +22 -1
- package/dist/components/custom/fourier-field/constants.d.ts +53 -0
- package/dist/components/custom/fourier-field/shaders/fourier-field.glsl.d.ts +1 -1
- package/dist/components/custom/fourier-field/shaders/fourier-field.render.wgsl.d.ts +1 -1
- package/dist/components/custom/goo-blob/composables/easing.d.ts +1 -0
- package/dist/components/custom/goo-blob/composables/useBlobSatellites.d.ts +1 -0
- package/dist/components/custom/goo-blob/constants.d.ts +26 -1
- package/dist/components/custom/goo-blob/shaders/metaball-uniforms.glsl.d.ts +1 -1
- package/dist/components/custom/goo-blob/shaders/metaball.wgsl.d.ts +1 -1
- package/dist/components/custom/goo-blob/types.d.ts +50 -1
- package/dist/components/custom/goo-dot-matrix/composables/gooDotSetup.d.ts +32 -3
- package/dist/components/custom/goo-dot-matrix/composables/uniformBridgeWGPU.d.ts +14 -4
- package/dist/components/custom/goo-dot-matrix/constants.d.ts +55 -16
- package/dist/components/custom/goo-dot-matrix/index.d.ts +1 -1
- package/dist/components/custom/goo-dot-matrix/shaders/goo-dot.frag.d.ts +2 -0
- package/dist/components/custom/goo-dot-matrix/shaders/goo-dot.wgsl.d.ts +1 -0
- package/dist/components/custom/goo-filter/GooFilter.vue.d.ts +22 -0
- package/dist/components/custom/goo-filter/index.d.ts +1 -0
- package/dist/components/custom/handmark/brush.d.ts +17 -17
- package/dist/components/custom/handmark/constants.d.ts +28 -8
- package/dist/components/custom/handmark/geometry.d.ts +34 -1
- package/dist/components/custom/handmark/index.d.ts +1 -1
- package/dist/components/custom/icon-chip/types.d.ts +10 -0
- package/dist/components/custom/paper-backdrop/PaperBackdrop.vue.d.ts +18 -0
- package/dist/components/custom/paper-grid/composables/paperGrid.d.ts +44 -29
- package/dist/components/custom/paper-grid/composables/paperGridGLSetup.d.ts +2 -0
- package/dist/components/custom/paper-grid/composables/paperGridWGPUSetup.d.ts +3 -1
- package/dist/components/custom/paper-grid/composables/uniformBridgeWGPU.d.ts +2 -2
- package/dist/components/custom/paper-grid/constants.d.ts +44 -6
- package/dist/components/custom/paper-grid/index.d.ts +2 -2
- package/dist/components/custom/paper-grid/shaders/paper-grid.glsl.d.ts +1 -1
- package/dist/components/custom/paper-grid/shaders/paper-grid.wgsl.d.ts +1 -1
- package/dist/components/custom/selectable-chip/chipVariants.d.ts +39 -0
- package/dist/components/custom/selectable-chip/selectableChipVariants.d.ts +12 -20
- package/dist/components/custom/tabs/constants.d.ts +16 -8
- package/dist/components/custom/timeline/ScrubberTimeline.vue.d.ts +16 -0
- package/dist/components/custom/timeline/geometry.d.ts +0 -8
- package/dist/components/custom/toggle-chip/index.d.ts +15 -4
- package/dist/components/custom/watercolor-dot/WatercolorDot.vue.d.ts +12 -10
- package/dist/components/ui/_shared/useSurfaceAxis.d.ts +17 -3
- package/dist/components/ui/badge/Badge.vue.d.ts +14 -0
- package/dist/components/ui/badge/index.d.ts +1 -0
- package/dist/components/ui/button/Button.vue.d.ts +1 -0
- package/dist/components/ui/card/Card.vue.d.ts +1 -1
- package/dist/components/ui/card/ScrollCard.vue.d.ts +1 -1
- package/dist/components/ui/carousel/CarouselContent.vue.d.ts +2 -4
- package/dist/components/ui/dialog/DialogContent.vue.d.ts +9 -0
- package/dist/components/ui/drawer/Drawer.vue.d.ts +33 -14
- package/dist/components/ui/drawer/constants.d.ts +8 -6
- package/dist/components/ui/drawer/index.d.ts +12 -2
- package/dist/components/ui/select/SelectContent.vue.d.ts +9 -0
- package/dist/components/ui/slider/Slider.vue.d.ts +9 -0
- package/dist/composables/dom/index.d.ts +1 -0
- package/dist/composables/dom/useDragVelocity.d.ts +46 -0
- package/dist/composables/glass/wave/index.d.ts +4 -0
- package/dist/composables/glass/wave/waveField.d.ts +115 -0
- package/dist/composables/glass/wave/waveField.glsl.d.ts +1 -0
- package/dist/composables/glass/wave/waveField.wgsl.d.ts +1 -0
- package/dist/composables/glass/webgl/createCanvasLifecycle.d.ts +95 -2
- package/dist/composables/glass/webgl/useWebGLCanvas.d.ts +40 -2
- package/dist/composables/glass/webgpu/index.d.ts +1 -0
- package/dist/composables/glass/webgpu/useGpuSubstrate.d.ts +37 -0
- package/dist/composables/glass/webgpu/useWebGPUCanvas.d.ts +49 -2
- package/dist/composables/glass/webgpu/webgpuDevice.d.ts +1 -1
- package/dist/composables/motion/core/index.d.ts +4 -0
- package/dist/composables/motion/core/useHaptic.d.ts +48 -0
- package/dist/composables/motion/core/writeVelocityWeight.d.ts +31 -0
- package/dist/composables/motion/index.d.ts +2 -0
- package/dist/composables/motion/springPresets.d.ts +16 -17
- package/dist/composables/motion/suite.d.ts +2 -2
- package/dist/composables/motion/useBloomUp.d.ts +97 -0
- package/dist/composables/motion/useCelebrationBurst.d.ts +50 -0
- package/dist/composables/motion/useDockCtaReceive.d.ts +10 -28
- package/dist/composables/motion/useGooMorph.d.ts +72 -0
- package/dist/composables/motion/useLiquidFlex.d.ts +18 -0
- package/dist/composables/motion/useLiquidMorph.d.ts +108 -0
- package/dist/composables/motion/useLiquidPress.d.ts +12 -0
- package/dist/composables/motion/useLiquidReveal.d.ts +7 -4
- package/dist/composables/motion/useMorphField.d.ts +157 -0
- package/dist/composables/motion/useScrollPin.d.ts +49 -0
- package/dist/composables/motion/useScrollScene.d.ts +67 -0
- package/dist/concentric.js +532 -563
- package/dist/configurator.js +2 -2
- package/dist/confirm-dialog.js +3 -3
- package/dist/constellation.js +4 -4
- package/dist/context-menu.js +2 -2
- package/dist/controls.js +2 -2
- package/dist/createCanvasLifecycle-CuCrGRfq.js +173 -0
- package/dist/dark.js +1 -1
- package/dist/data-table.js +1 -1
- package/dist/deck.js +1 -1
- package/dist/dialog.js +3 -3
- package/dist/dock.js +1163 -618
- package/dist/dockContext-gFVS2gMg.js +18 -0
- package/dist/dom.js +7 -6
- package/dist/dot-flow-field.js +1114 -523
- package/dist/dot-matrix.js +348 -204
- package/dist/drawer.js +106 -96
- package/dist/{dropdown-menu-aJmV5SqE.js → dropdown-menu-CCfcCQ68.js} +3 -3
- package/dist/dropdown-menu.js +1 -1
- package/dist/easing.js +3 -3
- package/dist/expandable-container.js +2 -2
- package/dist/fading-scroll.js +1 -1
- package/dist/forms.js +6 -6
- package/dist/fourier-field.js +441 -197
- package/dist/fourier-math.js +1 -1
- package/dist/glass-panel.js +2 -2
- package/dist/glass-ui.css +1 -1
- package/dist/glass-ui.js +139 -135
- package/dist/goo-blob.js +11 -9
- package/dist/goo-dot-matrix.js +418 -198
- package/dist/handmark.js +137 -94
- package/dist/header-ribbon.js +1 -1
- package/dist/hover-card.js +1 -1
- package/dist/hover-popover.js +1 -1
- package/dist/icon-chip.js +1 -1
- package/dist/icon-tooltip.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/instrument-chassis.js +1 -1
- package/dist/keyboard.js +1 -1
- package/dist/label.js +1 -1
- package/dist/labeled-field.js +7 -7
- package/dist/metric-badge.js +1 -1
- package/dist/metric-cell.js +1 -1
- package/dist/metric-stack.js +1 -1
- package/dist/motion-core.js +42 -39
- package/dist/motion-curves.js +20 -14
- package/dist/motion.js +226 -26
- package/dist/notification.js +1 -1
- package/dist/number-field.js +1 -1
- package/dist/pager-dots.js +1 -1
- package/dist/paper-backdrop.js +1 -1
- package/dist/paper-grid.js +315 -342
- package/dist/paperGrid-CXCSMbZ1.js +191 -0
- package/dist/popover.js +1 -1
- package/dist/{presets-CvOUqtdY.js → presets-QvYgnFIZ.js} +13 -12
- package/dist/progress.js +1 -1
- package/dist/pulse.js +1 -1
- package/dist/reactive.js +2 -2
- package/dist/{resolveCanvasColor-yPyIpJt9.js → resolveCanvasColor-DsSJk06I.js} +5 -5
- package/dist/scrolling-text.js +1 -1
- package/dist/search.js +7 -7
- package/dist/select.js +3 -3
- package/dist/selectable-chip.js +18 -35
- package/dist/separator.js +1 -1
- package/dist/{sheet-B2flBfLH.js → sheet-DUCIoOe0.js} +3 -3
- package/dist/sheet.js +1 -1
- package/dist/sidebar.js +1 -1
- package/dist/slider-CcICPe3u.js +149 -0
- package/dist/slider.js +1 -1
- package/dist/sortable-list.js +3 -3
- package/dist/{spectrum-walk-BVzA7Biz.js → spectrum-walk-cBNSx_zm.js} +1 -1
- package/dist/springPresets-DfoQ6XRY.js +64 -0
- package/dist/stacked-icons.js +54 -39
- package/dist/styles/cards.css +202 -2
- package/dist/styles/components.css +1 -3
- package/dist/styles/configurator.css +172 -7
- package/dist/styles/critical-partition.mjs +35 -2
- package/dist/styles/deferred.css +2 -0
- package/dist/styles/dock/adaptive-legibility.css +119 -0
- package/dist/styles/dock/density.css +18 -10
- package/dist/styles/dock/fission-bridge.css +552 -0
- package/dist/styles/dock/layers.css +88 -124
- package/dist/styles/dock/morph-bridge.css +15 -2
- package/dist/styles/dock/morph.css +45 -114
- package/dist/styles/dock/shape.css +137 -11
- package/dist/styles/dock/shell.css +1 -3
- package/dist/styles/dock/stack-rail.css +95 -7
- package/dist/styles/dock-controls/icon-button.css +34 -2
- package/dist/styles/dock-controls/triggers.css +23 -1
- package/dist/styles/dock-controls.css +18 -0
- package/dist/styles/dock.css +16 -1
- package/dist/styles/drawer.css +166 -0
- package/dist/styles/glass/glass-atom.css +244 -0
- package/dist/styles/glass/glass-capsule.css +143 -0
- package/dist/styles/glass/glass-chip.css +215 -0
- package/dist/styles/glass/liquid-enter.css +252 -0
- package/dist/styles/glass/liquid-morph.css +850 -0
- package/dist/styles/glass/material.css +70 -1
- package/dist/styles/glass/reveal.css +22 -2
- package/dist/styles/glass/rim.css +13 -1
- package/dist/styles/glass/surfaces.css +18 -8
- package/dist/styles/glass.css +11 -0
- package/dist/styles/icon-chip.css +45 -0
- package/dist/styles/index.css +11 -0
- package/dist/styles/jubilance.css +65 -0
- package/dist/styles/motion/morph-field.css +229 -0
- package/dist/styles/paper.css +230 -14
- package/dist/styles/scroll-choreography.css +135 -78
- package/dist/styles/segmented-tabs.css +85 -51
- package/dist/styles/select.css +151 -0
- package/dist/styles/tokens/color-radius.css +1 -1
- package/dist/styles/tokens/dark-arm.css +57 -8
- package/dist/styles/tokens/glass-deep.css +8 -4
- package/dist/styles/tokens/glass-fx.css +80 -2
- package/dist/styles/tokens/glass.css +140 -8
- package/dist/styles/tokens/light-dark.css +2 -2
- package/dist/styles/tokens/property-regs.css +144 -0
- package/dist/styles/tokens/scale-paper.css +20 -8
- package/dist/styles/tokens/scheme-motion.css +92 -157
- package/dist/styles/tokens/scheme-spring.css +239 -0
- package/dist/styles/tokens/scroll-tokens.css +15 -15
- package/dist/styles/tokens/shadow.css +73 -13
- package/dist/styles/tokens.css +9 -2
- package/dist/styles/utilities/btn.css +70 -0
- package/dist/switch.js +1 -1
- package/dist/tabs.js +198 -181
- package/dist/timeline.js +241 -206
- package/dist/toast.js +1 -1
- package/dist/toggle-chip.js +14 -41
- package/dist/toggle-group.js +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/typewriter.js +1 -1
- package/dist/{uniformBridgeWGPU-CtAKl45L.js → uniformBridgeWGPU-BCse1SHU.js} +191 -127
- package/dist/{useAnimatedNumber-CLrxicG9.js → useAnimatedNumber-BntskVdQ.js} +1 -1
- package/dist/{useConfiguratorState-4xblKfwd.js → useConfiguratorState-22BcULzF.js} +37 -35
- package/dist/useDockCtaReceive-DX7RDb6_.js +88 -0
- package/dist/{useDragMorph-BUgAC-zK.js → useDragMorph-DGLSewwB.js} +2 -2
- package/dist/useDragVelocity-CjcfX3dA.js +57 -0
- package/dist/useGooMorph-Cs13NgYL.js +104 -0
- package/dist/useGpuSubstrate-Ceet70xq.js +347 -0
- package/dist/{useViewTransition-CUI1nAgn.js → useHaptic-CPz5BT9I.js} +40 -1
- package/dist/{useLiquidFlex-UmA6LMZu.js → useLiquidFlex-Cc6PjVJD.js} +8 -3
- package/dist/useLiquidPress-CtOQ8Z0_.js +43 -0
- package/dist/useMorphField-ogBM7ubT.js +141 -0
- package/dist/{useScrollChrome-D66AVP7C.js → useScrollChrome-JuImi1DM.js} +2 -2
- package/dist/{useSpringMount-BWDnKIwh.js → useSpringMount-DFxtzmkR.js} +1 -1
- package/dist/{useSpringPress-B1-Czp-V.js → useSpringPress-VM9RgVq3.js} +1 -1
- package/dist/{useSurfaceAxis-g0NloYEF.js → useSurfaceAxis-Ci7Dnaw2.js} +1 -1
- package/dist/{useTouchGate-CBZTD4SD.js → useTouchGate-B1Lf9XqN.js} +1 -1
- package/dist/virtual.js +1 -1
- package/dist/watercolor-dot.js +16 -26
- package/dist/writeVelocityWeight-Bh83xZfs.js +19 -0
- package/package.json +14 -3
- package/src/styles/cards.css +202 -2
- package/src/styles/configurator.css +172 -7
- package/src/styles/critical-partition.mjs +35 -2
- package/src/styles/dock/adaptive-legibility.css +119 -0
- package/src/styles/dock/density.css +18 -10
- package/src/styles/dock/fission-bridge.css +552 -0
- package/src/styles/dock/layers.css +88 -124
- package/src/styles/dock/morph-bridge.css +15 -2
- package/src/styles/dock/morph.css +45 -114
- package/src/styles/dock/shape.css +137 -11
- package/src/styles/dock/shell.css +1 -3
- package/src/styles/dock/stack-rail.css +95 -7
- package/src/styles/dock-controls/icon-button.css +34 -2
- package/src/styles/dock-controls/triggers.css +23 -1
- package/src/styles/dock-controls.css +18 -0
- package/src/styles/dock.css +16 -1
- package/src/styles/drawer.css +164 -0
- package/src/styles/glass/glass-atom.css +244 -0
- package/src/styles/glass/glass-capsule.css +143 -0
- package/src/styles/glass/glass-chip.css +215 -0
- package/src/styles/glass/liquid-enter.css +252 -0
- package/src/styles/glass/liquid-morph.css +850 -0
- package/src/styles/glass/material.css +70 -1
- package/src/styles/glass/reveal.css +22 -2
- package/src/styles/glass/rim.css +13 -1
- package/src/styles/glass/surfaces.css +18 -8
- package/src/styles/glass.css +11 -0
- package/src/styles/icon-chip.css +45 -0
- package/src/styles/index.css +11 -0
- package/src/styles/jubilance.css +65 -0
- package/src/styles/motion/morph-field.css +229 -0
- package/src/styles/paper.css +230 -14
- package/src/styles/scroll-choreography.css +135 -78
- package/src/styles/segmented-tabs.css +85 -47
- package/src/styles/select.css +151 -0
- package/src/styles/tokens/color-radius.css +1 -1
- package/src/styles/tokens/dark-arm.css +57 -8
- package/src/styles/tokens/glass-deep.css +8 -4
- package/src/styles/tokens/glass-fx.css +80 -2
- package/src/styles/tokens/glass.css +140 -8
- package/src/styles/tokens/light-dark.css +2 -2
- package/src/styles/tokens/property-regs.css +144 -0
- package/src/styles/tokens/scale-paper.css +20 -8
- package/src/styles/tokens/scheme-motion.css +92 -157
- package/src/styles/tokens/scheme-spring.css +239 -0
- package/src/styles/tokens/scroll-tokens.css +15 -15
- package/src/styles/tokens/shadow.css +73 -13
- package/src/styles/tokens.css +9 -2
- package/src/styles/utilities/btn.css +70 -0
- package/dist/DialogContent-6JtTB5x2.js +0 -106
- package/dist/PagerDots-Cjx96la-.js +0 -98
- package/dist/PaperBackdrop-CqwjnfLv.js +0 -31
- package/dist/badge-D9XWW4GT.js +0 -53
- package/dist/button-5dY1Dvhd.js +0 -125
- package/dist/components/custom/concentric/composables/ringField.d.ts +0 -85
- package/dist/createCanvasLifecycle-Gh45Ce-p.js +0 -93
- package/dist/dockContext-BNfyPLe9.js +0 -17
- package/dist/slider-2A0rPn79.js +0 -142
- package/dist/springPresets-Ce7OYCm9.js +0 -46
- package/dist/useDockCtaReceive-DfFkmgL1.js +0 -82
- package/dist/useGpuSubstrate-4pTcp6mX.js +0 -294
- package/dist/useLiquidPress-BooG97RY.js +0 -39
- /package/dist/{CollapsibleContent-68dEXIAl.js → CollapsibleContent-D7UNsXQr.js} +0 -0
- /package/dist/{DialogFooter-9phqcRIS.js → DialogFooter-DoLy0ewI.js} +0 -0
- /package/dist/{InstrumentChassis-CxsHSyMj.js → InstrumentChassis-UUJX0-mW.js} +0 -0
- /package/dist/{Label-C2ejNWeC.js → Label-B_HPqi_q.js} +0 -0
- /package/dist/{ModalOverlay-D1VrWyoi.js → ModalOverlay-DfAf7o15.js} +0 -0
- /package/dist/{SelectGroup-BFiod7X2.js → SelectGroup-Dcczq44n.js} +0 -0
- /package/dist/{SelectSeparator-C2UGOkXF.js → SelectSeparator-CNRGG7Ew.js} +0 -0
- /package/dist/{Separator-DQcwC2TR.js → Separator-Cf9-JFgi.js} +0 -0
- /package/dist/{_plugin-vue_export-helper-C1je1s0u.js → _plugin-vue_export-helper-gLzyemvl.js} +0 -0
- /package/dist/{color-Cs0-oxYL.js → color-B9jZdhJX.js} +0 -0
- /package/dist/{compile-nZ1xVYTp.js → compile-INR_tPk_.js} +0 -0
- /package/dist/{constants--C2DkLW8.js → constants-BWj7DkMn.js} +0 -0
- /package/dist/{createContext-CpBNTRMI.js → createContext-Cd23P9j0.js} +0 -0
- /package/dist/{dotMatrixField-D_x9_1qq.js → dotMatrixField-CRO0ZFk4.js} +0 -0
- /package/dist/{flow.glsl-B2P7e0MT.js → flow.glsl-DqEGRpl4.js} +0 -0
- /package/dist/{math-Eb0w3LqU.js → math-5RcIgCB4.js} +0 -0
- /package/dist/{menuItemVariants-D8VNHq22.js → menuItemVariants-DAXquKja.js} +0 -0
- /package/dist/{prng-B-d9eYX8.js → prng-B5tIXTtj.js} +0 -0
- /package/dist/{procedural-color.wgsl-m7Zgv35j.js → procedural-color.wgsl-C1Y5PkM6.js} +0 -0
- /package/dist/{scrollReader-DI1NWxUw.js → scrollReader-rc3Ns_NM.js} +0 -0
- /package/dist/{useControlSize-SH1UmJCi.js → useControlSize-DAZ062x7.js} +0 -0
- /package/dist/{useFuzzySearch-DIhcYtcw.js → useFuzzySearch-0fTYOhS4.js} +0 -0
- /package/dist/{useGlobalDark-CmjB9E32.js → useGlobalDark-B4HO9LXG.js} +0 -0
- /package/dist/{useIntersectionPause-Dbozzjgm.js → useIntersectionPause-DO8Vl4gs.js} +0 -0
- /package/dist/{useInterval-CNdua40W.js → useInterval-BVUI-sez.js} +0 -0
- /package/dist/{useKeyboardShortcuts-BPRp33Im.js → useKeyboardShortcuts-BEut54Tk.js} +0 -0
- /package/dist/{usePointerVelocityField-QLsy3_kv.js → usePointerVelocityField-BrkqZFhj.js} +0 -0
- /package/dist/{useResizeObserver-Bpoi7tRE.js → useResizeObserver-DdEn3v2f.js} +0 -0
- /package/dist/{useResolveTokenColor-vAylyR0x.js → useResolveTokenColor-ChOnCckA.js} +0 -0
- /package/dist/{useScrollProgress-c3yxmH6a.js → useScrollProgress-A0M6qRl7.js} +0 -0
- /package/dist/{useSortable-BVqlJRBR.js → useSortable-DQV_UNDw.js} +0 -0
- /package/dist/{useTextHighlight-NmBM5V_4.js → useTextHighlight-Bma3UlKA.js} +0 -0
- /package/dist/{useTimer-BHJOKr35.js → useTimer-BBLGPXR8.js} +0 -0
- /package/dist/{useUserInvalidAria-y5namzeE.js → useUserInvalidAria-EmmwWPOl.js} +0 -0
- /package/dist/{useViewportReady-anbac3m6.js → useViewportReady-C9pYnn7K.js} +0 -0
- /package/dist/{vReveal-D9k0jADx.js → vReveal-CT1KtwZA.js} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as DotMatrix } from "./DotMatrix.vue";
|
|
2
2
|
export { useDotMatrix, type DotMatrixHandle, type UseDotMatrixOptions, } from "./composables/useDotMatrix";
|
|
3
|
-
export { type DotMatrixConfig, type DotPointerMode, DEFAULT_DOT_MATRIX_CONFIG, WARM_IDENTITY_PALETTE, MAX_DOTS, MAX_DOT_STOPS, } from "./constants";
|
|
3
|
+
export { type DotMatrixConfig, type DotPointerMode, type DotLayout, DEFAULT_DOT_MATRIX_CONFIG, WARM_IDENTITY_PALETTE, MAX_DOTS, MAX_DOT_STOPS, } from "./constants";
|
|
4
4
|
export { type Vec3, type Mat3, type FacingFade, fibonacciDot, facingFade, spinMatrix, applyMat3, breathRadius, GOLDEN_RATIO, GOLDEN_ANGLE, FACING_LO, FACING_HI, SIZE_TAPER_BASE, SIZE_TAPER_GAIN, } from "./composables/dotMatrixField";
|
|
5
5
|
export type { OklchStop } from "../../../composables/color";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** The instanced-billboard VERTEX shader (gl_VertexID quad + instanced dot attribute). */
|
|
2
|
-
export declare const DOT_MATRIX_VERT_GLSL = "#version 300 es\nprecision highp float;\n\n#define MAX_DOT_STOPS 4\n#define GOLDEN_ANGLE 2.399963229728653\n\n// The static quad corner (6 verts: two triangles) + the instanced per-dot data
|
|
2
|
+
export declare const DOT_MATRIX_VERT_GLSL = "#version 300 es\nprecision highp float;\n\n#define MAX_DOT_STOPS 4\n#define GOLDEN_ANGLE 2.399963229728653\n\n// \u2500\u2500 The \u221A\u03C6 proportion ladder (the liquid-lens constants; the line-for-line WGSL twin) \u2500\u2500\n#define PI_CONST 3.141592653589793\n#define TAU 6.283185307179586\n#define PHI 1.618033988749895\n#define SQRT_PHI 1.272019649514069\n#define INV_SQRT_PHI 0.7861513777574233\n#define INV_PHI2 0.3819660112501051\n#define VEL_SCALE 0.18\n#define STRETCH_GAIN 2.4\n#define WAKE_GAIN 12.0\n#define LENS_MAGNIFY SQRT_PHI\n#define RIM_CHROMA 1.0\n#define VIGNETTE_FLOOR INV_PHI2\n#define CAST_OFFSET 0.9\n#define CAST_DARK 0.0\n#define CAST_ALPHA 0.55\n\n// The static quad corner (6 verts: two triangles) + the instanced per-dot data. The draw is\n// 12 verts/instance: gl_VertexID 0..5 the lit dot, 6..11 the cartoon offset-CAST shadow (the\n// SAME instanced draw, zero new buffer). The corner is re-derived from gl_VertexID % 6.\nin vec4 aDot; // (unitPos.xyz, sphereIdx) \u2014 instanced (divisor 1)\n\nuniform vec4 uU0; // (resX, resY, dpr, time)\nuniform vec4 uU1; // (radius, dotSize, baseOpacity, aspect)\nuniform vec4 uU2; // (facingLo, facingHi, sizeLo, sizeHi)\nuniform vec4 uU3; // (parallax, pointerPush, pointerBloom, pointerActive)\nuniform vec4 uU4; // (pointerX, pointerY, sphere2Scale, breathRadius)\nuniform vec4 uU5; // (sphere2OffsetX, sphere2OffsetY, hasBackground, _pad)\nuniform vec4 uU6; // (layout, gravityStrength, gravityRadius, planeScale)\nuniform vec4 uU7; // (velX, velY, twinkleRate, wakeStrength)\nuniform mat3 uSpin; // the slow tilted Y-rotation\n\nout vec2 vLocal; // [-1,1] billboard-local for the SDF circle\nout float vBright; // facing-driven brightness\nout float vAlpha; // facing-driven opacity\nout float vTone; // palette tone (0 = bright cream near-front, 1 = amber rim)\nout float vCast; // 1 = the cartoon offset-cast shadow billboard, 0 = the lit dot\n\nfloat facingTerm(vec3 n) {\n return clamp(n.z * 0.5 + 0.5, 0.0, 1.0); // 1 = dead-front\n}\n\nvoid main() {\n // The two-triangle quad corners (6 verts), re-derived per gl_VertexID (no aCorner buffer).\n vec2 cornerLUT[6] = vec2[6](\n vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0),\n vec2(-1.0, 1.0), vec2(1.0, -1.0), vec2(1.0, 1.0)\n );\n bool isCast = gl_VertexID >= 6;\n vec2 aCorner = cornerLUT[gl_VertexID % 6];\n\n vec3 unitPos = aDot.xyz;\n float sphereIdx = aDot.w;\n\n float breath = uU4.w;\n float pAct = uU3.w;\n vec2 pointerNdc = vec2(uU4.x * 2.0 - 1.0, 1.0 - uU4.y * 2.0);\n bool isPlane = uU6.x > 0.5;\n float gravStrength = uU6.y;\n float gravRadius = max(uU6.z, 1e-3);\n float planeScale = uU6.w;\n // The reclaimed travel velocity \u2192 the lens-frame vector (host +y is DOWN \u2192 flip to NDC +up).\n vec2 velRaw = vec2(uU7.x, -uU7.y);\n float vmag = length(velRaw) * VEL_SCALE;\n vec2 vdir = length(velRaw) > 1e-5 ? velRaw / max(length(velRaw), 1e-5) : vec2(0.0);\n float wake = uU7.w; // the spring-engaged active (\u03B6=0.7) \u2192 the wake LAGS + overshoots\n float twkRate = uU7.z;\n // The per-dot twinkle phase \u2014 an on-GPU instance hash (no new buffer); uTime pinned under PRM.\n float twkPhase = fract(sin(float(gl_InstanceID) * 12.9898) * 43758.5453) * TAU;\n float twk = twkRate * sin(uU0.w * (twkRate + 0.6) + twkPhase);\n\n vec2 center2d;\n float facing;\n float sizeTaper;\n float burstScale;\n float facingOpacity;\n float burstBright;\n float castLobe = 0.0;\n\n if (isPlane) {\n // \u2500\u2500 The 2D-PLANE register \u2192 the DIRECTIONAL LIQUID LENS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n vec2 base2d = unitPos.xy * planeScale;\n\n // 1. ANTICIPATION \u2014 the lens LEADS its math-center along travel.\n vec2 wellC = pointerNdc + vdir * wake * (gravRadius * INV_PHI2 * 0.4);\n vec2 toLens = wellC - base2d;\n\n // 2. SQUASH / STRETCH \u2014 measure distance in a frame STRETCHED along travel.\n float along = dot(toLens, vdir);\n float perp = dot(toLens, vec2(-vdir.y, vdir.x));\n float stretch = 1.0 + clamp(vmag * STRETCH_GAIN, 0.0, INV_SQRT_PHI);\n float dE = length(vec2(along / stretch, perp * stretch));\n float well = gravStrength * exp(-(dE * dE) / (2.0 * gravRadius * gravRadius)) * pAct;\n vec2 lift = toLens * well;\n\n // 3. COMET-TAIL WAKE \u2014 a pull streamed BEHIND the lens (follow-through).\n float behind = max(0.0, -along);\n float tail = wake * exp(-(behind * behind) / (gravRadius * gravRadius)) * vmag * WAKE_GAIN;\n lift -= vdir * tail * gravRadius;\n vec2 burstPull = toLens * (uU3.z * 0.4 * exp(-(dE * dE) / (gravRadius * gravRadius))) * pAct;\n center2d = base2d + lift + burstPull;\n\n // 4. LENS OPTICS \u2014 magnify + brightness + the chromatic rim.\n float nearness = exp(-(dE * dE) / (2.0 * gravRadius * gravRadius));\n facing = nearness;\n sizeTaper = 0.8 + LENS_MAGNIFY * nearness * pAct;\n burstScale = 1.0 + uU3.z * 0.6 * nearness;\n float wakeGlow = behind * vmag * 1.4 * pAct;\n // 5. CONTENT-DEFERENTIAL VIGNETTE \u2014 dense edges, clear center; the lens re-densifies it.\n float presence = mix(VIGNETTE_FLOOR, 1.0, smoothstep(0.0, 1.0, length(base2d) / max(planeScale, 1e-3)));\n presence = max(presence, nearness * pAct);\n facingOpacity = (uU2.x + uU2.y * (0.5 + twk + 0.6 * nearness * pAct)) * presence;\n burstBright = 1.0 + (0.5 * nearness + wakeGlow + uU3.z * 0.8 * nearness) * pAct;\n castLobe = nearness * pAct;\n } else {\n // \u2500\u2500 The 3D-SPHERE register \u2192 the SAME directional lens (one warp idiom) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\n float sphereScale = uU1.x;\n vec2 sphereOffset = vec2(0.0);\n if (sphereIdx > 0.5) {\n sphereScale = uU1.x * uU4.z;\n sphereOffset = vec2(uU5.x, uU5.y);\n }\n vec3 n = uSpin * unitPos;\n facing = facingTerm(n);\n vec2 screenN = n.xy * sphereScale + sphereOffset;\n vec2 toLensS = pointerNdc - screenN;\n float alongS = dot(toLensS, vdir);\n float perpS = dot(toLensS, vec2(-vdir.y, vdir.x));\n float stretchS = 1.0 + clamp(vmag * STRETCH_GAIN, 0.0, INV_SQRT_PHI);\n float dES = length(vec2(alongS / stretchS, perpS * stretchS));\n float pd = dES;\n float well = exp(-(dES * dES) / (2.0 * gravRadius * gravRadius)) * gravStrength * uU3.y * pAct * facing;\n vec2 lift = -toLensS * well;\n float behindS = max(0.0, -alongS);\n float tailS = wake * exp(-(behindS * behindS) / (gravRadius * gravRadius)) * vmag * WAKE_GAIN * facing;\n lift -= vdir * tailS * gravRadius * sign(uU3.y);\n vec2 parallaxOff = pointerNdc * uU3.x * pAct;\n center2d = n.xy * sphereScale * breath + sphereOffset + lift + parallaxOff;\n float nearnessS = exp(-(dES * dES) / (2.0 * gravRadius * gravRadius)) * facing;\n sizeTaper = uU2.z + uU2.w * facing + LENS_MAGNIFY * 0.4 * nearnessS * pAct;\n burstScale = 1.0 + uU3.z * exp(-(dES * dES) / (gravRadius * gravRadius));\n float wakeGlowS = behindS * vmag * 1.0 * pAct * facing;\n facingOpacity = uU2.x + uU2.y * facing + uU2.y * twk * facing;\n burstBright = 1.0 + (uU3.z * 0.8 * exp(-(dES * dES) / (gravRadius * gravRadius)) + wakeGlowS) * pAct;\n castLobe = nearnessS * pAct;\n }\n\n float dotR = uU1.y * sizeTaper * burstScale;\n float offX = aCorner.x * dotR;\n float offY = aCorner.y * dotR;\n\n // THE CARTOON OFFSET-CAST \u2014 verts 6..11 are a dark, lower-\u03B1 duplicate offset along a \u221A\u03C6\n // light vector (down-right), gated by castLobe so the lobe appears only under the lens.\n vec2 lightVec = normalize(vec2(SQRT_PHI, -1.0)) * CAST_OFFSET * dotR;\n vec2 castShift = isCast ? lightVec : vec2(0.0);\n float castStrength = isCast ? smoothstep(0.05, 0.5, castLobe) : 1.0;\n\n vLocal = aCorner;\n vCast = isCast ? 1.0 : 0.0;\n if (isCast) {\n vBright = CAST_DARK;\n vAlpha = clamp(uU1.z * CAST_ALPHA * facingOpacity * castStrength, 0.0, 1.0);\n vTone = 1.0;\n } else {\n vBright = facingOpacity * burstBright;\n vAlpha = clamp(uU1.z * facingOpacity * burstBright, 0.0, 1.0);\n // The chromatic RIM refraction (REPLACES the old depth-only 1.0-facing tone \u2014 EXCISED).\n float rim = smoothstep(0.25, 0.6, 1.0 - facing) * RIM_CHROMA;\n vTone = clamp(max(1.0 - facing, rim * facing), 0.0, 1.0);\n }\n\n // The whole sphere + quad is built in a SQUARE space (so the globe stays circular); the\n // final clip-space x divides by aspect so a wide canvas does not squash it into an ellipse.\n float aspect = max(uU1.w, 1e-4);\n gl_Position = vec4((center2d.x + offX + castShift.x) / aspect, center2d.y + offY + castShift.y, 0.0, 1.0);\n}";
|
|
3
3
|
/** The crisp fwidth-SDF dot fragment (the ONE AA canon; splices the shared color chunk). */
|
|
4
|
-
export declare const DOT_MATRIX_FRAG_GLSL = "#version 300 es\nprecision highp float;\n\n#define MAX_DOT_STOPS 4\n\nin vec2 vLocal;\nin float vBright;\nin float vAlpha;\nin float vTone;\nout vec4 fragColor;\n\nuniform vec4 uBg; // (background.rgb, hasBackground)\nuniform int uStopCount;\nuniform vec3 uPalette[MAX_DOT_STOPS]; // linear-sRGB stops\n\nconst mat2 FBM_ROT = mat2(0.8, 0.6, -0.6, 0.8);\n\n\nfloat srgbToLinearCh(float c) {\n return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);\n}\nvec3 srgbToLinear(vec3 c) {\n return vec3(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfloat linearToSrgbCh(float c) {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nvec3 linearToSrgb(vec3 c) {\n return vec3(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS (constants.ts), row-major; written here as GLSL\n// columns (= the transpose) so mat3 * vec3 evaluates the row-major M\u00B7v.\nconst mat3 LINEAR_SRGB_TO_LMS = mat3(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients (gamut.ts lines 295-297 \u2014\n// these differ at the ~1e-9 digit from LMS_TO_OKLAB_MATRIX; use the inline ones to\n// mirror the exact value.js path), row-major \u2192 GLSL columns.\nconst mat3 LMS_TO_OKLAB = mat3(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF (constants.ts), row-major \u2192 GLSL columns. Rows:\n// l = [1, 0.3963377774, 0.2158037573], m = [1, -0.1055613458, -0.0638541728],\n// s = [1, -0.0894841775, -1.2914855480].\nconst mat3 OKLAB_TO_LMS = mat3(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB (constants.ts), row-major \u2192 GLSL columns.\nconst mat3 LMS_TO_LINEAR_SRGB = mat3(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nvec3 srgbToOklab(vec3 c) {\n vec3 lin = srgbToLinear(c);\n vec3 lms = LINEAR_SRGB_TO_LMS * lin;\n vec3 lmsCbrt = sign(lms) * pow(abs(lms), vec3(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nvec3 oklabToLinearSrgb(vec3 lab) {\n vec3 lms_ = OKLAB_TO_LMS * lab;\n vec3 lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS. Mirrors value.js rawOklabToOklch (which returns\n// degrees; we stay in radians and only fold to [0, 2pi)).\nvec3 oklabToOklch(vec3 lab) {\n float C = length(lab.yz);\n float H = atan(lab.z, lab.y);\n if (H < 0.0) H += 2.0 * PI;\n return vec3(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nvec3 oklchToOklab(vec3 lch) {\n return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\nvec3 samplePaletteLin(float t) {\n if (uStopCount <= 1) return uPalette[0];\n float n = float(uStopCount);\n float ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n int i0 = int(floor(ft));\n int i1 = min(i0 + 1, uStopCount - 1);\n float f = ft - float(i0);\n vec3 lmsA = LINEAR_SRGB_TO_LMS * uPalette[i0];\n vec3 lmsB = LINEAR_SRGB_TO_LMS * uPalette[i1];\n vec3 labA = LMS_TO_OKLAB * (sign(lmsA) * pow(abs(lmsA), vec3(1.0 / 3.0)));\n vec3 labB = LMS_TO_OKLAB * (sign(lmsB) * pow(abs(lmsB), vec3(1.0 / 3.0)));\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\nvoid main() {\n // The crisp fwidth-smoothstep SDF circle (the ONE AA canon; ~1px band at any DPR).\n float d = length(vLocal) - 1.0;\n float aa = max(fwidth(d), 1e-4);\n float coverage = 1.0 - smoothstep(-aa, aa, d);\n if (coverage < 0.002) discard;\n\n vec3 lin = samplePaletteLin(vTone) * clamp(vBright, 0.0, 1.6);\n float alpha = vAlpha * coverage;\n vec3 rgb = clamp(linearToSrgb(lin), vec3(0.0), vec3(1.0));\n fragColor = vec4(rgb * alpha, alpha);\n}";
|
|
4
|
+
export declare const DOT_MATRIX_FRAG_GLSL = "#version 300 es\nprecision highp float;\n\n#define MAX_DOT_STOPS 4\n\nin vec2 vLocal;\nin float vBright;\nin float vAlpha;\nin float vTone;\nin float vCast;\nout vec4 fragColor;\n\nuniform vec4 uBg; // (background.rgb, hasBackground)\nuniform int uStopCount;\nuniform vec3 uPalette[MAX_DOT_STOPS]; // linear-sRGB stops\n\nconst mat2 FBM_ROT = mat2(0.8, 0.6, -0.6, 0.8);\n\n// PI in scope for the spliced OKLCH hue fns (the procedural-color chunk contract:\n// the consumer splices the matrices chunk + DEFINES PI FIRST \u2014 see\n// procedural-color.glsl.ts \u00A7\"PI being in scope\"). The dot SDF main() reads only the\n// OKLAB path, but the whole spliced chunk still compiles, so PI must be declared.\n#define PI 3.141592653589793\n\nfloat srgbToLinearCh(float c) {\n return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);\n}\nvec3 srgbToLinear(vec3 c) {\n return vec3(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfloat linearToSrgbCh(float c) {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nvec3 linearToSrgb(vec3 c) {\n return vec3(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS (constants.ts), row-major; written here as GLSL\n// columns (= the transpose) so mat3 * vec3 evaluates the row-major M\u00B7v.\nconst mat3 LINEAR_SRGB_TO_LMS = mat3(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients (gamut.ts lines 295-297 \u2014\n// these differ at the ~1e-9 digit from LMS_TO_OKLAB_MATRIX; use the inline ones to\n// mirror the exact value.js path), row-major \u2192 GLSL columns.\nconst mat3 LMS_TO_OKLAB = mat3(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF (constants.ts), row-major \u2192 GLSL columns. Rows:\n// l = [1, 0.3963377774, 0.2158037573], m = [1, -0.1055613458, -0.0638541728],\n// s = [1, -0.0894841775, -1.2914855480].\nconst mat3 OKLAB_TO_LMS = mat3(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB (constants.ts), row-major \u2192 GLSL columns.\nconst mat3 LMS_TO_LINEAR_SRGB = mat3(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nvec3 srgbToOklab(vec3 c) {\n vec3 lin = srgbToLinear(c);\n vec3 lms = LINEAR_SRGB_TO_LMS * lin;\n vec3 lmsCbrt = sign(lms) * pow(abs(lms), vec3(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nvec3 oklabToLinearSrgb(vec3 lab) {\n vec3 lms_ = OKLAB_TO_LMS * lab;\n vec3 lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS. Mirrors value.js rawOklabToOklch (which returns\n// degrees; we stay in radians and only fold to [0, 2pi)).\nvec3 oklabToOklch(vec3 lab) {\n float C = length(lab.yz);\n float H = atan(lab.z, lab.y);\n if (H < 0.0) H += 2.0 * PI;\n return vec3(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nvec3 oklchToOklab(vec3 lch) {\n return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\nvec3 samplePaletteLin(float t) {\n if (uStopCount <= 1) return uPalette[0];\n float n = float(uStopCount);\n float ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n int i0 = int(floor(ft));\n int i1 = min(i0 + 1, uStopCount - 1);\n float f = ft - float(i0);\n vec3 lmsA = LINEAR_SRGB_TO_LMS * uPalette[i0];\n vec3 lmsB = LINEAR_SRGB_TO_LMS * uPalette[i1];\n vec3 labA = LMS_TO_OKLAB * (sign(lmsA) * pow(abs(lmsA), vec3(1.0 / 3.0)));\n vec3 labB = LMS_TO_OKLAB * (sign(lmsB) * pow(abs(lmsB), vec3(1.0 / 3.0)));\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\nvoid main() {\n // The crisp fwidth-smoothstep SDF circle (the ONE AA canon; ~1px band at any DPR).\n float d = length(vLocal) - 1.0;\n float aa = max(fwidth(d), 1e-4);\n float coverage = 1.0 - smoothstep(-aa, aa, d);\n if (coverage < 0.002) discard;\n // The cartoon-cast billboard collapses to nothing away from the lens \u2014 drop the dead quads.\n if (vCast > 0.5 && vAlpha < 0.003) discard;\n\n vec3 lin = samplePaletteLin(vTone) * clamp(vBright, 0.0, 1.6);\n float alpha = vAlpha * coverage;\n vec3 rgb = clamp(linearToSrgb(lin), vec3(0.0), vec3(1.0));\n fragColor = vec4(rgb * alpha, alpha);\n}";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const DOT_MATRIX_WGSL = "\nconst PI: f32 = 3.141592653589793;\nconst TAU: f32 = 6.283185307179586;\nconst MAX_DOT_STOPS: i32 = 4;\n// The golden-angle constant (transcribes dotMatrixField.ts GOLDEN_ANGLE = PI*(3-sqrt5)).\nconst GOLDEN_ANGLE: f32 = 2.399963229728653;\n\nstruct Dot {\n // (unitPos.xyz, sphereIdx)\n data: vec4<f32>,\n};\n\n// \u2500\u2500 Render uniforms (the typed-struct source-of-truth \u2014 see uniformBridgeWGPU.ts) \u2500\u2500\nstruct Uniforms {\n // u0: (uResolutionX, uResolutionY, uDpr, uTime)\n u0: vec4<f32>,\n // u1: (uRadius, uDotSize, uBaseOpacity, uAspect)\n u1: vec4<f32>,\n // u2: (uFacingLo, uFacingHi, uSizeLo, uSizeHi)\n u2: vec4<f32>,\n // u3: (uParallax, uPointerPush, uPointerBloom, uPointerActive)\n u3: vec4<f32>,\n // u4: (uPointerX, uPointerY, uSphere2Scale, uBreathRadius)\n u4: vec4<f32>,\n // u5: (uSphere2OffsetX, uSphere2OffsetY, uHasBackground, _pad)\n u5: vec4<f32>,\n spin: mat3x3<f32>,\n // ints: (uStopCount, uDotCount, uSpheres, _pad)\n ints: vec4<i32>,\n // bg: (background.rgb, _pad)\n bg: vec4<f32>,\n palette: array<vec4<f32>, 4>,\n};\n\n@group(0) @binding(0) var<uniform> u: Uniforms;\n@group(0) @binding(1) var<storage, read> dots: array<Dot>;\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n if (c <= 0.04045) { return c / 12.92; }\n return pow((c + 0.055) / 1.055, 2.4);\n}\nfn srgbToLinear(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfn linearToSrgbCh(c: f32) -> f32 {\n if (c <= 0.0031308) { return c * 12.92; }\n return 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nfn linearToSrgb(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS, row-major \u2192 WGSL columns (= the transpose).\nconst LINEAR_SRGB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients, row-major \u2192 WGSL columns.\nconst LMS_TO_OKLAB: mat3x3<f32> = mat3x3<f32>(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF, row-major \u2192 WGSL columns.\nconst OKLAB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB, row-major \u2192 WGSL columns.\nconst LMS_TO_LINEAR_SRGB: mat3x3<f32> = mat3x3<f32>(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn cbrt3(v: vec3<f32>) -> vec3<f32> {\n return sign(v) * pow(abs(v), vec3<f32>(1.0 / 3.0));\n}\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nfn srgbToOklab(c: vec3<f32>) -> vec3<f32> {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nfn oklabToLinearSrgb(lab: vec3<f32>) -> vec3<f32> {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS, folded to [0, 2pi). Mirrors value.js rawOklabToOklch.\nfn oklabToOklch(lab: vec3<f32>) -> vec3<f32> {\n let C = length(lab.yz);\n var H = atan2(lab.z, lab.y);\n if (H < 0.0) { H = H + 2.0 * PI; }\n return vec3<f32>(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nfn oklchToOklab(lch: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\n// The \u00A7T2 depth-fade (transcribes dotMatrixField.ts facingFade): the rotated-normal's z\n// \u2192 a SOFT facing term \u2192 opacity + size ramps. NOT a binary back-face cull.\nfn facingTerm(n: vec3<f32>) -> f32 {\n return clamp(n.z * 0.5 + 0.5, 0.0, 1.0); // 1 = dead-front, 0 = dead-back\n}\n\n// Sample the multi-stop palette (linear-sRGB stops, OKLab mix). t in [0,1].\nfn samplePaletteLin(t: f32) -> vec3<f32> {\n let stopCount = u.ints.x;\n if (stopCount <= 1) { return u.palette[0].rgb; }\n let n = f32(stopCount);\n let ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n let i0 = i32(floor(ft));\n let i1 = min(i0 + 1, stopCount - 1);\n let f = ft - f32(i0);\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i0].rgb);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i1].rgb);\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\nstruct VSOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) local: vec2<f32>, // [-1,1] billboard-local coords for the SDF circle\n @location(1) bright: f32, // facing-driven brightness [0, ~1.6]\n @location(2) alpha: f32, // facing-driven opacity\n @location(3) tone: f32, // palette tone (0 = bright cream near-front, 1 = amber rim)\n};\n\n@vertex\nfn vs_main(\n @builtin(vertex_index) vi: u32,\n @builtin(instance_index) ii: u32,\n) -> VSOut {\n // Two-triangle quad corners in billboard-local space (camera-facing by construction).\n var corners = array<vec2<f32>, 6>(\n vec2<f32>(-1.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(-1.0, 1.0),\n vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0),\n );\n let local = corners[vi];\n\n let dot = dots[ii].data;\n var unitPos = dot.xyz; // the STATIC phyllotaxis position (JS-computed)\n let sphereIdx = dot.w;\n\n // The breathing radius pulse (transcribes breathRadius \u2014 1 at rest).\n let breath = u.u4.w;\n // Per-sphere offset/scale (the reference two-globe composition).\n var sphereScale = u.u1.x; // uRadius\n var sphereOffset = vec2<f32>(0.0, 0.0);\n if (sphereIdx > 0.5) {\n sphereScale = u.u1.x * u.u4.z; // uRadius * uSphere2Scale\n sphereOffset = vec2<f32>(u.u5.x, u.u5.y); // uSphere2Offset\n }\n\n // The rotated surface NORMAL = spin \u00B7 unitPos (the \u00A7T2 facing source). The spin matrix\n // is the slow tilted Y-rotation (dotMatrixField.ts spinMatrix).\n let n = u.spin * unitPos;\n let facing = facingTerm(n);\n\n // Pointer-repel/attract \u2014 a soft Gaussian dimple lifts the dot radially off the surface\n // near the cursor (the \u00A7T7 local perturbation). The pointer is in NDC (-1..1); a repel\n // (push>0) pushes the dot OUT, an attract (push<0) pulls IN. Velocity scales the push.\n let pAct = u.u3.w;\n let pointerNdc = vec2<f32>(u.u4.x * 2.0 - 1.0, 1.0 - u.u4.y * 2.0);\n let screenN = n.xy * sphereScale + sphereOffset;\n let toPointer = screenN - pointerNdc;\n let pd = length(toPointer);\n let dimple = exp(-pd * pd * 18.0) * u.u3.y * pAct; // soft Gaussian falloff \u00D7 push\n // Lift along the rotated normal's screen projection (radially through the shell).\n let lift = n.xy * dimple;\n\n // Project the rotated normal to the screen (orthographic \u2014 the \u00A7T5 flat hero look),\n // scaled by the sphere radius + the breathing, plus the per-sphere offset + the dimple\n // lift + the pointer-parallax screen-center track.\n let parallaxOff = (pointerNdc) * u.u3.x * pAct; // uParallax \u00B7 pointer \u00B7 active\n let center2d = n.xy * sphereScale * breath + sphereOffset + lift + parallaxOff;\n\n // The depth-of-field dot-size taper (transcribes facingFade size): 0.6 + 0.4\u00B7facing,\n // plus the accel-burst bloom scale near the cursor.\n let sizeTaper = u.u2.z + u.u2.w * facing;\n let burstScale = 1.0 + u.u3.z * exp(-pd * pd * 12.0);\n let dotR = u.u1.y * sizeTaper * burstScale; // uDotSize \u00B7 taper \u00B7 burst\n\n let offX = local.x * dotR;\n let offY = local.y * dotR;\n\n // The opacity ramp (transcribes facingFade opacity): facingLo + facingHi\u00B7facing, scaled\n // by the base opacity + a faint burst brightness lift near the cursor.\n let facingOpacity = u.u2.x + u.u2.y * facing; // facingLo + facingHi\u00B7facing\n let burstBright = 1.0 + u.u3.z * 0.8 * exp(-pd * pd * 12.0);\n\n // The whole sphere + quad is built in a SQUARE space (so the globe stays circular); the\n // final clip-space x divides by aspect so a wide canvas does not squash it into an\n // ellipse (aspect \u2265 1 \u2192 x shrinks; aspect < 1 \u2192 x grows). This keeps the dot quads round\n // AND the sphere circular in PIXELS regardless of the canvas aspect.\n let aspect = max(u.u1.w, 1e-4);\n let clipX = (center2d.x + offX) / aspect;\n let clipY = center2d.y + offY;\n\n var out: VSOut;\n out.pos = vec4<f32>(clipX, clipY, 0.0, 1.0);\n out.local = local;\n out.bright = facingOpacity * burstBright;\n out.alpha = clamp(u.u1.z * facingOpacity * burstBright, 0.0, 1.0);\n // The palette tone reads the DEPTH (not the opacity): the near-front samples the bright\n // cream stop[0] (tone 0), the rim/far the darker amber stop (tone \u2192 1), so the depth-shell\n // reads TONALLY on BOTH a dark AND a light ground (a light-ground globe can't lean on the\n // opacity fade alone \u2014 over a light card a faded dot reads brighter, inverting the shell).\n out.tone = clamp(1.0 - facing, 0.0, 1.0);\n return out;\n}\n\n@fragment\nfn fs_main(in: VSOut) -> @location(0) vec4<f32> {\n // The crisp fwidth-smoothstep SDF circle (the ONE AA canon \u2014 the band is ~1px wide at\n // any DPR/zoom, so fine sub-2px dots stay crisp). d = |uv| - 1; aa = fwidth(d).\n let d = length(in.local) - 1.0;\n let aa = max(fwidth(d), 1e-4);\n let coverage = 1.0 - smoothstep(-aa, aa, d);\n if (coverage < 0.002) { discard; }\n\n // The palette tints the dot by DEPTH (near-front cream \u2192 rim amber); the brightness lights\n // the core. The near hemisphere reads as the bright warm-cream stop, the rim/far fading to\n // the darker amber \u2014 the translucent dot-shell on both grounds.\n let lin = samplePaletteLin(in.tone) * clamp(in.bright, 0.0, 1.6);\n let alpha = in.alpha * coverage;\n let rgb = clamp(linearToSrgb(lin), vec3<f32>(0.0), vec3<f32>(1.0));\n // Premultiplied-alpha output (the translucent dot-shell additive over the clear).\n return vec4<f32>(rgb * alpha, alpha);\n}\n";
|
|
1
|
+
export declare const DOT_MATRIX_WGSL = "\nconst PI: f32 = 3.141592653589793;\nconst TAU: f32 = 6.283185307179586;\nconst MAX_DOT_STOPS: i32 = 4;\n// The golden-angle constant (transcribes dotMatrixField.ts GOLDEN_ANGLE = PI*(3-sqrt5)).\nconst GOLDEN_ANGLE: f32 = 2.399963229728653;\n\n// \u2500\u2500 The \u221A\u03C6 proportion ladder (the liquid-lens constants; Aristotelian, no magic numbers) \u2500\u2500\nconst PHI: f32 = 1.618033988749895; // the golden ratio \u03C6\nconst SQRT_PHI: f32 = 1.272019649514069; // \u221A\u03C6\nconst INV_SQRT_PHI: f32 = 0.7861513777574233; // 1/\u221A\u03C6 \u2248 the squash/stretch cap\nconst INV_PHI2: f32 = 0.3819660112501051; // 1/\u03C6\u00B2 \u2248 the anticipation-lead rung\n// The velocity (normalized-host/sec) \u2192 NDC/lens-frame scale: a sweep across the host reads as a\n// strong-but-bounded ellipse. The lens constants ladder off \u221A\u03C6 off this scaled vmag.\nconst VEL_SCALE: f32 = 0.18;\nconst STRETCH_GAIN: f32 = 2.4; // vmag \u2192 ellipse eccentricity gain\nconst WAKE_GAIN: f32 = 12.0; // the comet-tail displacement gain (spike-calibrated)\nconst LENS_MAGNIFY: f32 = SQRT_PHI; // the dot SWELLS \u221A\u03C6\u00D7 under the lens core (over the 0.8 base)\nconst RIM_CHROMA: f32 = 1.0; // the chromatic-rim tone gain (samples the warmer amber stop)\nconst VIGNETTE_FLOOR: f32 = INV_PHI2; // the resting center-clear floor (dense edges, clear center)\n// The cartoon 1940s offset-cast: a darkened, lower-\u03B1, offset duplicate billboard (the SAME\n// instanced draw, verts 6..11). The light vector is a \u221A\u03C6-proportioned down-right cast.\nconst CAST_OFFSET: f32 = 0.9; // the cast displacement (\u00D7 dot radius), down-right\nconst CAST_DARK: f32 = 0.0; // the cast samples pure-dark (a shadow lobe, not a tint)\nconst CAST_ALPHA: f32 = 0.55; // the cast's lower opacity (under the lit dot)\n\nstruct Dot {\n // (unitPos.xyz, sphereIdx)\n data: vec4<f32>,\n};\n\n// \u2500\u2500 Render uniforms (the typed-struct source-of-truth \u2014 see uniformBridgeWGPU.ts) \u2500\u2500\nstruct Uniforms {\n // u0: (uResolutionX, uResolutionY, uDpr, uTime)\n u0: vec4<f32>,\n // u1: (uRadius, uDotSize, uBaseOpacity, uAspect)\n u1: vec4<f32>,\n // u2: (uFacingLo, uFacingHi, uSizeLo, uSizeHi)\n u2: vec4<f32>,\n // u3: (uParallax, uPointerPush, uPointerBloom, uPointerActive)\n u3: vec4<f32>,\n // u4: (uPointerX, uPointerY, uSphere2Scale, uBreathRadius)\n u4: vec4<f32>,\n // u5: (uSphere2OffsetX, uSphere2OffsetY, uHasBackground, _pad)\n u5: vec4<f32>,\n // u6: (uLayout, uGravityStrength, uGravityRadius, uPlaneScale) // layout: 0 sphere \u00B7 1 plane\n u6: vec4<f32>,\n // u7: (uVelX, uVelY, uTwinkleRate, uWakeStrength) // the reclaimed travel-velocity lane\n u7: vec4<f32>,\n spin: mat3x3<f32>,\n // ints: (uStopCount, uDotCount, uSpheres, _pad)\n ints: vec4<i32>,\n // bg: (background.rgb, _pad)\n bg: vec4<f32>,\n palette: array<vec4<f32>, 4>,\n};\n\n@group(0) @binding(0) var<uniform> u: Uniforms;\n@group(0) @binding(1) var<storage, read> dots: array<Dot>;\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n if (c <= 0.04045) { return c / 12.92; }\n return pow((c + 0.055) / 1.055, 2.4);\n}\nfn srgbToLinear(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfn linearToSrgbCh(c: f32) -> f32 {\n if (c <= 0.0031308) { return c * 12.92; }\n return 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nfn linearToSrgb(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS, row-major \u2192 WGSL columns (= the transpose).\nconst LINEAR_SRGB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients, row-major \u2192 WGSL columns.\nconst LMS_TO_OKLAB: mat3x3<f32> = mat3x3<f32>(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF, row-major \u2192 WGSL columns.\nconst OKLAB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB, row-major \u2192 WGSL columns.\nconst LMS_TO_LINEAR_SRGB: mat3x3<f32> = mat3x3<f32>(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn cbrt3(v: vec3<f32>) -> vec3<f32> {\n return sign(v) * pow(abs(v), vec3<f32>(1.0 / 3.0));\n}\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nfn srgbToOklab(c: vec3<f32>) -> vec3<f32> {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nfn oklabToLinearSrgb(lab: vec3<f32>) -> vec3<f32> {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS, folded to [0, 2pi). Mirrors value.js rawOklabToOklch.\nfn oklabToOklch(lab: vec3<f32>) -> vec3<f32> {\n let C = length(lab.yz);\n var H = atan2(lab.z, lab.y);\n if (H < 0.0) { H = H + 2.0 * PI; }\n return vec3<f32>(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nfn oklchToOklab(lch: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\n// The \u00A7T2 depth-fade (transcribes dotMatrixField.ts facingFade): the rotated-normal's z\n// \u2192 a SOFT facing term \u2192 opacity + size ramps. NOT a binary back-face cull.\nfn facingTerm(n: vec3<f32>) -> f32 {\n return clamp(n.z * 0.5 + 0.5, 0.0, 1.0); // 1 = dead-front, 0 = dead-back\n}\n\n// A 2D dot product \u2014 the local 'let dot = dots[ii].data' SHADOWS the WGSL 'dot()' builtin\n// inside vs_main, so the lens math calls this helper instead (the same value, un-shadowed).\nfn dot2(a: vec2<f32>, b: vec2<f32>) -> f32 {\n return a.x * b.x + a.y * b.y;\n}\n\n// Sample the multi-stop palette (linear-sRGB stops, OKLab mix). t in [0,1].\nfn samplePaletteLin(t: f32) -> vec3<f32> {\n let stopCount = u.ints.x;\n if (stopCount <= 1) { return u.palette[0].rgb; }\n let n = f32(stopCount);\n let ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n let i0 = i32(floor(ft));\n let i1 = min(i0 + 1, stopCount - 1);\n let f = ft - f32(i0);\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i0].rgb);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i1].rgb);\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\nstruct VSOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) local: vec2<f32>, // [-1,1] billboard-local coords for the SDF circle\n @location(1) bright: f32, // facing-driven brightness [0, ~1.6]\n @location(2) alpha: f32, // facing-driven opacity\n @location(3) tone: f32, // palette tone (0 = bright cream near-front, 1 = amber rim)\n @location(4) cast: f32, // 1 = the cartoon offset-cast shadow billboard, 0 = the lit dot\n};\n\n@vertex\nfn vs_main(\n @builtin(vertex_index) vi: u32,\n @builtin(instance_index) ii: u32,\n) -> VSOut {\n // Two-triangle quad corners in billboard-local space (camera-facing by construction).\n // 12 verts/instance: 0..5 the LIT dot, 6..11 the cartoon offset-CAST shadow duplicate (the\n // SAME instanced draw, zero new buffer \u2014 the 1940s layered-offset cast as a binding precept).\n var corners = array<vec2<f32>, 6>(\n vec2<f32>(-1.0, -1.0), vec2<f32>(1.0, -1.0), vec2<f32>(-1.0, 1.0),\n vec2<f32>(-1.0, 1.0), vec2<f32>(1.0, -1.0), vec2<f32>(1.0, 1.0),\n );\n let isCast = vi >= 6u;\n let local = corners[vi % 6u];\n\n let dot = dots[ii].data;\n var unitPos = dot.xyz; // the STATIC phyllotaxis position (JS-computed)\n let sphereIdx = dot.w;\n\n let breath = u.u4.w;\n let pAct = u.u3.w;\n let pointerNdc = vec2<f32>(u.u4.x * 2.0 - 1.0, 1.0 - u.u4.y * 2.0);\n let isPlane = u.u6.x > 0.5;\n let gravStrength = u.u6.y;\n let gravRadius = max(u.u6.z, 1e-3);\n let planeScale = u.u6.w;\n // The reclaimed travel velocity \u2192 the lens-frame vector (host +y is DOWN \u2192 flip to NDC +up).\n let velRaw = vec2<f32>(u.u7.x, -u.u7.y);\n let vmag = length(velRaw) * VEL_SCALE;\n let vdir = select(vec2<f32>(0.0, 0.0), velRaw / max(length(velRaw), 1e-5), length(velRaw) > 1e-5);\n let wake = u.u7.w; // the spring-engaged active (\u03B6=0.7) \u2192 the wake LAGS + overshoots\n let twkRate = u.u7.z;\n // The per-dot twinkle phase \u2014 an on-GPU instance_index hash (no new buffer); the resting\n // field becomes a LIVING warm constellation. uTime is pinned under PRM (deterministic capture).\n let twkPhase = fract(sin(f32(ii) * 12.9898) * 43758.5453) * TAU;\n let twk = twkRate * sin(u.u0.w * (twkRate + 0.6) + twkPhase);\n\n var center2d: vec2<f32>;\n var facing: f32;\n var sizeTaper: f32;\n var burstScale: f32;\n var facingOpacity: f32;\n var burstBright: f32;\n var castLobe: f32 = 0.0; // the cartoon-cast strength (nearness\u00B7pAct), set per-register\n\n if (isPlane) {\n // \u2500\u2500 The 2D-PLANE register \u2192 the DIRECTIONAL LIQUID LENS \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // A heavy liquid-glass lens dragged across a taut warm membrane: it LEADS its travel,\n // STRETCHES the gathered dots along the travel axis, drags an ember comet-tail WAKE behind,\n // and MAGNIFIES+refracts the lattice beneath it. (Replaces the symmetric 'toCursor*well' \u2014\n // clean break, no alias, no dual path; the reclaimed velocity makes the warp directional.)\n let base2d = unitPos.xy * planeScale;\n\n // 1. ANTICIPATION \u2014 the lens LEADS its math-center along travel (dots ahead feel it early).\n let wellC = pointerNdc + vdir * wake * (gravRadius * INV_PHI2 * 0.4);\n let toLens = wellC - base2d;\n\n // 2. SQUASH / STRETCH \u2014 measure distance in a frame STRETCHED along travel (vol-preserving).\n let along = dot2(toLens, vdir);\n let perp = dot2(toLens, vec2<f32>(-vdir.y, vdir.x));\n let stretch = 1.0 + clamp(vmag * STRETCH_GAIN, 0.0, INV_SQRT_PHI);\n let dE = length(vec2<f32>(along / stretch, perp * stretch));\n let well = gravStrength * exp(-(dE * dE) / (2.0 * gravRadius * gravRadius)) * pAct;\n var lift = toLens * well;\n\n // 3. COMET-TAIL WAKE \u2014 a pull streamed BEHIND the lens (overlapping action; follow-through).\n let behind = max(0.0, -along);\n let tail = wake * exp(-(behind * behind) / (gravRadius * gravRadius)) * vmag * WAKE_GAIN;\n lift = lift - vdir * tail * gravRadius;\n // The flick BURST over-extends the gather then the spring snaps it (the cartoon take).\n let burstPull = toLens * (u.u3.z * 0.4 * exp(-(dE * dE) / (gravRadius * gravRadius))) * pAct;\n center2d = base2d + lift + burstPull;\n\n // 4. LENS OPTICS \u2014 the ellipse nearness drives magnify + brightness + the chromatic rim.\n let nearness = exp(-(dE * dE) / (2.0 * gravRadius * gravRadius));\n facing = nearness;\n sizeTaper = 0.8 + LENS_MAGNIFY * nearness * pAct; // the dot SWELLS (a lens magnifies)\n burstScale = 1.0 + u.u3.z * 0.6 * nearness;\n // the comet-tail also reads via LUMINANCE (trailing dots glow \u221D behind\u00B7vmag \u2014 the PRIMARY\n // wake witness; the displacement alone is fragile, the spike proved).\n let wakeGlow = behind * vmag * 1.4 * pAct;\n // 5. CONTENT-DEFERENTIAL VIGNETTE \u2014 dense edges, clear center (the field FRAMES the content);\n // the cursor-lens RE-DENSIFIES the center it enters (the calm-until-touched duality).\n var presence = mix(VIGNETTE_FLOOR, 1.0, smoothstep(0.0, 1.0, length(base2d) / max(planeScale, 1e-3)));\n presence = max(presence, nearness * pAct);\n // the resting TWINKLE floor + the lens bloom; the vignette modulates the whole field.\n facingOpacity = (u.u2.x + u.u2.y * (0.5 + twk + 0.6 * nearness * pAct)) * presence;\n burstBright = 1.0 + (0.5 * nearness + wakeGlow + u.u3.z * 0.8 * nearness) * pAct;\n castLobe = nearness * pAct; // the cartoon offset-cast under the magnified lozenge\n } else {\n // \u2500\u2500 The 3D-SPHERE register (the KEPT preset \u2014 \"good\") \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n var sphereScale = u.u1.x; // uRadius\n var sphereOffset = vec2<f32>(0.0, 0.0);\n if (sphereIdx > 0.5) {\n sphereScale = u.u1.x * u.u4.z; // uRadius * uSphere2Scale\n sphereOffset = vec2<f32>(u.u5.x, u.u5.y); // uSphere2Offset\n }\n let n = u.spin * unitPos; // the rotated surface NORMAL (the \u00A7T2 facing source)\n facing = facingTerm(n);\n let screenN = n.xy * sphereScale + sphereOffset;\n // The SAME screen-space DIRECTIONAL LENS as the plane (one warp idiom, both registers \u2014\n // the dim-idiom, no second mechanism): the surface dots smear toward the cursor ALONG\n // travel + carry a wake (a finger pressing a dot-globe through glass). Bounded by 'facing'\n // so the near hemisphere leads + the silhouette can't tear.\n let toLensS = pointerNdc - screenN;\n let alongS = dot2(toLensS, vdir);\n let perpS = dot2(toLensS, vec2<f32>(-vdir.y, vdir.x));\n let stretchS = 1.0 + clamp(vmag * STRETCH_GAIN, 0.0, INV_SQRT_PHI);\n let dES = length(vec2<f32>(alongS / stretchS, perpS * stretchS));\n let pd = dES;\n // A repel pushes OUT, an attract pulls IN (u.u3.y sign); facing bounds the near hemisphere.\n let well = exp(-(dES * dES) / (2.0 * gravRadius * gravRadius)) * gravStrength * u.u3.y * pAct * facing;\n var lift = -toLensS * well; // toward (attract) / away (repel) per the u3.y sign baked above\n // The comet-tail WAKE on the near hemisphere (streams behind the cursor along travel).\n let behindS = max(0.0, -alongS);\n let tailS = wake * exp(-(behindS * behindS) / (gravRadius * gravRadius)) * vmag * WAKE_GAIN * facing;\n lift = lift - vdir * tailS * gravRadius * sign(u.u3.y);\n let parallaxOff = pointerNdc * u.u3.x * pAct;\n center2d = n.xy * sphereScale * breath + sphereOffset + lift + parallaxOff;\n let nearnessS = exp(-(dES * dES) / (2.0 * gravRadius * gravRadius)) * facing;\n sizeTaper = u.u2.z + u.u2.w * facing + LENS_MAGNIFY * 0.4 * nearnessS * pAct; // the lens magnify\n burstScale = 1.0 + u.u3.z * exp(-(dES * dES) / (gravRadius * gravRadius));\n let wakeGlowS = behindS * vmag * 1.0 * pAct * facing;\n facingOpacity = u.u2.x + u.u2.y * facing + u.u2.y * twk * facing; // the twinkle rides the facing\n burstBright = 1.0 + (u.u3.z * 0.8 * exp(-(dES * dES) / (gravRadius * gravRadius)) + wakeGlowS) * pAct;\n castLobe = nearnessS * pAct; // the cartoon offset-cast under the smeared near-hemisphere\n }\n\n let dotR = u.u1.y * sizeTaper * burstScale; // uDotSize \u00B7 taper \u00B7 burst\n let offX = local.x * dotR;\n let offY = local.y * dotR;\n\n // THE CARTOON OFFSET-CAST (the 1940s layered-offset shadow, a binding precept). Verts 6..11\n // are a darkened, lower-\u03B1 duplicate billboard offset along a \u221A\u03C6 light vector (down-right),\n // keyed off castLobe (nearness\u00B7pAct) so the shadow lobe appears ONLY under the magnified lens\n // lozenge. When castLobe \u2248 0 the cast collapses to alpha 0 (the fragment discards it).\n let lightVec = normalize(vec2<f32>(SQRT_PHI, -1.0)) * CAST_OFFSET * dotR;\n let castShift = select(vec2<f32>(0.0, 0.0), lightVec, isCast);\n let castStrength = select(1.0, smoothstep(0.05, 0.5, castLobe), isCast);\n\n // The whole sphere + quad is built in a SQUARE space (so the globe stays circular); the\n // final clip-space x divides by aspect so a wide canvas does not squash it into an\n // ellipse (aspect \u2265 1 \u2192 x shrinks; aspect < 1 \u2192 x grows). This keeps the dot quads round\n // AND the sphere circular in PIXELS regardless of the canvas aspect.\n let aspect = max(u.u1.w, 1e-4);\n let clipX = (center2d.x + offX + castShift.x) / aspect;\n let clipY = center2d.y + offY + castShift.y;\n\n var out: VSOut;\n out.pos = vec4<f32>(clipX, clipY, 0.0, 1.0);\n out.local = local;\n out.cast = select(0.0, 1.0, isCast);\n if (isCast) {\n // The cast is a dark shadow lobe \u2014 full dark tone, lower \u03B1, gated by the cast strength.\n out.bright = CAST_DARK;\n out.alpha = clamp(u.u1.z * CAST_ALPHA * facingOpacity * castStrength, 0.0, 1.0);\n out.tone = 1.0;\n } else {\n out.bright = facingOpacity * burstBright;\n out.alpha = clamp(u.u1.z * facingOpacity * burstBright, 0.0, 1.0);\n // The chromatic RIM refraction (REPLACES the old depth-only '1.0 - facing' tone \u2014 EXCISED,\n // no double-write, no legacy). At rest the tone reads the depth/edge; under the lens a faint\n // RIM band glows the warmer-amber stop (tone \u2192 1), the iOS-27 glass chromatic refraction.\n let rim = smoothstep(0.25, 0.6, 1.0 - facing) * RIM_CHROMA;\n out.tone = clamp(max(1.0 - facing, rim * facing), 0.0, 1.0);\n }\n return out;\n}\n\n@fragment\nfn fs_main(in: VSOut) -> @location(0) vec4<f32> {\n // The crisp fwidth-smoothstep SDF circle (the ONE AA canon \u2014 the band is ~1px wide at\n // any DPR/zoom, so fine sub-2px dots stay crisp). d = |uv| - 1; aa = fwidth(d).\n let d = length(in.local) - 1.0;\n let aa = max(fwidth(d), 1e-4);\n let coverage = 1.0 - smoothstep(-aa, aa, d);\n if (coverage < 0.002) { discard; }\n // The cartoon-cast billboard collapses to nothing away from the lens \u2014 drop the dead quads.\n if (in.cast > 0.5 && in.alpha < 0.003) { discard; }\n\n // The palette tints the dot by DEPTH (near-front cream \u2192 rim amber); the brightness lights\n // the core. The near hemisphere reads as the bright warm-cream stop, the rim/far fading to\n // the darker amber \u2014 the translucent dot-shell on both grounds.\n let lin = samplePaletteLin(in.tone) * clamp(in.bright, 0.0, 1.6);\n let alpha = in.alpha * coverage;\n let rgb = clamp(linearToSrgb(lin), vec3<f32>(0.0), vec3<f32>(1.0));\n // Premultiplied-alpha output (the translucent dot-shell additive over the clear).\n return vec4<f32>(rgb * alpha, alpha);\n}\n";
|
|
@@ -46,6 +46,10 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {
|
|
|
46
46
|
wake: () => void;
|
|
47
47
|
renderAt: (timeSec: number) => void;
|
|
48
48
|
setHeadT: (t: number) => void;
|
|
49
|
+
headUnit: () => {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
} | null;
|
|
49
53
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
50
54
|
seed: string;
|
|
51
55
|
freeze: boolean;
|
|
@@ -10,6 +10,15 @@ export interface FourierGLSetupDeps {
|
|
|
10
10
|
getHeadT: () => number;
|
|
11
11
|
shouldContinue: () => boolean;
|
|
12
12
|
onFrame?: (timeSec: number) => void;
|
|
13
|
+
/**
|
|
14
|
+
* BD.W-VIZ-BROKEN-FIX D6b — the 2-D cursor FOLLOW (model-space center lean). Mirrors the
|
|
15
|
+
* WGSL arm exactly (both read the SAME uFit center) — parity-safe by construction.
|
|
16
|
+
* `{x:0,y:0}` (ambient/PRM) is byte-identical to today.
|
|
17
|
+
*/
|
|
18
|
+
getPointerLean?: () => {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
};
|
|
13
22
|
}
|
|
14
23
|
/** Build the `setupGL(gl)` callback the `createGpuSubstrate` WebGL2 path invokes. */
|
|
15
24
|
export declare function createFourierGLSetup(deps: FourierGLSetupDeps): (gl: WebGL2RenderingContext) => WebGLCanvasFrame;
|
|
@@ -15,6 +15,17 @@ export interface FourierWGPUSetupDeps {
|
|
|
15
15
|
shouldContinue: () => boolean;
|
|
16
16
|
/** The per-frame pointer hook — useFourierField advances the shared pointer field here. */
|
|
17
17
|
onFrame?: (timeSec: number) => void;
|
|
18
|
+
/**
|
|
19
|
+
* BD.W-VIZ-BROKEN-FIX D6b — the 2-D cursor FOLLOW: a small MODEL-space offset added to
|
|
20
|
+
* the view-fit center so the whole reconstruction LEANS toward the cursor (the spatial
|
|
21
|
+
* "follow" beside D6a's velocity scrub). Reuses the EXISTING uFit center uniform (NO new
|
|
22
|
+
* uniform, NO shader/bridge edit — parity-safe by construction, both arms read the same
|
|
23
|
+
* fit). `{x:0,y:0}` (the ambient/PRM register) is byte-identical to today.
|
|
24
|
+
*/
|
|
25
|
+
getPointerLean?: () => {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
};
|
|
18
29
|
}
|
|
19
30
|
/** Build the fourier-field `setupWGPU(device, context, format)` callback. */
|
|
20
31
|
export declare function createFourierWGPUSetup(deps: FourierWGPUSetupDeps): (device: GPUDevice, context: GPUCanvasContext, format: GPUTextureFormat) => WebGPUCanvasFrame;
|
|
@@ -26,6 +26,21 @@ export interface FourierFit {
|
|
|
26
26
|
* inverts this transform per fragment so the curve never clips and the scale is fixed.
|
|
27
27
|
*/
|
|
28
28
|
export declare function computeFourierFit(spectrum: readonly BasisComponent[]): FourierFit;
|
|
29
|
+
/**
|
|
30
|
+
* Map a MODEL-space head point to the stage's `[0,1]²` UV via the SAME fit + aspect the
|
|
31
|
+
* render's clip→model inverse uses (BD.W-FOURIER-LOOM §2a — the bloom-desync fence). The
|
|
32
|
+
* render maps clip = (model − center)·scale with the x clip un-aspect-corrected (clip.x is
|
|
33
|
+
* scaled by aspect in the inverse), so the forward is:
|
|
34
|
+
* clipX = (mx − cx)·scale / aspect, clipY = (my − cy)·scale, clip ∈ [-1,1].
|
|
35
|
+
* Then UV = (clipX·0.5 + 0.5, 1 − (clipY·0.5 + 0.5)) (CSS y grows DOWN; the lean is folded
|
|
36
|
+
* into `center` upstream so the bloom tracks the LEANED head). The result is NOT clamped —
|
|
37
|
+
* a head that leaves the stage carries the bloom off-edge honestly; the consumer clamps if
|
|
38
|
+
* it must paint a sprite. Pure arithmetic off the cached `fit` — no second CPU fit to drift.
|
|
39
|
+
*/
|
|
40
|
+
export declare function headToUnit(headX: number, headY: number, fit: FourierFit, aspect: number): {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
};
|
|
29
44
|
/**
|
|
30
45
|
* Convert a CSS-px stroke width to MODEL units (the shader's space). The fit `scale` maps
|
|
31
46
|
* model → clip; clip spans [-1,1] over the min CSS dimension → cssPx-per-clip = canvasCssMin/2.
|
|
@@ -33,7 +48,7 @@ export declare function computeFourierFit(spectrum: readonly BasisComponent[]):
|
|
|
33
48
|
*/
|
|
34
49
|
export declare function trailWidthToModel(trailWidthPx: number, scale: number, canvasCssMin: number): number;
|
|
35
50
|
/** Pack the render uniforms in place. */
|
|
36
|
-
export declare function packFourierRenderUniforms(scratch: FourierUniformScratch, fit: FourierFit, aspect: number, trailWidthModel: number, peakAlpha: number, headGlowAlpha: number, trailFadeExp: number, trailFloor: number, intensity: number, showEpicycles: boolean, rainbowChain: boolean, sampleCount: number, armCount: number, palette: OklchStop[]): FourierUniformScratch;
|
|
51
|
+
export declare function packFourierRenderUniforms(scratch: FourierUniformScratch, fit: FourierFit, aspect: number, trailWidthModel: number, peakAlpha: number, headGlowAlpha: number, trailFadeExp: number, trailFloor: number, intensity: number, showEpicycles: boolean, rainbowChain: boolean, squashGain: number, celGain: number, sampleCount: number, armCount: number, palette: OklchStop[]): FourierUniformScratch;
|
|
37
52
|
/**
|
|
38
53
|
* Pack the phasor storage table (re, im, index, _pad) from the CPU-minted spectrum. The
|
|
39
54
|
* WGSL SUMS this per frame — it is the ONLY spectrum upload (no WGSL-side mint).
|
|
@@ -2,7 +2,7 @@ import { type Ref } from "vue";
|
|
|
2
2
|
import { type GpuBackend } from "../../../../composables/glass/webgpu/useGpuSubstrate";
|
|
3
3
|
import type { OklchStop } from "../../../../composables/color";
|
|
4
4
|
import type { FourierFieldConfig } from "../constants";
|
|
5
|
-
import type
|
|
5
|
+
import { type BasisComponent } from "../math";
|
|
6
6
|
export interface UseFourierFieldOptions {
|
|
7
7
|
config: FourierFieldConfig;
|
|
8
8
|
/** The active phasor spectrum (CPU-minted; re-read each frame so a source-swap reaches the buffer). */
|
|
@@ -17,6 +17,17 @@ export interface UseFourierFieldOptions {
|
|
|
17
17
|
periodS?: number;
|
|
18
18
|
/** `"capture"` → renderAt-only (the deterministic π capture path). Default `"live"`. */
|
|
19
19
|
mode?: "live" | "capture";
|
|
20
|
+
/**
|
|
21
|
+
* BD.W-FOURIER-LOOM §2a — the lit-field seam hook. Fired ONCE per frame from INSIDE the
|
|
22
|
+
* ONE clock's `onFrame` (NO second rAF), AFTER `headT` advances, with the head's `[0,1]²`
|
|
23
|
+
* UV (or `null` before the canvas is sized). The SFC writes `--ff-head-xy` on its host so
|
|
24
|
+
* the warm field's phosphor bloom tracks the comet. PRM → fired with the STABLE frozen-T
|
|
25
|
+
* UV (the bloom seats, no sweep).
|
|
26
|
+
*/
|
|
27
|
+
onHeadFrame?: (unit: {
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
} | null) => void;
|
|
20
31
|
}
|
|
21
32
|
/** The uniform handle a consumer wires its lifecycle against (backend-agnostic). */
|
|
22
33
|
export interface FourierFieldHandle {
|
|
@@ -36,6 +47,16 @@ export interface FourierFieldHandle {
|
|
|
36
47
|
readonly headT: number;
|
|
37
48
|
/** Scrub the clock directly (the transport scrubber / a pointer drag). */
|
|
38
49
|
setHeadT: (t: number) => void;
|
|
50
|
+
/**
|
|
51
|
+
* BD.W-FOURIER-LOOM §2a — the comet head mapped to the stage's `[0,1]²` UV, off the SAME
|
|
52
|
+
* cached fit the GPU comet paints from. The SFC writes this to `--ff-head-xy` per frame so
|
|
53
|
+
* the warm field's phosphor bloom tracks the head (the lit-field seam). `null` before the
|
|
54
|
+
* canvas is sized. PRM → a STABLE frozen-T value (the bloom seats, no sweep).
|
|
55
|
+
*/
|
|
56
|
+
headUnit: () => {
|
|
57
|
+
x: number;
|
|
58
|
+
y: number;
|
|
59
|
+
} | null;
|
|
39
60
|
/** Tear down the renderer + release GPU/GL resources. */
|
|
40
61
|
dispose: () => void;
|
|
41
62
|
}
|
|
@@ -21,6 +21,46 @@ export declare const MAX_PHASORS = 64;
|
|
|
21
21
|
export declare const MAX_CURVE_SAMPLES = 384;
|
|
22
22
|
/** The palette-stop ceiling — the WGSL `MAX_FOURIER_STOPS` #define. */
|
|
23
23
|
export declare const MAX_FOURIER_STOPS = 4;
|
|
24
|
+
/**
|
|
25
|
+
* The cursor-velocity → head_t clock-coupling gain (BD.W-VIZ-BROKEN-FIX D6 — the weighted
|
|
26
|
+
* scrub). The cursor MOTION (not its absolute X) nudges the loop clock: a flick fast-
|
|
27
|
+
* forwards, a still cursor lets the field drift at its config speed. Velocity-continuous —
|
|
28
|
+
* the head never teleports (the prior `headT = pointerX` absolute-X scrub read as "not
|
|
29
|
+
* following" a 2-D cursor).
|
|
30
|
+
*/
|
|
31
|
+
export declare const SCRUB_GAIN = 0.15;
|
|
32
|
+
/**
|
|
33
|
+
* The 2-D cursor-FOLLOW lean depth (BD.W-VIZ-BROKEN-FIX D6b — the spatial follow beside the
|
|
34
|
+
* velocity scrub). A bounded fraction of the model half-span the view-fit center leans
|
|
35
|
+
* toward the cursor, so the whole reconstruction pans TOWARD the cursor on screen (a gentle
|
|
36
|
+
* pull, never a yank — the [[feedback-liquid-weight-universal]] weighted register). At 0
|
|
37
|
+
* the field is byte-identical (the ambient/non-interactive register).
|
|
38
|
+
*/
|
|
39
|
+
export declare const FOLLOW_LEAN = 0.12;
|
|
40
|
+
/**
|
|
41
|
+
* The degenerate-tangent guard (BD.W-FOURIER-LOOM §2b). At a cusp the head's instantaneous
|
|
42
|
+
* speed `s = length(head − headBack)` collapses to ~0 and the unit tangent `T = d/s` blows
|
|
43
|
+
* up; below `FOURIER_TANGENT_EPS` the shaders + the GL CPU bead path all fall back to the
|
|
44
|
+
* LAST stable tangent, so `s→0` resolves IDENTICALLY across both engines (the cross-engine
|
|
45
|
+
* cusp-parity fence — one shared constant, no per-engine drift).
|
|
46
|
+
*/
|
|
47
|
+
export declare const FOURIER_TANGENT_EPS = 0.0001;
|
|
48
|
+
/**
|
|
49
|
+
* The head squash-and-stretch gain (BD.W-FOURIER-LOOM §2b). The round head bead becomes a
|
|
50
|
+
* volume-preserving anisotropic ellipse: the tangent extent scales `1 + k·ŝ`, the normal
|
|
51
|
+
* extent `1/(1 + k·ŝ)`, where `ŝ` is the normalized head speed. The library identity carries
|
|
52
|
+
* a SMALL non-zero gain (cartoon-weight at rest — the head is never a dead disc); a consumer
|
|
53
|
+
* vivid preset turns it to 11. `0` = the byte-frozen round disc.
|
|
54
|
+
*/
|
|
55
|
+
export declare const FOURIER_SQUASH_GAIN = 0.55;
|
|
56
|
+
/**
|
|
57
|
+
* The cartoon cel-shadow gain (BD.W-FOURIER-LOOM §3b). Each chain ring/arm/dot + the comet
|
|
58
|
+
* rope paints a second darker copy OFFSET opposite its travel — the 1940s technicolor ink.
|
|
59
|
+
* The library identity carries a SMALL non-zero gain (a quiet weight under the chain); a
|
|
60
|
+
* consumer vivid preset floors it UP. `0` = no cel pass (byte-frozen). The offset rides the
|
|
61
|
+
* head tangent, mirrored on both engines off the SAME `curveSamples` tangent.
|
|
62
|
+
*/
|
|
63
|
+
export declare const FOURIER_CEL_GAIN = 0.35;
|
|
24
64
|
/**
|
|
25
65
|
* The full author schema — the studio's `useConfiguratorState` model. Every field is a
|
|
26
66
|
* tunable the demo configurator drives; the composable resolves it into the uniform table.
|
|
@@ -52,6 +92,19 @@ export interface FourierFieldConfig {
|
|
|
52
92
|
respectReducedMotion: boolean;
|
|
53
93
|
/** Pointer scrubs `head_t` + a flick injects clock momentum (the W-VIZ-INTERACTION consume). */
|
|
54
94
|
interactive: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Head squash-and-stretch gain (BD.W-FOURIER-LOOM §2b). The head bead stretches along the
|
|
97
|
+
* curve tangent on straights, squashes across it on corners (volume-preserving). The
|
|
98
|
+
* library identity carries a small non-zero default (cartoon-weight at rest); a vivid
|
|
99
|
+
* consumer preset turns it to 11. 0 = the byte-frozen round disc.
|
|
100
|
+
*/
|
|
101
|
+
squashGain: number;
|
|
102
|
+
/**
|
|
103
|
+
* Cartoon cel-shadow gain (BD.W-FOURIER-LOOM §3b). A second darker copy of the chain + rope,
|
|
104
|
+
* offset opposite the head travel (the technicolor ink). The library identity carries a
|
|
105
|
+
* small non-zero default; a vivid consumer preset floors it UP. 0 = no cel pass.
|
|
106
|
+
*/
|
|
107
|
+
celGain: number;
|
|
55
108
|
}
|
|
56
109
|
/**
|
|
57
110
|
* The warm-cream identity palette (the library default — NOT teal). A two-stop warm-amber
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** The full-screen-triangle vertex shader (the aurora/concentric pattern). */
|
|
2
2
|
export declare const FOURIER_FIELD_VERT_GLSL = "#version 300 es\nin vec2 aPosition;\nout vec2 vClip;\nvoid main() {\n vClip = aPosition;\n gl_Position = vec4(aPosition, 0.0, 1.0);\n}\n";
|
|
3
3
|
/** The fragment SDF — the GLSL twin of fourier-field.render.wgsl.ts's fs_main. */
|
|
4
|
-
export declare const FOURIER_FIELD_FRAG_GLSL = "#version 300 es\nprecision highp float;\n\n#define MAX_CURVE_SAMPLES 256\n#define MAX_PHASORS 64\n\nin vec2 vClip;\nout vec4 fragColor;\n\nuniform vec4 uFit; // (centerX, centerY, scale, aspect)\nuniform vec4 uTrail; // (trailWidth, peakAlpha, headGlowAlpha, trailFadeExp)\nuniform vec4 uEnv; // (trailFloor, intensity, showEpicycles, rainbowChain)\nuniform int uSampleCount;\nuniform int uArmCount;\nuniform int uStopCount;\nuniform vec3 uPalette[4]; // linear-sRGB stops\n// curveSamples[i] = (x, y, age) in MODEL space; chainTips[k] = (x, y) in MODEL space.\nuniform vec3 uCurve[MAX_CURVE_SAMPLES];\nuniform vec2 uChain[MAX_PHASORS];\n\nconst float PI = 3.141592653589793;\n\n\nfloat srgbToLinearCh(float c) {\n return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);\n}\nvec3 srgbToLinear(vec3 c) {\n return vec3(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfloat linearToSrgbCh(float c) {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nvec3 linearToSrgb(vec3 c) {\n return vec3(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS (constants.ts), row-major; written here as GLSL\n// columns (= the transpose) so mat3 * vec3 evaluates the row-major M\u00B7v.\nconst mat3 LINEAR_SRGB_TO_LMS = mat3(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients (gamut.ts lines 295-297 \u2014\n// these differ at the ~1e-9 digit from LMS_TO_OKLAB_MATRIX; use the inline ones to\n// mirror the exact value.js path), row-major \u2192 GLSL columns.\nconst mat3 LMS_TO_OKLAB = mat3(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF (constants.ts), row-major \u2192 GLSL columns. Rows:\n// l = [1, 0.3963377774, 0.2158037573], m = [1, -0.1055613458, -0.0638541728],\n// s = [1, -0.0894841775, -1.2914855480].\nconst mat3 OKLAB_TO_LMS = mat3(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB (constants.ts), row-major \u2192 GLSL columns.\nconst mat3 LMS_TO_LINEAR_SRGB = mat3(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nvec3 srgbToOklab(vec3 c) {\n vec3 lin = srgbToLinear(c);\n vec3 lms = LINEAR_SRGB_TO_LMS * lin;\n vec3 lmsCbrt = sign(lms) * pow(abs(lms), vec3(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nvec3 oklabToLinearSrgb(vec3 lab) {\n vec3 lms_ = OKLAB_TO_LMS * lab;\n vec3 lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS. Mirrors value.js rawOklabToOklch (which returns\n// degrees; we stay in radians and only fold to [0, 2pi)).\nvec3 oklabToOklch(vec3 lab) {\n float C = length(lab.yz);\n float H = atan(lab.z, lab.y);\n if (H < 0.0) H += 2.0 * PI;\n return vec3(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nvec3 oklchToOklab(vec3 lch) {\n return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\n// Negative-safe cube root (the WGSL chunk's cbrt3 twin; the GLSL chunk inlines it).\nvec3 cbrt3(vec3 v) { return sign(v) * pow(abs(v), vec3(1.0 / 3.0)); }\n\nvec3 samplePaletteLin(float t) {\n if (uStopCount <= 1) return uPalette[0];\n float n = float(uStopCount);\n float ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n int i0 = int(floor(ft));\n int i1 = min(i0 + 1, uStopCount - 1);\n float f = ft - float(i0);\n vec3 labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * uPalette[i0]);\n vec3 labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * uPalette[i1]);\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\nvec3 chainColorLin(float seg, bool rainbow) {\n vec3 baseLin = uPalette[0];\n if (!rainbow) return baseLin;\n vec3 lab = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * baseLin);\n vec3 lch = oklabToOklch(lab);\n float hueShift = mix(-0.45, 1.15, clamp(seg, 0.0, 1.0));\n vec3 lifted = vec3(0.66, max(lch.y, 0.14), lch.z + hueShift);\n return oklabToLinearSrgb(oklchToOklab(lifted));\n}\n\nfloat segDist(vec2 p, vec2 a, vec2 b) {\n vec2 pa = p - a;\n vec2 ba = b - a;\n float h = clamp(dot(pa, ba) / max(dot(ba, ba), 1e-8), 0.0, 1.0);\n return length(pa - ba * h);\n}\n\nvoid main() {\n vec2 center = uFit.xy;\n float scale = max(uFit.z, 1e-6);\n float aspect = max(uFit.w, 1e-4);\n // clip \u2192 model (the inverse of model\u2192clip fit).\n vec2 p = center + vec2(vClip.x * aspect, vClip.y) / scale;\n\n float aa = max(fwidth(p.x) + fwidth(p.y), 1e-5) * 0.75;\n float trailWidth = uTrail.x; // already in model units (packed CPU-side)\n float peak = uTrail.y * uEnv.y;\n float headGlow = uTrail.z * uEnv.y;\n float fadeExp = uTrail.w;\n float trailFloor = uEnv.x;\n bool showEpicycles = uEnv.z > 0.5;\n bool rainbow = uEnv.w > 0.5;\n float halfW = trailWidth * 0.5;\n\n vec4 accum = vec4(0.0);\n\n if (showEpicycles && uArmCount >= 1) {\n for (int k = 0; k < MAX_PHASORS; k++) {\n if (k >= uArmCount) break;\n vec2 a = uChain[k];\n vec2 b = uChain[k + 1];\n float seg = float(k) / max(float(uArmCount - 1), 1.0);\n vec3 lin = chainColorLin(seg, rainbow);\n float radius = length(b - a);\n float dRing = abs(length(p - a) - radius);\n float ringW = max(halfW * 0.45, aa);\n float ringMask = (1.0 - smoothstep(ringW, ringW + aa, dRing)) * 0.5;\n float dArm = segDist(p, a, b);\n float armMask = 1.0 - smoothstep(halfW * 0.55, halfW * 0.55 + aa, dArm);\n float dotR = max(halfW * 0.7, aa * 2.0);\n float dDot = length(p - a);\n float dotMask = 1.0 - smoothstep(dotR, dotR + aa, dDot);\n float m = clamp(max(max(ringMask, armMask), dotMask), 0.0, 1.0) * peak * 0.7;\n vec3 rgb = clamp(linearToSrgb(lin), vec3(0.0), vec3(1.0));\n accum = vec4(rgb * m, m) + accum * (1.0 - m);\n }\n }\n\n for (int i = 0; i < MAX_CURVE_SAMPLES; i++) {\n if (i >= uSampleCount - 1) break;\n vec3 sa = uCurve[i];\n vec3 sb = uCurve[i + 1];\n float d = segDist(p, sa.xy, sb.xy);\n float cover = 1.0 - smoothstep(halfW, halfW + aa, d);\n if (cover < 0.002) continue;\n float age = max(sa.z, sb.z);\n float a = peak * pow(age, fadeExp);\n a = max(a, peak * trailFloor) * cover;\n vec3 lin = samplePaletteLin(1.0 - age * 0.4);\n vec3 rgb = clamp(linearToSrgb(lin), vec3(0.0), vec3(1.0));\n accum = vec4(rgb * a, a) + accum * (1.0 - a);\n }\n\n if (uSampleCount >= 1) {\n vec2 head = uCurve[0].xy;\n float dHead = length(p - head);\n float coreR = halfW * 1.5;\n float haloR = coreR * 3.0;\n vec3 headRgb = clamp(linearToSrgb(samplePaletteLin(0.0)), vec3(0.0), vec3(1.0));\n float halo = (1.0 - smoothstep(0.0, haloR, dHead)) * headGlow * 0.35;\n accum = vec4(headRgb * halo, halo) + accum * (1.0 - halo);\n float core = (1.0 - smoothstep(coreR, coreR + aa, dHead)) * headGlow;\n accum = vec4(headRgb * core, core) + accum * (1.0 - core);\n float spec = (1.0 - smoothstep(coreR * 0.4, coreR * 0.4 + aa, dHead)) * headGlow * 0.9;\n accum = vec4(vec3(1.0) * spec, spec) + accum * (1.0 - spec);\n }\n\n fragColor = accum;\n}\n";
|
|
4
|
+
export declare const FOURIER_FIELD_FRAG_GLSL = "#version 300 es\nprecision highp float;\n\n#define MAX_CURVE_SAMPLES 256\n#define MAX_PHASORS 64\n// BD.W-FOURIER-LOOM \u00A72b \u2014 MUST equal the WGSL twin + FOURIER_TANGENT_EPS (constants.ts).\n#define TANGENT_EPS 1e-4\n#define SPEED_REF_HALFWIDTHS 6.0\n\nin vec2 vClip;\nout vec4 fragColor;\n\nuniform vec4 uFit; // (centerX, centerY, scale, aspect)\nuniform vec4 uTrail; // (trailWidth, peakAlpha, headGlowAlpha, trailFadeExp)\nuniform vec4 uEnv; // (trailFloor, intensity, showEpicycles, rainbowChain)\nuniform vec4 uLoom; // (squashGain, celGain, _pad, _pad) \u2014 FOURIER-LOOM \u00A72b/\u00A73b\nuniform int uSampleCount;\nuniform int uArmCount;\nuniform int uStopCount;\nuniform vec3 uPalette[4]; // linear-sRGB stops\n// curveSamples[i] = (x, y, age) in MODEL space; chainTips[k] = (x, y) in MODEL space.\nuniform vec3 uCurve[MAX_CURVE_SAMPLES];\nuniform vec2 uChain[MAX_PHASORS];\n\nconst float PI = 3.141592653589793;\n\n\nfloat srgbToLinearCh(float c) {\n return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);\n}\nvec3 srgbToLinear(vec3 c) {\n return vec3(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfloat linearToSrgbCh(float c) {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nvec3 linearToSrgb(vec3 c) {\n return vec3(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS (constants.ts), row-major; written here as GLSL\n// columns (= the transpose) so mat3 * vec3 evaluates the row-major M\u00B7v.\nconst mat3 LINEAR_SRGB_TO_LMS = mat3(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients (gamut.ts lines 295-297 \u2014\n// these differ at the ~1e-9 digit from LMS_TO_OKLAB_MATRIX; use the inline ones to\n// mirror the exact value.js path), row-major \u2192 GLSL columns.\nconst mat3 LMS_TO_OKLAB = mat3(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF (constants.ts), row-major \u2192 GLSL columns. Rows:\n// l = [1, 0.3963377774, 0.2158037573], m = [1, -0.1055613458, -0.0638541728],\n// s = [1, -0.0894841775, -1.2914855480].\nconst mat3 OKLAB_TO_LMS = mat3(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB (constants.ts), row-major \u2192 GLSL columns.\nconst mat3 LMS_TO_LINEAR_SRGB = mat3(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nvec3 srgbToOklab(vec3 c) {\n vec3 lin = srgbToLinear(c);\n vec3 lms = LINEAR_SRGB_TO_LMS * lin;\n vec3 lmsCbrt = sign(lms) * pow(abs(lms), vec3(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nvec3 oklabToLinearSrgb(vec3 lab) {\n vec3 lms_ = OKLAB_TO_LMS * lab;\n vec3 lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS. Mirrors value.js rawOklabToOklch (which returns\n// degrees; we stay in radians and only fold to [0, 2pi)).\nvec3 oklabToOklch(vec3 lab) {\n float C = length(lab.yz);\n float H = atan(lab.z, lab.y);\n if (H < 0.0) H += 2.0 * PI;\n return vec3(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nvec3 oklchToOklab(vec3 lch) {\n return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\n// Negative-safe cube root (the WGSL chunk's cbrt3 twin; the GLSL chunk inlines it).\nvec3 cbrt3(vec3 v) { return sign(v) * pow(abs(v), vec3(1.0 / 3.0)); }\n\nvec3 samplePaletteLin(float t) {\n if (uStopCount <= 1) return uPalette[0];\n float n = float(uStopCount);\n float ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n int i0 = int(floor(ft));\n int i1 = min(i0 + 1, uStopCount - 1);\n float f = ft - float(i0);\n vec3 labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * uPalette[i0]);\n vec3 labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * uPalette[i1]);\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\nvec3 chainColorLin(float seg, bool rainbow) {\n vec3 baseLin = uPalette[0];\n if (!rainbow) return baseLin;\n vec3 lab = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * baseLin);\n vec3 lch = oklabToOklch(lab);\n float hueShift = mix(-0.45, 1.15, clamp(seg, 0.0, 1.0));\n vec3 lifted = vec3(0.66, max(lch.y, 0.14), lch.z + hueShift);\n return oklabToLinearSrgb(oklchToOklab(lifted));\n}\n\nfloat segDist(vec2 p, vec2 a, vec2 b) {\n vec2 pa = p - a;\n vec2 ba = b - a;\n float h = clamp(dot(pa, ba) / max(dot(ba, ba), 1e-8), 0.0, 1.0);\n return length(pa - ba * h);\n}\n\n// BD.W-FOURIER-LOOM \u00A72b \u2014 the head travel frame (T, \u015D), the GLSL twin of the WGSL headFrame.\n// The cusp guard returns +x as the stable fallback when s \u2264 TANGENT_EPS \u2014 identical to the\n// WGSL twin + the CPU bead path.\nstruct HeadFrame { vec2 T; float sHat; };\nHeadFrame headFrame(vec2 head, vec2 headBack, float halfW) {\n vec2 d = head - headBack;\n float s = length(d);\n HeadFrame fr;\n if (s <= TANGENT_EPS) { fr.T = vec2(1.0, 0.0); fr.sHat = 0.0; return fr; }\n fr.T = d / s;\n float refScale = max(halfW * SPEED_REF_HALFWIDTHS, 1e-6);\n fr.sHat = clamp(s / refScale, 0.0, 1.0);\n return fr;\n}\n\n// The volume-preserving anisotropic head distance (the WGSL headAniso twin).\nfloat headAniso(vec2 p, vec2 head, HeadFrame fr, float k) {\n float g = 1.0 + k * fr.sHat;\n vec2 n = vec2(-fr.T.y, fr.T.x);\n vec2 rel = p - head;\n float along = dot(rel, fr.T) / g;\n float across = dot(rel, n) * g;\n return length(vec2(along, across));\n}\n\nvoid main() {\n vec2 center = uFit.xy;\n float scale = max(uFit.z, 1e-6);\n float aspect = max(uFit.w, 1e-4);\n // clip \u2192 model (the inverse of model\u2192clip fit).\n vec2 p = center + vec2(vClip.x * aspect, vClip.y) / scale;\n\n float aa = max(fwidth(p.x) + fwidth(p.y), 1e-5) * 0.75;\n float trailWidth = uTrail.x; // already in model units (packed CPU-side)\n float peak = uTrail.y * uEnv.y;\n float headGlow = uTrail.z * uEnv.y;\n float fadeExp = uTrail.w;\n float trailFloor = uEnv.x;\n bool showEpicycles = uEnv.z > 0.5;\n bool rainbow = uEnv.w > 0.5;\n float squashGain = uLoom.x; // FOURIER-LOOM \u00A72b\n float celGain = uLoom.y; // FOURIER-LOOM \u00A73b\n float halfW = trailWidth * 0.5;\n\n // \u00A72b \u2014 the head travel frame (SAME uCurve[0]/[1] the WGSL twin reads \u2192 parity).\n HeadFrame headFr;\n headFr.T = vec2(1.0, 0.0);\n headFr.sHat = 0.0;\n if (uSampleCount >= 2) {\n headFr = headFrame(uCurve[0].xy, uCurve[1].xy, halfW);\n }\n vec2 celOff = -headFr.T * halfW * 1.4;\n\n vec4 accum = vec4(0.0);\n\n // \u00A73b \u2014 the cartoon CEL-SHADOW: a darker offset copy of the rope + chain, opposite travel,\n // painted FIRST (under the lit chain). PRM-static; celGain = 0 \u2192 no pass (byte-frozen).\n if (celGain > 0.001) {\n vec3 ink = vec3(0.0);\n float celA = clamp(celGain, 0.0, 1.0);\n for (int i = 0; i < MAX_CURVE_SAMPLES; i++) {\n if (i >= uSampleCount - 1) break;\n vec2 sa = uCurve[i].xy + celOff;\n vec2 sb = uCurve[i + 1].xy + celOff;\n float d = segDist(p, sa, sb);\n float cover = 1.0 - smoothstep(halfW, halfW + aa, d);\n if (cover < 0.002) continue;\n float m = cover * celA * peak;\n accum = vec4(ink * m, m) + accum * (1.0 - m);\n }\n if (showEpicycles && uArmCount >= 1) {\n for (int k = 0; k < MAX_PHASORS; k++) {\n if (k >= uArmCount) break;\n vec2 a = uChain[k] + celOff;\n vec2 b = uChain[k + 1] + celOff;\n float dArm = segDist(p, a, b);\n float armMask = 1.0 - smoothstep(halfW * 0.55, halfW * 0.55 + aa, dArm);\n float m = armMask * celA * peak * 0.7;\n accum = vec4(ink * m, m) + accum * (1.0 - m);\n }\n }\n }\n\n if (showEpicycles && uArmCount >= 1) {\n for (int k = 0; k < MAX_PHASORS; k++) {\n if (k >= uArmCount) break;\n vec2 a = uChain[k];\n vec2 b = uChain[k + 1];\n float seg = float(k) / max(float(uArmCount - 1), 1.0);\n vec3 lin = chainColorLin(seg, rainbow);\n float radius = length(b - a);\n float dRing = abs(length(p - a) - radius);\n float ringW = max(halfW * 0.45, aa);\n float ringMask = (1.0 - smoothstep(ringW, ringW + aa, dRing)) * 0.5;\n float dArm = segDist(p, a, b);\n float armMask = 1.0 - smoothstep(halfW * 0.55, halfW * 0.55 + aa, dArm);\n float dotR = max(halfW * 0.7, aa * 2.0);\n float dDot = length(p - a);\n float dotMask = 1.0 - smoothstep(dotR, dotR + aa, dDot);\n float m = clamp(max(max(ringMask, armMask), dotMask), 0.0, 1.0) * peak * 0.7;\n vec3 rgb = clamp(linearToSrgb(lin), vec3(0.0), vec3(1.0));\n accum = vec4(rgb * m, m) + accum * (1.0 - m);\n }\n }\n\n for (int i = 0; i < MAX_CURVE_SAMPLES; i++) {\n if (i >= uSampleCount - 1) break;\n vec3 sa = uCurve[i];\n vec3 sb = uCurve[i + 1];\n float d = segDist(p, sa.xy, sb.xy);\n float cover = 1.0 - smoothstep(halfW, halfW + aa, d);\n if (cover < 0.002) continue;\n float age = max(sa.z, sb.z);\n float a = peak * pow(age, fadeExp);\n a = max(a, peak * trailFloor) * cover;\n vec3 lin = samplePaletteLin(1.0 - age * 0.4);\n vec3 rgb = clamp(linearToSrgb(lin), vec3(0.0), vec3(1.0));\n accum = vec4(rgb * a, a) + accum * (1.0 - a);\n }\n\n if (uSampleCount >= 1) {\n vec2 head = uCurve[0].xy;\n // \u00A72b \u2014 the squash-and-stretch anisotropic head (collapses to the round disc at rest).\n float dHead = headAniso(p, head, headFr, squashGain);\n float coreR = halfW * 1.5;\n float haloR = coreR * 3.0;\n vec3 headRgb = clamp(linearToSrgb(samplePaletteLin(0.0)), vec3(0.0), vec3(1.0));\n float halo = (1.0 - smoothstep(0.0, haloR, dHead)) * headGlow * 0.35;\n accum = vec4(headRgb * halo, halo) + accum * (1.0 - halo);\n float core = (1.0 - smoothstep(coreR, coreR + aa, dHead)) * headGlow;\n accum = vec4(headRgb * core, core) + accum * (1.0 - core);\n float spec = (1.0 - smoothstep(coreR * 0.4, coreR * 0.4 + aa, dHead)) * headGlow * 0.9;\n accum = vec4(vec3(1.0) * spec, spec) + accum * (1.0 - spec);\n }\n\n fragColor = accum;\n}\n";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const FOURIER_FIELD_RENDER_WGSL = "\nconst PI: f32 = 3.141592653589793;\nconst TAU: f32 = 6.283185307179586;\nconst MAX_CURVE_SAMPLES: i32 = 384;\nconst MAX_PHASORS: i32 = 64;\nconst MAX_FOURIER_STOPS: i32 = 4;\n\n// \u2500\u2500 Render uniforms (the typed-struct source-of-truth \u2014 see uniformBridgeWGPU.ts) \u2500\u2500\nstruct RenderUniforms {\n // r0: (centerX, centerY, scale, aspect) \u2014 model\u2192clip fit transform\n r0: vec4<f32>,\n // r1: (trailWidth, peakAlpha, headGlowAlpha, trailFadeExp)\n r1: vec4<f32>,\n // r2: (trailFloor, intensity, showEpicycles, rainbowChain)\n r2: vec4<f32>,\n // ints: (sampleCount, armCount, stopCount, _pad)\n ints: vec4<i32>,\n // palette stops: linear-sRGB rgb + _pad\n palette: array<vec4<f32>, 4>,\n};\n\n@group(0) @binding(0) var<uniform> u: RenderUniforms;\n// curveSamples[i] = (x, y, age, _pad) in MODEL space\n@group(0) @binding(1) var<storage, read> curveSamples: array<vec4<f32>>;\n// chainTips[k] = (x, y, _pad, _pad) in MODEL space\n@group(0) @binding(2) var<storage, read> chainTips: array<vec4<f32>>;\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n if (c <= 0.04045) { return c / 12.92; }\n return pow((c + 0.055) / 1.055, 2.4);\n}\nfn srgbToLinear(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfn linearToSrgbCh(c: f32) -> f32 {\n if (c <= 0.0031308) { return c * 12.92; }\n return 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nfn linearToSrgb(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS, row-major \u2192 WGSL columns (= the transpose).\nconst LINEAR_SRGB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients, row-major \u2192 WGSL columns.\nconst LMS_TO_OKLAB: mat3x3<f32> = mat3x3<f32>(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF, row-major \u2192 WGSL columns.\nconst OKLAB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB, row-major \u2192 WGSL columns.\nconst LMS_TO_LINEAR_SRGB: mat3x3<f32> = mat3x3<f32>(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn cbrt3(v: vec3<f32>) -> vec3<f32> {\n return sign(v) * pow(abs(v), vec3<f32>(1.0 / 3.0));\n}\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nfn srgbToOklab(c: vec3<f32>) -> vec3<f32> {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nfn oklabToLinearSrgb(lab: vec3<f32>) -> vec3<f32> {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS, folded to [0, 2pi). Mirrors value.js rawOklabToOklch.\nfn oklabToOklch(lab: vec3<f32>) -> vec3<f32> {\n let C = length(lab.yz);\n var H = atan2(lab.z, lab.y);\n if (H < 0.0) { H = H + 2.0 * PI; }\n return vec3<f32>(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nfn oklchToOklab(lch: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\n// Sample the multi-stop palette (linear-sRGB stops, OKLab mix). t in [0,1].\nfn samplePaletteLin(t: f32) -> vec3<f32> {\n let stopCount = u.ints.z;\n if (stopCount <= 1) { return u.palette[0].rgb; }\n let n = f32(stopCount);\n let ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n let i0 = i32(floor(ft));\n let i1 = min(i0 + 1, stopCount - 1);\n let f = ft - f32(i0);\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i0].rgb);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i1].rgb);\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\n// A warm-anchored chain hue sweep over the palette base \u2014 the rainbow chain register.\n// Tilts the base hue by a tight warm-ward band so the chain stays colourful but warm.\nfn chainColorLin(seg: f32, rainbow: bool) -> vec3<f32> {\n let baseLin = u.palette[0].rgb;\n if (!rainbow) { return baseLin; }\n let lab = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * baseLin);\n let lch = oklabToOklch(lab);\n // warm-anchored: a touch cool at the root climbing warm toward the tips.\n let hueShift = mix(-0.45, 1.15, clamp(seg, 0.0, 1.0)); // radians (~ -26\u00B0..+66\u00B0)\n let h = lch.z + hueShift;\n let lifted = vec3<f32>(0.66, max(lch.y, 0.14), h);\n return oklabToLinearSrgb(oklchToOklab(lifted));\n}\n\n// Distance from point p to segment ab (model space).\nfn segDist(p: vec2<f32>, a: vec2<f32>, b: vec2<f32>) -> f32 {\n let pa = p - a;\n let ba = b - a;\n let h = clamp(dot(pa, ba) / max(dot(ba, ba), 1e-8), 0.0, 1.0);\n return length(pa - ba * h);\n}\n\nstruct VSOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) model: vec2<f32>, // model-space coordinate of this fragment\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {\n // Full-screen triangle (covers the viewport with 3 verts).\n var p = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -3.0), vec2<f32>(-1.0, 1.0), vec2<f32>(3.0, 1.0),\n );\n let clip = p[vi];\n // Inverse of the model\u2192clip fit: clip = (model \u2212 center)\u00B7scale, aspect-corrected x.\n // \u2192 model = center + clip\u00B7(1/scale), x un-aspect-corrected.\n let center = u.r0.xy;\n let scale = max(u.r0.z, 1e-6);\n let aspect = max(u.r0.w, 1e-4);\n let model = center + vec2<f32>(clip.x * aspect, clip.y) / scale;\n\n var out: VSOut;\n out.pos = vec4<f32>(clip, 0.0, 1.0);\n out.model = model;\n return out;\n}\n\n@fragment\nfn fs_main(in: VSOut) -> @location(0) vec4<f32> {\n let p = in.model;\n // AA feather: one device pixel in model units (fwidth of the model coord).\n let aa = max(fwidth(p.x) + fwidth(p.y), 1e-5) * 0.75;\n\n let trailWidth = u.r1.x; // already in model units (packed CPU-side)\n let peak = u.r1.y * u.r2.y; // peakAlpha \u00B7 intensity\n let headGlow = u.r1.z * u.r2.y;\n let fadeExp = u.r1.w;\n let trailFloor = u.r2.x;\n let sampleCount = u.ints.x;\n let armCount = u.ints.y;\n let showEpicycles = u.r2.z > 0.5;\n let rainbow = u.r2.w > 0.5;\n let halfW = trailWidth * 0.5;\n\n var accum = vec4<f32>(0.0); // premultiplied accumulation\n\n // \u2500\u2500 Epicycle scaffolding (orbit rings + arms + joint dots) UNDER the curve \u2500\u2500\n if (showEpicycles && armCount >= 1) {\n for (var k = 0; k < MAX_PHASORS; k = k + 1) {\n if (k >= armCount) { break; }\n let a = chainTips[k].xy;\n let b = chainTips[k + 1].xy;\n let seg = f32(k) / max(f32(armCount - 1), 1.0);\n let lin = chainColorLin(seg, rainbow);\n // orbit RING around a (radius = arm length).\n let radius = length(b - a);\n let dRing = abs(length(p - a) - radius);\n let ringW = max(halfW * 0.45, aa);\n let ringMask = (1.0 - smoothstep(ringW, ringW + aa, dRing)) * 0.5;\n // ARM segment a\u2192b.\n let dArm = segDist(p, a, b);\n let armMask = 1.0 - smoothstep(halfW * 0.55, halfW * 0.55 + aa, dArm);\n // joint DOT at a.\n let dotR = max(halfW * 0.7, aa * 2.0);\n let dDot = length(p - a);\n let dotMask = 1.0 - smoothstep(dotR, dotR + aa, dDot);\n let m = clamp(max(max(ringMask, armMask), dotMask), 0.0, 1.0) * peak * 0.7;\n let rgb = clamp(linearToSrgb(lin), vec3<f32>(0.0), vec3<f32>(1.0));\n // over-composite (premultiplied).\n accum = vec4<f32>(rgb * m, m) + accum * (1.0 - m);\n }\n }\n\n // \u2500\u2500 The comet TRAIL \u2014 min-distance to the curve polyline, per-segment age fade \u2500\u2500\n for (var i = 0; i < MAX_CURVE_SAMPLES; i = i + 1) {\n if (i >= sampleCount - 1) { break; }\n let sa = curveSamples[i];\n let sb = curveSamples[i + 1];\n let d = segDist(p, sa.xy, sb.xy);\n let cover = 1.0 - smoothstep(halfW, halfW + aa, d);\n if (cover < 0.002) { continue; }\n let age = max(sa.z, sb.z); // 1 at head, 0 at tail\n var a = peak * pow(age, fadeExp);\n a = max(a, peak * trailFloor);\n a = a * cover;\n let lin = samplePaletteLin(1.0 - age * 0.4); // warm core, lighter tail\n let rgb = clamp(linearToSrgb(lin), vec3<f32>(0.0), vec3<f32>(1.0));\n accum = vec4<f32>(rgb * a, a) + accum * (1.0 - a);\n }\n\n // \u2500\u2500 The comet HEAD \u2014 halo + saturated core + white specular at curveSamples[0] \u2500\u2500\n if (sampleCount >= 1) {\n let head = curveSamples[0].xy;\n let dHead = length(p - head);\n let coreR = halfW * 1.5;\n let haloR = coreR * 3.0;\n let headLin = samplePaletteLin(0.0);\n let headRgb = clamp(linearToSrgb(headLin), vec3<f32>(0.0), vec3<f32>(1.0));\n // soft halo\n let halo = (1.0 - smoothstep(0.0, haloR, dHead)) * headGlow * 0.35;\n accum = vec4<f32>(headRgb * halo, halo) + accum * (1.0 - halo);\n // saturated core\n let core = (1.0 - smoothstep(coreR, coreR + aa, dHead)) * headGlow;\n accum = vec4<f32>(headRgb * core, core) + accum * (1.0 - core);\n // white specular\n let spec = (1.0 - smoothstep(coreR * 0.4, coreR * 0.4 + aa, dHead)) * headGlow * 0.9;\n accum = vec4<f32>(vec3<f32>(1.0) * spec, spec) + accum * (1.0 - spec);\n }\n\n return accum;\n}\n";
|
|
1
|
+
export declare const FOURIER_FIELD_RENDER_WGSL = "\nconst PI: f32 = 3.141592653589793;\nconst TAU: f32 = 6.283185307179586;\nconst MAX_CURVE_SAMPLES: i32 = 384;\nconst MAX_PHASORS: i32 = 64;\nconst MAX_FOURIER_STOPS: i32 = 4;\n// BD.W-FOURIER-LOOM \u00A72b \u2014 the degenerate-tangent guard (must equal FOURIER_TANGENT_EPS in\n// constants.ts + the GL CPU bead path so a cusp resolves IDENTICALLY across both engines).\nconst TANGENT_EPS: f32 = 1e-4;\n// The speed yardstick: the head speed (model units between curveSamples[0] and [1]) is\n// normalized against this many trail half-widths so \u015D \u2208 [0,1] is scale-free + parity-safe.\nconst SPEED_REF_HALFWIDTHS: f32 = 6.0;\n\n// \u2500\u2500 Render uniforms (the typed-struct source-of-truth \u2014 see uniformBridgeWGPU.ts) \u2500\u2500\nstruct RenderUniforms {\n // r0: (centerX, centerY, scale, aspect) \u2014 model\u2192clip fit transform\n r0: vec4<f32>,\n // r1: (trailWidth, peakAlpha, headGlowAlpha, trailFadeExp)\n r1: vec4<f32>,\n // r2: (trailFloor, intensity, showEpicycles, rainbowChain)\n r2: vec4<f32>,\n // r3: (squashGain, celGain, _pad, _pad) \u2014 FOURIER-LOOM \u00A72b/\u00A73b\n r3: vec4<f32>,\n // ints: (sampleCount, armCount, stopCount, _pad)\n ints: vec4<i32>,\n // palette stops: linear-sRGB rgb + _pad\n palette: array<vec4<f32>, 4>,\n};\n\n@group(0) @binding(0) var<uniform> u: RenderUniforms;\n// curveSamples[i] = (x, y, age, _pad) in MODEL space\n@group(0) @binding(1) var<storage, read> curveSamples: array<vec4<f32>>;\n// chainTips[k] = (x, y, _pad, _pad) in MODEL space\n@group(0) @binding(2) var<storage, read> chainTips: array<vec4<f32>>;\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n if (c <= 0.04045) { return c / 12.92; }\n return pow((c + 0.055) / 1.055, 2.4);\n}\nfn srgbToLinear(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfn linearToSrgbCh(c: f32) -> f32 {\n if (c <= 0.0031308) { return c * 12.92; }\n return 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nfn linearToSrgb(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS, row-major \u2192 WGSL columns (= the transpose).\nconst LINEAR_SRGB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients, row-major \u2192 WGSL columns.\nconst LMS_TO_OKLAB: mat3x3<f32> = mat3x3<f32>(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF, row-major \u2192 WGSL columns.\nconst OKLAB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB, row-major \u2192 WGSL columns.\nconst LMS_TO_LINEAR_SRGB: mat3x3<f32> = mat3x3<f32>(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn cbrt3(v: vec3<f32>) -> vec3<f32> {\n return sign(v) * pow(abs(v), vec3<f32>(1.0 / 3.0));\n}\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nfn srgbToOklab(c: vec3<f32>) -> vec3<f32> {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nfn oklabToLinearSrgb(lab: vec3<f32>) -> vec3<f32> {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS, folded to [0, 2pi). Mirrors value.js rawOklabToOklch.\nfn oklabToOklch(lab: vec3<f32>) -> vec3<f32> {\n let C = length(lab.yz);\n var H = atan2(lab.z, lab.y);\n if (H < 0.0) { H = H + 2.0 * PI; }\n return vec3<f32>(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nfn oklchToOklab(lch: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\n// Sample the multi-stop palette (linear-sRGB stops, OKLab mix). t in [0,1].\nfn samplePaletteLin(t: f32) -> vec3<f32> {\n let stopCount = u.ints.z;\n if (stopCount <= 1) { return u.palette[0].rgb; }\n let n = f32(stopCount);\n let ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n let i0 = i32(floor(ft));\n let i1 = min(i0 + 1, stopCount - 1);\n let f = ft - f32(i0);\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i0].rgb);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * u.palette[i1].rgb);\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\n// A warm-anchored chain hue sweep over the palette base \u2014 the rainbow chain register.\n// Tilts the base hue by a tight warm-ward band so the chain stays colourful but warm.\nfn chainColorLin(seg: f32, rainbow: bool) -> vec3<f32> {\n let baseLin = u.palette[0].rgb;\n if (!rainbow) { return baseLin; }\n let lab = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * baseLin);\n let lch = oklabToOklch(lab);\n // warm-anchored: a touch cool at the root climbing warm toward the tips.\n let hueShift = mix(-0.45, 1.15, clamp(seg, 0.0, 1.0)); // radians (~ -26\u00B0..+66\u00B0)\n let h = lch.z + hueShift;\n let lifted = vec3<f32>(0.66, max(lch.y, 0.14), h);\n return oklabToLinearSrgb(oklchToOklab(lifted));\n}\n\n// Distance from point p to segment ab (model space).\nfn segDist(p: vec2<f32>, a: vec2<f32>, b: vec2<f32>) -> f32 {\n let pa = p - a;\n let ba = b - a;\n let h = clamp(dot(pa, ba) / max(dot(ba, ba), 1e-8), 0.0, 1.0);\n return length(pa - ba * h);\n}\n\n// BD.W-FOURIER-LOOM \u00A72b \u2014 the head travel frame: the unit tangent T from curveSamples[0]\n// (head) vs [1] (one step back) + the normalized speed \u015D \u2208 [0,1]. The cusp guard returns\n// the +x axis as the stable fallback when s \u2264 TANGENT_EPS, identical to the GL CPU path.\nstruct HeadFrame { T: vec2<f32>, sHat: f32, };\nfn headFrame(head: vec2<f32>, headBack: vec2<f32>, halfW: f32) -> HeadFrame {\n let d = head - headBack;\n let s = length(d);\n var out: HeadFrame;\n if (s <= TANGENT_EPS) {\n out.T = vec2<f32>(1.0, 0.0);\n out.sHat = 0.0;\n return out;\n }\n out.T = d / s;\n let refScale = max(halfW * SPEED_REF_HALFWIDTHS, 1e-6);\n out.sHat = clamp(s / refScale, 0.0, 1.0);\n return out;\n}\n\n// The volume-preserving anisotropic head distance: project (p \u2212 head) onto T and T\u22A5, stretch\n// the tangent extent by (1 + k\u00B7\u015D) and squeeze the normal extent by 1/(1 + k\u00B7\u015D), then length.\n// k = 0 (or \u015D = 0) collapses to the round disc length(p \u2212 head) \u2014 byte-frozen at rest.\nfn headAniso(p: vec2<f32>, head: vec2<f32>, fr: HeadFrame, k: f32) -> f32 {\n let g = 1.0 + k * fr.sHat;\n let n = vec2<f32>(-fr.T.y, fr.T.x);\n let rel = p - head;\n let along = dot(rel, fr.T) / g;\n let across = dot(rel, n) * g;\n return length(vec2<f32>(along, across));\n}\n\nstruct VSOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) model: vec2<f32>, // model-space coordinate of this fragment\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {\n // Full-screen triangle (covers the viewport with 3 verts).\n var p = array<vec2<f32>, 3>(\n vec2<f32>(-1.0, -3.0), vec2<f32>(-1.0, 1.0), vec2<f32>(3.0, 1.0),\n );\n let clip = p[vi];\n // Inverse of the model\u2192clip fit: clip = (model \u2212 center)\u00B7scale, aspect-corrected x.\n // \u2192 model = center + clip\u00B7(1/scale), x un-aspect-corrected.\n let center = u.r0.xy;\n let scale = max(u.r0.z, 1e-6);\n let aspect = max(u.r0.w, 1e-4);\n let model = center + vec2<f32>(clip.x * aspect, clip.y) / scale;\n\n var out: VSOut;\n out.pos = vec4<f32>(clip, 0.0, 1.0);\n out.model = model;\n return out;\n}\n\n@fragment\nfn fs_main(in: VSOut) -> @location(0) vec4<f32> {\n let p = in.model;\n // AA feather: one device pixel in model units (fwidth of the model coord).\n let aa = max(fwidth(p.x) + fwidth(p.y), 1e-5) * 0.75;\n\n let trailWidth = u.r1.x; // already in model units (packed CPU-side)\n let peak = u.r1.y * u.r2.y; // peakAlpha \u00B7 intensity\n let headGlow = u.r1.z * u.r2.y;\n let fadeExp = u.r1.w;\n let trailFloor = u.r2.x;\n let sampleCount = u.ints.x;\n let armCount = u.ints.y;\n let showEpicycles = u.r2.z > 0.5;\n let rainbow = u.r2.w > 0.5;\n let squashGain = u.r3.x; // FOURIER-LOOM \u00A72b\n let celGain = u.r3.y; // FOURIER-LOOM \u00A73b\n let halfW = trailWidth * 0.5;\n\n // BD.W-FOURIER-LOOM \u00A72b \u2014 the head travel frame (the SAME curveSamples[0]/[1] both engines\n // read \u2192 parity by construction). The cel offset (\u00A73b) rides \u2212T (opposite travel).\n var headFr: HeadFrame;\n headFr.T = vec2<f32>(1.0, 0.0);\n headFr.sHat = 0.0;\n if (sampleCount >= 2) {\n headFr = headFrame(curveSamples[0].xy, curveSamples[1].xy, halfW);\n }\n let celOff = -headFr.T * halfW * 1.4; // the ink lags behind the travel\n\n var accum = vec4<f32>(0.0); // premultiplied accumulation\n\n // \u2500\u2500 \u00A73b \u2014 the cartoon CEL-SHADOW: a darker offset copy of the chain + rope, opposite\n // travel, painted FIRST (it sits UNDER the lit chain). PRM-static (the frozen-T travel\n // frame is a fixed offset, no live sweep). celGain = 0 \u2192 no pass (byte-frozen). \u2500\u2500\n if (celGain > 0.001) {\n let ink = vec3<f32>(0.0); // pure ink \u2014 the technicolor 2-tone shadow\n let celA = clamp(celGain, 0.0, 1.0);\n // the rope cel (one offset distance-to-curve band).\n for (var i = 0; i < MAX_CURVE_SAMPLES; i = i + 1) {\n if (i >= sampleCount - 1) { break; }\n let sa = curveSamples[i].xy + celOff;\n let sb = curveSamples[i + 1].xy + celOff;\n let d = segDist(p, sa, sb);\n let cover = 1.0 - smoothstep(halfW, halfW + aa, d);\n if (cover < 0.002) { continue; }\n let m = cover * celA * peak;\n accum = vec4<f32>(ink * m, m) + accum * (1.0 - m);\n }\n // the chain cel (arms only \u2014 the bold scaffold ink).\n if (showEpicycles && armCount >= 1) {\n for (var k = 0; k < MAX_PHASORS; k = k + 1) {\n if (k >= armCount) { break; }\n let a = chainTips[k].xy + celOff;\n let b = chainTips[k + 1].xy + celOff;\n let dArm = segDist(p, a, b);\n let armMask = 1.0 - smoothstep(halfW * 0.55, halfW * 0.55 + aa, dArm);\n let m = armMask * celA * peak * 0.7;\n accum = vec4<f32>(ink * m, m) + accum * (1.0 - m);\n }\n }\n }\n\n // \u2500\u2500 Epicycle scaffolding (orbit rings + arms + joint dots) UNDER the curve \u2500\u2500\n if (showEpicycles && armCount >= 1) {\n for (var k = 0; k < MAX_PHASORS; k = k + 1) {\n if (k >= armCount) { break; }\n let a = chainTips[k].xy;\n let b = chainTips[k + 1].xy;\n let seg = f32(k) / max(f32(armCount - 1), 1.0);\n let lin = chainColorLin(seg, rainbow);\n // orbit RING around a (radius = arm length).\n let radius = length(b - a);\n let dRing = abs(length(p - a) - radius);\n let ringW = max(halfW * 0.45, aa);\n let ringMask = (1.0 - smoothstep(ringW, ringW + aa, dRing)) * 0.5;\n // ARM segment a\u2192b.\n let dArm = segDist(p, a, b);\n let armMask = 1.0 - smoothstep(halfW * 0.55, halfW * 0.55 + aa, dArm);\n // joint DOT at a.\n let dotR = max(halfW * 0.7, aa * 2.0);\n let dDot = length(p - a);\n let dotMask = 1.0 - smoothstep(dotR, dotR + aa, dDot);\n let m = clamp(max(max(ringMask, armMask), dotMask), 0.0, 1.0) * peak * 0.7;\n let rgb = clamp(linearToSrgb(lin), vec3<f32>(0.0), vec3<f32>(1.0));\n // over-composite (premultiplied).\n accum = vec4<f32>(rgb * m, m) + accum * (1.0 - m);\n }\n }\n\n // \u2500\u2500 The comet TRAIL \u2014 min-distance to the curve polyline, per-segment age fade \u2500\u2500\n for (var i = 0; i < MAX_CURVE_SAMPLES; i = i + 1) {\n if (i >= sampleCount - 1) { break; }\n let sa = curveSamples[i];\n let sb = curveSamples[i + 1];\n let d = segDist(p, sa.xy, sb.xy);\n let cover = 1.0 - smoothstep(halfW, halfW + aa, d);\n if (cover < 0.002) { continue; }\n let age = max(sa.z, sb.z); // 1 at head, 0 at tail\n var a = peak * pow(age, fadeExp);\n a = max(a, peak * trailFloor);\n a = a * cover;\n let lin = samplePaletteLin(1.0 - age * 0.4); // warm core, lighter tail\n let rgb = clamp(linearToSrgb(lin), vec3<f32>(0.0), vec3<f32>(1.0));\n accum = vec4<f32>(rgb * a, a) + accum * (1.0 - a);\n }\n\n // \u2500\u2500 The comet HEAD \u2014 squash-and-stretch (\u00A72b) halo + saturated core + white specular.\n // dHead is the volume-preserving anisotropic distance off the travel frame; at rest\n // (sHat=0) or squashGain=0 it collapses to the round disc length(p \u2212 head). \u2500\u2500\n if (sampleCount >= 1) {\n let head = curveSamples[0].xy;\n let dHead = headAniso(p, head, headFr, squashGain);\n let coreR = halfW * 1.5;\n let haloR = coreR * 3.0;\n let headLin = samplePaletteLin(0.0);\n let headRgb = clamp(linearToSrgb(headLin), vec3<f32>(0.0), vec3<f32>(1.0));\n // soft halo\n let halo = (1.0 - smoothstep(0.0, haloR, dHead)) * headGlow * 0.35;\n accum = vec4<f32>(headRgb * halo, halo) + accum * (1.0 - halo);\n // saturated core\n let core = (1.0 - smoothstep(coreR, coreR + aa, dHead)) * headGlow;\n accum = vec4<f32>(headRgb * core, core) + accum * (1.0 - core);\n // white specular\n let spec = (1.0 - smoothstep(coreR * 0.4, coreR * 0.4 + aa, dHead)) * headGlow * 0.9;\n accum = vec4<f32>(vec3<f32>(1.0) * spec, spec) + accum * (1.0 - spec);\n }\n\n return accum;\n}\n";
|
|
@@ -4,3 +4,4 @@ export declare function easeInOut(t: number): number;
|
|
|
4
4
|
export declare function easeIn(t: number): number;
|
|
5
5
|
/** Quadratic ease-out (the satellite retract ramp). */
|
|
6
6
|
export declare function easeOut(t: number): number;
|
|
7
|
+
export declare function fissionSnap(t: number): number;
|
|
@@ -9,6 +9,7 @@ export declare function useBlobSatellites(config: BlobConfig, initialColor: stri
|
|
|
9
9
|
tick: (now: number, mood: MoodParams) => void;
|
|
10
10
|
nudge: () => void;
|
|
11
11
|
reseed: (color: string) => void;
|
|
12
|
+
onPinch: (cb: (impulse: number) => void) => void;
|
|
12
13
|
isQuiescent: () => boolean;
|
|
13
14
|
nextEventMs: (now: number) => number;
|
|
14
15
|
};
|
|
@@ -53,5 +53,30 @@ export declare const BASE_OPACITY = 0.75;
|
|
|
53
53
|
export declare const ORBIT_BLEND_MS = 2000;
|
|
54
54
|
/** Minimum spacing (ms) between satellite merge events. */
|
|
55
55
|
export declare const MERGE_STAGGER_MS = 3000;
|
|
56
|
-
|
|
56
|
+
/**
|
|
57
|
+
* The bounded fission APEX (config-UV) — the orbit radius the fissioning satellite
|
|
58
|
+
* breathes OUT to at its excursion peak. Beyond the body skin (0.22) by a clean gap that
|
|
59
|
+
* exceeds the smin reach so the neck SNAPS; bounded so the single fissioner + apex rule
|
|
60
|
+
* re-prevents the "two unrelated discs" failure WITHOUT the blanket never-leaves-reach
|
|
61
|
+
* clamp. Paints at `FISSION_REACH_MAX · POS_SCALE` uv.
|
|
62
|
+
*/
|
|
63
|
+
export declare const FISSION_REACH_MAX = 0.4;
|
|
64
|
+
/** The fission PERIAPSIS (config-UV) — the merged-in radius the satellite gathers to before the snap (well inside the body skin, fully bonded). */
|
|
65
|
+
export declare const FISSION_REACH_MIN = 0.18;
|
|
66
|
+
/**
|
|
67
|
+
* The full fission BEAT duration (ms) — the SLOW gather + FAST snap + re-merge window the
|
|
68
|
+
* `fissioning` phase runs for. Long enough that the colony reads as a steady-state
|
|
69
|
+
* merge↔split breath (perpetually mid-merge on one satellite while mid-split on another),
|
|
70
|
+
* NOT a rare twitch. The `fissionSnap` curve maps the beat's `t in [0,1]` to the reach.
|
|
71
|
+
*/
|
|
72
|
+
export declare const FISSION_BEAT_MS = 5200;
|
|
73
|
+
/**
|
|
74
|
+
* The fission INTENSITY → cadence map. `fissionAmp` (0..1, config × mood) scales how
|
|
75
|
+
* OFTEN a satellite enters the fissioning beat vs the calm bonded breath: at amp 0 the
|
|
76
|
+
* fission probability is 0 (the calm default never splits); at amp 1 a satellite reaching
|
|
77
|
+
* its orbit-phase boundary enters `fissioning` with this probability instead of a plain
|
|
78
|
+
* merge. Mood-coupled (excited raises the effective amp, sleepy lowers it).
|
|
79
|
+
*/
|
|
80
|
+
export declare const FISSION_PROB_AT_FULL = 0.75;
|
|
81
|
+
export declare const UNIFORM_NAMES: readonly ["uResolution", "uTime", "uBaseColor", "uPointer", "uPointerActive", "uPointerAttraction", "uPointerStrength", "uBodyRadius", "uPulsePhase", "uPulseAmp", "uNoiseAmp", "uNoiseFreq", "uNoiseSpeed", "uWarpAmp", "uSmoothK", "uMerge", "uStage", "uMorphT", "uMaxReach", "uLit", "uRimColor", "uLightDir", "uSpecStrength", "uSpecShininess", "uRimPower", "uRimStrength", "uShadow", "uShadowSoftness", "uIridescence", "uIridHue", "uIridSpeed", "uSssScale", "uSssPower", "uCoreGlow", "uHueRange", "uSatShift", "uBrightnessShift", "uColorNoiseFreq", "uColorNoiseSpeed", "uStopCount", "uSatCount", "uTrailCount", "uVelocity", "uStretch"];
|
|
57
82
|
export type UniformName = (typeof UNIFORM_NAMES)[number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const METABALL_UNIFORMS_GLSL = "#version 300 es\nprecision highp float;\n\nin vec2 vUv;\nout vec4 fragColor;\n\nuniform vec2 uResolution;\nuniform float uTime;\nuniform vec3 uBaseColor;\n\n// Multi-stop palette (W11.b) \u2014 2-4 in-family OKLCh stops (uploaded GAMMA sRGB, like\n// uBaseColor). uStopCount <= 1 falls back to uBaseColor (the single-color default,\n// zero regression). MAX_BLOB_STOPS is a compile-time #define.\n#define MAX_BLOB_STOPS 4\nuniform int uStopCount;\nuniform vec3 uPalette[MAX_BLOB_STOPS];\n\n// Pointer\nuniform vec2 uPointer;\nuniform float uPointerActive;\nuniform float uPointerAttraction;\nuniform float uPointerStrength;\n\n// Main body\nuniform float uBodyRadius;\n\n// Pulsation\nuniform float uPulsePhase;\nuniform float uPulseAmp;\n\n// Surface noise\nuniform float uNoiseAmp;\nuniform float uNoiseFreq;\nuniform float uNoiseSpeed;\n\n// Gooey\nuniform float uSmoothK;\nuniform float uMerge; // 0 = quadratic smin, 1 = circular smin (rounder menisci)\n\n// BC.W-GOOBLOB-PLAIN \u2014 the STAGE-1 gate. 1.0 = the plain shadowless lightless\n// fill-only floor (variant=\"blob\"); 0.0 = the full lit pipeline (variant=\"meatball\" /\n// STAGE 2). The WebGL2 fallback transcribes the SAME stripped branch the WGSL primary\n// gates (research/viz/goo-blob.md \u00A75 \u2014 the GLSL twin).\nuniform float uStage;\n\n// AX.W16 (arm 5) \u2014 the PRE-FBM bounding-discard radius (UV space). main() early-outs\n// to a transparent write for any fragment OUTSIDE this radius BEFORE the two 3-octave\n// FBM calls + the OKLCh round-trip. The renderer uploads it PADDED by every\n// outward-expanding term (body + eccentric orbit + sat radius + smin band + FBM edge\n// amplitude + click-pulse + a lean/squash safety pad) so it NEVER clips the wet\n// meniscus (IQ: inflate the bound to match any outward op).\nuniform float uMaxReach;\n\n// Membrane \u2014 domain-warp strength on the FBM edge displacement (0 = plain fbm).\nuniform float uWarpAmp;\n\n// Lit glass surface (W9.b) \u2014 gated behind uLit so the flat fill stays default.\nuniform float uLit; // 0 = flat fill (default), 1 = lit droplet\nuniform vec3 uRimColor; // Fresnel rim tint (GAMMA sRGB, via the /color leaf)\nuniform vec3 uLightDir; // normalized light direction (x, y, z)\nuniform float uSpecStrength; // Blinn-Phong specular weight\nuniform float uSpecShininess; // specular exponent (16-64, tight glint)\nuniform float uRimPower; // Fresnel/Schlick exponent (~2-3)\nuniform float uRimStrength; // Fresnel rim weight\n\n// BC.W-GOOBLOB-MEATBALL \u2014 the procedural 2D SDF soft-shadow march (T2, the GLSL twin of\n// the WGSL res.z/res.w lanes). uShadow gates the IQ rmshadows soft contact shadow that\n// FOLLOWS the irregular silhouette; uShadowSoftness is the penumbra hardness (4-48).\nuniform float uShadow; // 0 = no shadow, 1 = the soft contact shadow ON\nuniform float uShadowSoftness; // penumbra hardness (inverse light-source size, 4-48)\n\n// Iridescence + fake subsurface (W11.a) \u2014 translucent-gel read.\nuniform float uIridescence; // warm-pearl rim sheen weight (0 = off)\nuniform float uIridHue; // base hue (radians) the cosine palette biases toward (warm)\nuniform float uIridSpeed; // animated-thickness scroll for the iridescent t\nuniform float uSssScale; // fast-SSS back-light weight\nuniform float uSssPower; // fast-SSS exponent\nuniform float uCoreGlow; // thickness-driven inner-luminosity lift\n\n// Color perturbation (perceptually uniform \u2014 OKLCh L/C/h)\nuniform float uHueRange; // degrees of hue swing (converted to radians)\nuniform float uSatShift; // OKLCh chroma swing\nuniform float uBrightnessShift; // OKLCh lightness bias\nuniform float uColorNoiseFreq;\nuniform float uColorNoiseSpeed;\n\n// Satellites (max 4)\n#define MAX_SATS 4\nuniform int uSatCount;\nuniform vec2 uSatPos[MAX_SATS];\nuniform float uSatRadius[MAX_SATS];\nuniform float uSatOpacity[MAX_SATS];\n\n// Pointer trail (W10) \u2014 a decaying-radius pseudopod toward the cursor. uTrail is a\n// COMPILE-TIME-SIZED array (GLSL ES 3.00 forbids a uniform-sized array) with a\n// DYNAMIC break on uTrailCount, mirroring the satellite loop. Later samples paint\n// smaller metaballs (r *= 1 - i/N) so the trail tapers into a tail.\n#define TRAIL_N 15\nuniform int uTrailCount;\nuniform vec2 uTrailPos[TRAIL_N];\nuniform float uTrailRadius[TRAIL_N];\n\n// Velocity-driven volume-preserving squash-and-stretch (W10).\nuniform vec2 uVelocity; // smoothed pointer velocity (motion direction + speed)\nuniform float uStretch; // squash-stretch magnitude (0 = off)\n\nconst float PI = 3.141592653589793;\n";
|
|
1
|
+
export declare const METABALL_UNIFORMS_GLSL = "#version 300 es\nprecision highp float;\n\nin vec2 vUv;\nout vec4 fragColor;\n\nuniform vec2 uResolution;\nuniform float uTime;\nuniform vec3 uBaseColor;\n\n// Multi-stop palette (W11.b) \u2014 2-4 in-family OKLCh stops (uploaded GAMMA sRGB, like\n// uBaseColor). uStopCount <= 1 falls back to uBaseColor (the single-color default,\n// zero regression). MAX_BLOB_STOPS is a compile-time #define.\n#define MAX_BLOB_STOPS 4\nuniform int uStopCount;\nuniform vec3 uPalette[MAX_BLOB_STOPS];\n\n// Pointer\nuniform vec2 uPointer;\nuniform float uPointerActive;\nuniform float uPointerAttraction;\nuniform float uPointerStrength;\n\n// Main body\nuniform float uBodyRadius;\n\n// Pulsation\nuniform float uPulsePhase;\nuniform float uPulseAmp;\n\n// Surface noise\nuniform float uNoiseAmp;\nuniform float uNoiseFreq;\nuniform float uNoiseSpeed;\n\n// Gooey\nuniform float uSmoothK;\nuniform float uMerge; // 0 = quadratic smin, 1 = circular smin (rounder menisci)\n\n// BC.W-GOOBLOB-PLAIN \u2014 the STAGE-1 gate. 1.0 = the plain shadowless lightless\n// fill-only floor (variant=\"blob\"); 0.0 = the full lit pipeline (variant=\"meatball\" /\n// STAGE 2). The WebGL2 fallback transcribes the SAME stripped branch the WGSL primary\n// gates (research/viz/goo-blob.md \u00A75 \u2014 the GLSL twin).\n// BD.W-GOO-CAROUSEL-DECK \u2014 uStage is now DERIVED from uMorphT (uStage = uMorphT<=0 ?\n// 1 : 0) at upload; the SHADING morph (below) reads uMorphT directly.\nuniform float uStage;\n\n// BD.W-GOO-CAROUSEL-DECK \u2014 the blob to meatball SHADING-MORPH scalar. 0.0 = the flat blob\n// fill (byte-identical to the STAGE-1 floor), 1.0 = the fully-dressed lit meatball; the\n// in-between LERPS the surface shading (NOT the geometry \u2014 the smin field is shared). The\n// renderer resolves it from config.morphT (variant to endpoint back-compat: blob to 0,\n// meatball to 1). A consumer ANIMATING morphT 0 to 1 gets the live morph.\nuniform float uMorphT;\n\n// AX.W16 (arm 5) \u2014 the PRE-FBM bounding-discard radius (UV space). main() early-outs\n// to a transparent write for any fragment OUTSIDE this radius BEFORE the two 3-octave\n// FBM calls + the OKLCh round-trip. The renderer uploads it PADDED by every\n// outward-expanding term (body + eccentric orbit + sat radius + smin band + FBM edge\n// amplitude + click-pulse + a lean/squash safety pad) so it NEVER clips the wet\n// meniscus (IQ: inflate the bound to match any outward op).\nuniform float uMaxReach;\n\n// Membrane \u2014 domain-warp strength on the FBM edge displacement (0 = plain fbm).\nuniform float uWarpAmp;\n\n// Lit glass surface (W9.b) \u2014 gated behind uLit so the flat fill stays default.\nuniform float uLit; // 0 = flat fill (default), 1 = lit droplet\nuniform vec3 uRimColor; // Fresnel rim tint (GAMMA sRGB, via the /color leaf)\nuniform vec3 uLightDir; // normalized light direction (x, y, z)\nuniform float uSpecStrength; // Blinn-Phong specular weight\nuniform float uSpecShininess; // specular exponent (16-64, tight glint)\nuniform float uRimPower; // Fresnel/Schlick exponent (~2-3)\nuniform float uRimStrength; // Fresnel rim weight\n\n// BC.W-GOOBLOB-MEATBALL \u2014 the procedural 2D SDF soft-shadow march (T2, the GLSL twin of\n// the WGSL res.z/res.w lanes). uShadow gates the IQ rmshadows soft contact shadow that\n// FOLLOWS the irregular silhouette; uShadowSoftness is the penumbra hardness (4-48).\nuniform float uShadow; // 0 = no shadow, 1 = the soft contact shadow ON\nuniform float uShadowSoftness; // penumbra hardness (inverse light-source size, 4-48)\n\n// Iridescence + fake subsurface (W11.a) \u2014 translucent-gel read.\nuniform float uIridescence; // warm-pearl rim sheen weight (0 = off)\nuniform float uIridHue; // base hue (radians) the cosine palette biases toward (warm)\nuniform float uIridSpeed; // animated-thickness scroll for the iridescent t\nuniform float uSssScale; // fast-SSS back-light weight\nuniform float uSssPower; // fast-SSS exponent\nuniform float uCoreGlow; // thickness-driven inner-luminosity lift\n\n// Color perturbation (perceptually uniform \u2014 OKLCh L/C/h)\nuniform float uHueRange; // degrees of hue swing (converted to radians)\nuniform float uSatShift; // OKLCh chroma swing\nuniform float uBrightnessShift; // OKLCh lightness bias\nuniform float uColorNoiseFreq;\nuniform float uColorNoiseSpeed;\n\n// Satellites (max 4)\n#define MAX_SATS 4\nuniform int uSatCount;\nuniform vec2 uSatPos[MAX_SATS];\nuniform float uSatRadius[MAX_SATS];\nuniform float uSatOpacity[MAX_SATS];\n\n// Pointer trail (W10) \u2014 a decaying-radius pseudopod toward the cursor. uTrail is a\n// COMPILE-TIME-SIZED array (GLSL ES 3.00 forbids a uniform-sized array) with a\n// DYNAMIC break on uTrailCount, mirroring the satellite loop. Later samples paint\n// smaller metaballs (r *= 1 - i/N) so the trail tapers into a tail.\n#define TRAIL_N 15\nuniform int uTrailCount;\nuniform vec2 uTrailPos[TRAIL_N];\nuniform float uTrailRadius[TRAIL_N];\n\n// Velocity-driven volume-preserving squash-and-stretch (W10).\nuniform vec2 uVelocity; // smoothed pointer velocity (motion direction + speed)\nuniform float uStretch; // squash-stretch magnitude (0 = off)\n\nconst float PI = 3.141592653589793;\n";
|