@mkbabb/glass-ui 4.0.1 → 4.1.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-CW7a0m8M.js → CardAction-8whJrLWt.js} +23 -8
- package/dist/{CommandShortcut-DvZNQ_HF.js → CommandShortcut-D3yxgHNn.js} +23 -12
- package/dist/{ConfiguratorRow-BR4l-ixA.js → ConfiguratorRow-CaP7U3ZA.js} +11 -11
- package/dist/ContextMenuContent-B3ZsheLV.js +90 -0
- package/dist/{ContextMenuSubContent-xol4sD2q.js → ContextMenuSubContent-DDRID3qC.js} +15 -15
- package/dist/{DataTable-CuKwF7wr.js → DataTable-Bc9kUtu_.js} +2 -2
- package/dist/{DialogContent-DcKK8k9i.js → DialogContent-6JtTB5x2.js} +8 -5
- package/dist/DialogFooter-9phqcRIS.js +80 -0
- package/dist/DialogScrollContent-TTuovQst.js +93 -0
- package/dist/HoverCardContent-Bel1gdLZ.js +88 -0
- package/dist/{IconChip-BTD-nay5.js → IconChip-DhbdGdFQ.js} +1 -1
- package/dist/{IconTooltip-Bl8gFZ8d.js → IconTooltip-B0zimd2H.js} +1 -1
- package/dist/Input-Cg9DF62r.js +57 -0
- package/dist/{Notification-VUwexFUf.js → Notification-CphK7AAm.js} +3 -3
- package/dist/NumberFieldContent-D8cXeyO4.js +140 -0
- package/dist/PagerDots-Cjx96la-.js +98 -0
- package/dist/{SelectScrollDownButton-CrA8F0iV.js → SelectScrollDownButton-DAKCBEdW.js} +62 -57
- package/dist/{SelectSeparator-DXswVdRz.js → SelectSeparator-C2UGOkXF.js} +1 -1
- package/dist/Separator-DQcwC2TR.js +44 -0
- package/dist/Switch-B8RscmRu.js +49 -0
- package/dist/{Toaster-Bw4xR8xV.js → Toaster-D9qIHjBI.js} +1 -1
- package/dist/{ToggleGroupItem-pEM9vIhs.js → ToggleGroupItem-BLtr3TJq.js} +2 -2
- package/dist/TooltipProvider-BJ_Y5EGX.js +109 -0
- package/dist/animated-digit.js +1 -1
- package/dist/api/index.d.ts +15 -32
- package/dist/api/types-extra.d.ts +31 -0
- package/dist/api.js +1 -1
- package/dist/aurora.js +579 -251
- package/dist/border-progress.js +49 -57
- package/dist/{button-DYCUa-tH.js → button-5dY1Dvhd.js} +13 -14
- package/dist/button.js +1 -1
- package/dist/canvas.js +2 -3
- package/dist/card.js +1 -1
- package/dist/carousel.js +20 -20
- package/dist/color-Cs0-oxYL.js +116 -0
- package/dist/color.js +2 -2
- package/dist/command.js +1 -1
- package/dist/completion-seal.d.ts +1 -0
- package/dist/completion-seal.js +89 -0
- package/dist/components/custom/aurora/Aurora.vue.d.ts +10 -5
- package/dist/components/custom/aurora/composables/color.d.ts +13 -0
- package/dist/components/custom/aurora/composables/frameLoop.d.ts +9 -0
- package/dist/components/custom/aurora/composables/runtime.d.ts +12 -0
- package/dist/components/custom/aurora/composables/uniformBridgeWGPU.d.ts +11 -2
- package/dist/components/custom/aurora/composables/wgpuSetup.d.ts +8 -0
- package/dist/components/custom/aurora/constants/presets.d.ts +18 -0
- package/dist/components/custom/aurora/constants/renderMode.d.ts +16 -8
- package/dist/components/custom/aurora/constants/shaders/aurora-mediums.wgsl.d.ts +1 -0
- package/dist/components/custom/aurora/constants/shaders/aurora.wgsl.d.ts +1 -1
- package/dist/components/custom/border-progress/composables/spectrum-walk.d.ts +25 -0
- package/dist/components/custom/border-progress/composables/useBorderSpectrum.d.ts +14 -18
- package/dist/components/custom/border-progress/index.d.ts +1 -1
- package/dist/components/custom/completion-seal/CompletionSeal.vue.d.ts +21 -0
- package/dist/components/custom/completion-seal/composables/useCompletionSeal.d.ts +37 -0
- package/dist/components/custom/completion-seal/constants.d.ts +46 -0
- package/dist/components/custom/completion-seal/index.d.ts +4 -0
- package/dist/components/custom/concentric/composables/concentricGLSetup.d.ts +7 -0
- package/dist/components/custom/concentric/composables/concentricWGPUSetup.d.ts +9 -0
- package/dist/components/custom/concentric/composables/ringField.d.ts +57 -16
- package/dist/components/custom/concentric/composables/uniformBridgeWGPU.d.ts +2 -1
- package/dist/components/custom/concentric/composables/useConcentric.d.ts +4 -4
- package/dist/components/custom/concentric/constants.d.ts +30 -6
- 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/confirm-dialog/ConfirmDialog.vue.d.ts +3 -3
- package/dist/components/custom/constellation/Constellation.vue.d.ts +5 -4
- package/dist/components/custom/constellation/composables/constellationGLSetup.d.ts +10 -0
- package/dist/components/custom/constellation/composables/constellationWGPUSetup.d.ts +19 -0
- package/dist/components/custom/constellation/composables/uniformBridgeWGPU.d.ts +66 -0
- package/dist/components/custom/constellation/composables/useConstellation.d.ts +3 -10
- package/dist/components/custom/constellation/constants.d.ts +33 -0
- package/dist/components/custom/constellation/constellationField.d.ts +31 -1
- package/dist/components/custom/constellation/constellationInteraction.d.ts +23 -14
- package/dist/components/custom/constellation/constellationRender.d.ts +25 -0
- package/dist/components/custom/constellation/constellationTypes.d.ts +38 -0
- package/dist/components/custom/constellation/constellationWell.d.ts +15 -0
- package/dist/components/custom/constellation/index.d.ts +5 -5
- package/dist/components/custom/constellation/shaders/constellation-lines.glsl.d.ts +2 -0
- package/dist/components/custom/constellation/shaders/constellation-lines.wgsl.d.ts +1 -0
- package/dist/components/custom/constellation/shaders/constellation-points.glsl.d.ts +2 -0
- package/dist/components/custom/constellation/shaders/constellation-points.wgsl.d.ts +1 -0
- package/dist/components/custom/deck/DeckPager.vue.d.ts +29 -0
- package/dist/components/custom/deck/composables/useDeck.d.ts +27 -0
- package/dist/components/custom/deck/composables/useDeckKeyboard.d.ts +25 -0
- package/dist/components/custom/deck/composables/useDeckSpring.d.ts +22 -0
- package/dist/components/custom/deck/constants.d.ts +14 -0
- package/dist/components/custom/deck/index.d.ts +8 -0
- package/dist/components/custom/dock/DockIconButton.vue.d.ts +12 -0
- package/dist/components/custom/dock/DockStack.vue.d.ts +65 -0
- package/dist/components/custom/dock/GlassDock.vue.d.ts +4 -2
- package/dist/components/custom/dock/composables/dockMorphMeasure.d.ts +80 -0
- package/dist/components/custom/dock/composables/index.d.ts +2 -0
- package/dist/components/custom/dock/composables/useDockSearch.d.ts +85 -0
- package/dist/components/custom/dock/composables/useDockShellProps.d.ts +11 -0
- package/dist/components/custom/dock/constants.d.ts +18 -14
- package/dist/components/custom/dock/index.d.ts +5 -2
- package/dist/components/custom/dot-flow-field/DotFlowField.vue.d.ts +15 -11
- package/dist/components/custom/dot-flow-field/composables/flowField.d.ts +48 -6
- package/dist/components/custom/dot-flow-field/composables/uniformBridgeWGPU.d.ts +23 -8
- package/dist/components/custom/dot-flow-field/composables/useDotFlowField.d.ts +6 -5
- package/dist/components/custom/dot-flow-field/composables/useFlowParticles.d.ts +18 -5
- package/dist/components/custom/dot-flow-field/constants.d.ts +42 -21
- package/dist/components/custom/dot-flow-field/index.d.ts +2 -2
- 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 +4 -20
- package/dist/components/custom/dot-flow-field/shaders/flow-field.render.wgsl.d.ts +1 -1
- package/dist/components/custom/dot-matrix/DotMatrix.vue.d.ts +43 -0
- package/dist/components/custom/dot-matrix/composables/dotMatrixField.d.ts +89 -0
- package/dist/components/custom/dot-matrix/composables/uniformBridgeWGPU.d.ts +52 -0
- package/dist/components/custom/dot-matrix/composables/useDotMatrix.d.ts +34 -0
- package/dist/components/custom/dot-matrix/composables/useDotSphere.d.ts +27 -0
- package/dist/components/custom/dot-matrix/constants.d.ts +58 -0
- package/dist/components/custom/dot-matrix/index.d.ts +5 -0
- package/dist/components/custom/dot-matrix/shaders/dot-matrix.glsl.d.ts +4 -0
- package/dist/components/custom/dot-matrix/shaders/dot-matrix.wgsl.d.ts +1 -0
- package/dist/components/custom/easing/EasingPicker.vue.d.ts +2 -2
- package/dist/components/custom/easing/composables/useEasingPicker.d.ts +5 -0
- package/dist/components/custom/expandable-container/ExpandableContainer.vue.d.ts +11 -0
- package/dist/components/custom/fourier-field/FourierField.vue.d.ts +40 -47
- package/dist/components/custom/fourier-field/composables/fourierFieldGLSetup.d.ts +15 -0
- package/dist/components/custom/fourier-field/composables/fourierFieldWGPUSetup.d.ts +20 -0
- package/dist/components/custom/fourier-field/composables/uniformBridgeWGPU.d.ts +41 -0
- package/dist/components/custom/fourier-field/composables/useFourierField.d.ts +40 -37
- package/dist/components/custom/fourier-field/constants.d.ts +61 -4
- package/dist/components/custom/fourier-field/index.d.ts +23 -18
- package/dist/components/custom/fourier-field/shaders/fourier-field.compute.wgsl.d.ts +1 -0
- package/dist/components/custom/fourier-field/shaders/fourier-field.glsl.d.ts +4 -0
- package/dist/components/custom/fourier-field/shaders/fourier-field.render.wgsl.d.ts +1 -0
- package/dist/components/custom/goo-blob/GooBlob.vue.d.ts +9 -1
- package/dist/components/custom/goo-blob/constants.d.ts +1 -1
- package/dist/components/custom/goo-blob/index.d.ts +1 -1
- package/dist/components/custom/goo-blob/shaders/metaball-noise.wgsl.d.ts +1 -0
- package/dist/components/custom/goo-blob/shaders/metaball-palette.wgsl.d.ts +1 -0
- 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 +36 -0
- package/dist/components/custom/goo-dot-matrix/GooDotMatrix.vue.d.ts +49 -0
- package/dist/components/custom/goo-dot-matrix/composables/gooDotLattice.d.ts +24 -0
- package/dist/components/custom/goo-dot-matrix/composables/gooDotSetup.d.ts +50 -0
- package/dist/components/custom/goo-dot-matrix/composables/uniformBridgeWGPU.d.ts +35 -0
- package/dist/components/custom/goo-dot-matrix/composables/useGooDotMatrix.d.ts +31 -0
- package/dist/components/custom/goo-dot-matrix/constants.d.ts +76 -0
- package/dist/components/custom/goo-dot-matrix/index.d.ts +5 -0
- package/dist/components/custom/goo-dot-matrix/shaders/goo-dot.frag.d.ts +11 -0
- package/dist/components/custom/goo-dot-matrix/shaders/goo-dot.wgsl.d.ts +3 -0
- package/dist/components/custom/labeled-field/LabeledField.vue.d.ts +21 -2
- package/dist/components/custom/metric-stack/MetricRow.vue.d.ts +15 -0
- package/dist/components/custom/pager-dots/PagerDots.vue.d.ts +9 -0
- package/dist/components/custom/pager-dots/index.d.ts +2 -0
- package/dist/components/custom/pager-dots/pagerWindow.d.ts +9 -0
- package/dist/components/custom/paper-grid/PaperGrid.vue.d.ts +42 -0
- package/dist/components/custom/paper-grid/composables/paperGrid.d.ts +90 -0
- package/dist/components/custom/paper-grid/composables/paperGridGLSetup.d.ts +16 -0
- package/dist/components/custom/paper-grid/composables/paperGridWGPUSetup.d.ts +19 -0
- package/dist/components/custom/paper-grid/composables/uniformBridgeWGPU.d.ts +16 -0
- package/dist/components/custom/paper-grid/composables/usePaperGrid.d.ts +31 -0
- package/dist/components/custom/paper-grid/constants.d.ts +57 -0
- package/dist/components/custom/paper-grid/index.d.ts +5 -0
- package/dist/components/custom/paper-grid/shaders/paper-grid.glsl.d.ts +4 -0
- package/dist/components/custom/paper-grid/shaders/paper-grid.wgsl.d.ts +1 -0
- package/dist/components/custom/search/FuzzySearch.vue.d.ts +8 -2
- package/dist/components/custom/search/SearchBar.vue.d.ts +8 -0
- package/dist/components/custom/search/index.d.ts +2 -0
- package/dist/components/custom/search/searchVariants.d.ts +19 -0
- package/dist/components/custom/selectable-chip/SelectableChip.vue.d.ts +50 -0
- package/dist/components/custom/selectable-chip/index.d.ts +2 -0
- package/dist/components/custom/selectable-chip/selectableChipVariants.d.ts +23 -0
- package/dist/components/custom/split-chars/SplitChars.vue.d.ts +39 -0
- package/dist/components/custom/split-chars/index.d.ts +1 -0
- package/dist/components/custom/tabs/SegmentedTabs.vue.d.ts +8 -6
- package/dist/components/custom/tabs/constants.d.ts +20 -2
- package/dist/components/custom/watercolor-dot/WatercolorDot.vue.d.ts +29 -16
- package/dist/components/custom/watercolor-dot/useWatercolorBlob.d.ts +10 -5
- package/dist/components/ui/_shared/index.d.ts +2 -0
- package/dist/components/ui/_shared/useControlSize.d.ts +46 -0
- package/dist/components/ui/button/index.d.ts +1 -1
- package/dist/components/ui/card/Card.vue.d.ts +45 -0
- package/dist/components/ui/card/index.d.ts +1 -1
- package/dist/components/ui/carousel/CarouselNext.vue.d.ts +1 -1
- package/dist/components/ui/carousel/CarouselPrevious.vue.d.ts +1 -1
- package/dist/components/ui/command/CommandDialog.vue.d.ts +9 -3
- package/dist/components/ui/command/CommandList.vue.d.ts +3 -0
- package/dist/components/ui/context-menu/ContextMenuContent.vue.d.ts +5 -1
- package/dist/components/ui/dropdown-menu/DropdownMenuContent.vue.d.ts +4 -0
- package/dist/components/ui/hover-card/HoverCardContent.vue.d.ts +3 -0
- package/dist/components/ui/input/Input.vue.d.ts +3 -0
- package/dist/components/ui/number-field/NumberFieldInput.vue.d.ts +6 -1
- package/dist/components/ui/select/SelectContent.vue.d.ts +3 -0
- package/dist/components/ui/switch/Switch.vue.d.ts +2 -0
- package/dist/components/ui/textarea/Textarea.vue.d.ts +3 -0
- package/dist/components/ui/tooltip/TooltipContent.vue.d.ts +3 -0
- package/dist/composables/color/index.d.ts +2 -0
- package/dist/composables/color/useAccentTone.d.ts +34 -0
- package/dist/composables/glass/useVizChoreography.d.ts +123 -0
- package/dist/composables/glass/webgl/createCanvasLifecycle.d.ts +10 -0
- package/dist/composables/glass/webgl/shaders/flow.wgsl.d.ts +1 -0
- package/dist/composables/glass/webgpu/index.d.ts +1 -1
- package/dist/composables/glass/webgpu/useGpuSubstrate.d.ts +28 -7
- package/dist/composables/glass/webgpu/useWebGPUCanvas.d.ts +1 -0
- package/dist/composables/glass/webgpu/webgpuDevice.d.ts +42 -0
- package/dist/composables/motion/core/index.d.ts +3 -0
- package/dist/composables/motion/scrollReader.d.ts +25 -0
- package/dist/composables/motion/springPresets.d.ts +20 -1
- package/dist/composables/motion/useCharStagger.d.ts +44 -0
- package/dist/composables/motion/useDockCtaReceive.d.ts +31 -6
- package/dist/composables/motion/useScrollChrome.d.ts +58 -0
- package/dist/composables/motion/useScrollTrigger.d.ts +67 -0
- package/dist/composables/motion/useSpringPress.d.ts +5 -5
- package/dist/composables/motion/useStaggerReveal.d.ts +24 -0
- package/dist/composables/sidebar/index.d.ts +4 -1
- package/dist/composables/sidebar/types.d.ts +68 -3
- package/dist/composables/sidebar/useClickDelegate.d.ts +4 -0
- package/dist/composables/sidebar/useLazyLoader.d.ts +5 -0
- package/dist/composables/sidebar/useScrollTo.d.ts +5 -0
- package/dist/composables/virtual/index.d.ts +4 -0
- package/dist/composables/virtual/useVirtualSectionWindow.d.ts +72 -0
- package/dist/composables/virtual/useWindowedStore.d.ts +43 -0
- package/dist/composables/virtual/virtualSectionLayout.d.ts +103 -0
- package/dist/concentric.js +495 -221
- package/dist/configurator.js +2 -2
- package/dist/confirm-dialog.js +62 -47
- package/dist/constellation.js +635 -235
- package/dist/context-menu.js +2 -2
- package/dist/controls.js +1 -1
- package/dist/createCanvasLifecycle-Gh45Ce-p.js +93 -0
- package/dist/dark.js +1 -1
- package/dist/data-table.js +1 -1
- package/dist/deck.d.ts +1 -0
- package/dist/deck.js +105 -0
- package/dist/dialog.js +4 -3
- package/dist/dock.js +602 -458
- package/dist/dot-flow-field.js +580 -290
- package/dist/dot-matrix.d.ts +1 -0
- package/dist/dot-matrix.js +630 -0
- package/dist/dotMatrixField-D_x9_1qq.js +48 -0
- package/dist/{dropdown-menu-CofSZuk8.js → dropdown-menu-aJmV5SqE.js} +83 -79
- package/dist/dropdown-menu.js +1 -1
- package/dist/easing.js +188 -176
- package/dist/expandable-container.js +42 -20
- package/dist/fading-scroll.js +1 -1
- package/dist/flow.glsl-B2P7e0MT.js +4 -0
- package/dist/forms.js +62 -60
- package/dist/fourier-field.js +825 -178
- package/dist/fourier-math.js +1 -1
- package/dist/glass-ui.css +1 -1
- package/dist/glass-ui.js +145 -104
- package/dist/goo-blob.js +171 -1228
- package/dist/goo-dot-matrix.d.ts +1 -0
- package/dist/goo-dot-matrix.js +591 -0
- package/dist/handmark.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 +2 -0
- package/dist/instrument-chassis.js +1 -1
- package/dist/keyboard.js +1 -1
- package/dist/labeled-field.js +70 -60
- package/dist/metric-cell.js +1 -1
- package/dist/metric-stack.js +11 -2
- package/dist/motion-core.js +54 -32
- package/dist/motion-curves.js +11 -1
- package/dist/motion.js +46 -111
- package/dist/notification.js +1 -1
- package/dist/number-field.js +1 -1
- package/dist/pager-dots.js +2 -91
- package/dist/paper-backdrop.js +1 -1
- package/dist/paper-grid.d.ts +1 -0
- package/dist/paper-grid.js +788 -0
- package/dist/{presets-BW2b-fr1.js → presets-CvOUqtdY.js} +3 -3
- package/dist/{procedural-color.glsl-CIFsJhwI.js → procedural-color.wgsl-m7Zgv35j.js} +2 -2
- package/dist/progress.js +1 -1
- package/dist/pulse.js +1 -1
- package/dist/reactive.js +1 -1
- package/dist/{useCanvas2D-B8w_E3Rr.js → resolveCanvasColor-yPyIpJt9.js} +12 -2
- package/dist/scrollReader-DI1NWxUw.js +42 -0
- package/dist/scrolling-text.js +1 -1
- package/dist/search.js +160 -304
- package/dist/select.js +3 -3
- package/dist/selectable-chip.d.ts +1 -0
- package/dist/selectable-chip.js +65 -0
- package/dist/separator.js +1 -1
- package/dist/{sheet-tZMg0E0w.js → sheet-B2flBfLH.js} +2 -2
- package/dist/sheet.js +1 -1
- package/dist/sidebar.js +216 -114
- package/dist/slider.js +1 -1
- package/dist/sortable-list.js +1 -1
- package/dist/spectrum-walk-BVzA7Biz.js +33 -0
- package/dist/{springPresets-CFQGQfdl.js → springPresets-Ce7OYCm9.js} +12 -6
- package/dist/styles/animations.css +13 -3
- package/dist/styles/cards.css +69 -0
- package/dist/styles/completion-seal.css +131 -0
- package/dist/styles/components.css +2 -3
- package/dist/styles/configurator.css +55 -0
- package/dist/styles/critical-partition.mjs +33 -11
- package/dist/styles/deferred.css +2 -0
- package/dist/styles/dock/cta-seat.css +87 -0
- package/dist/styles/dock/density.css +153 -0
- package/dist/styles/dock/layers.css +55 -7
- package/dist/styles/dock/morph-bridge.css +54 -18
- package/dist/styles/dock/morph.css +41 -33
- package/dist/styles/dock/overflow.css +12 -0
- package/dist/styles/dock/search.css +68 -0
- package/dist/styles/dock/shape.css +124 -0
- package/dist/styles/dock/shell.css +23 -5
- package/dist/styles/dock/stack-rail.css +264 -0
- package/dist/styles/dock-controls/dark-mode-toggle.css +17 -4
- package/dist/styles/dock-controls/icon-button.css +14 -0
- package/dist/styles/dock.css +11 -1
- package/dist/styles/glass/accent-tone.css +66 -0
- package/dist/styles/glass/control-surfaces.css +167 -0
- package/dist/styles/glass/ladder.css +67 -17
- package/dist/styles/glass/rim.css +19 -9
- package/dist/styles/glass/surfaces.css +69 -148
- package/dist/styles/glass.css +10 -0
- package/dist/styles/index.css +15 -0
- package/dist/styles/scroll-chrome.css +123 -0
- package/dist/styles/scroll-driven.css +38 -0
- package/dist/styles/segmented-tabs.css +50 -14
- package/dist/styles/theme/bridges.css +22 -0
- package/dist/styles/theme/radius.css +18 -1
- package/dist/styles/tokens/color-radius.css +13 -3
- package/dist/styles/tokens/dark-arm.css +39 -6
- package/dist/styles/tokens/glass-deep.css +28 -3
- package/dist/styles/tokens/glass-fx.css +44 -1
- package/dist/styles/tokens/glass.css +91 -31
- package/dist/styles/tokens/light-dark.css +1 -1
- package/dist/styles/tokens/offsets.css +27 -0
- package/dist/styles/tokens/property-regs.css +133 -0
- package/dist/styles/tokens/scale-paper.css +73 -29
- package/dist/styles/tokens/scheme-motion.css +44 -6
- package/dist/styles/tokens/sizing-config.css +110 -0
- package/dist/styles/tokens/sizing.css +55 -104
- package/dist/styles/tokens.css +5 -0
- package/dist/styles/typography/semantic.css +18 -5
- package/dist/styles/utilities/a11y-overrides.css +20 -5
- package/dist/styles/utilities/base-misc.css +52 -0
- package/dist/styles/utilities/btn.css +14 -3
- package/dist/styles/utilities/components.css +31 -6
- package/dist/styles/utilities/metal.css +26 -0
- package/dist/switch.js +1 -1
- package/dist/tabs.js +8 -8
- package/dist/timeline.js +1 -1
- package/dist/toast.js +1 -1
- package/dist/toggle-chip.js +10 -10
- package/dist/toggle-group.js +1 -1
- package/dist/tooltip.js +1 -1
- package/dist/typewriter.js +1 -1
- package/dist/uniformBridgeWGPU-CtAKl45L.js +1204 -0
- package/dist/{useAnimatedNumber-BfUq2bUM.js → useAnimatedNumber-CLrxicG9.js} +1 -1
- package/dist/{useConfiguratorState-DNlOh6c8.js → useConfiguratorState-4xblKfwd.js} +25 -24
- package/dist/useControlSize-SH1UmJCi.js +9 -0
- package/dist/useDockCtaReceive-DfFkmgL1.js +82 -0
- package/dist/{useDragMorph-DgxnsVJz.js → useDragMorph-BUgAC-zK.js} +4 -4
- package/dist/useFuzzySearch-DIhcYtcw.js +177 -0
- package/dist/useGpuSubstrate-4pTcp6mX.js +294 -0
- package/dist/{useLiquidPress-kfb4KX79.js → useLiquidPress-BooG97RY.js} +2 -2
- package/dist/usePointerVelocityField-QLsy3_kv.js +91 -0
- package/dist/useScrollChrome-D66AVP7C.js +258 -0
- package/dist/{useSpringMount-q1amq1SJ.js → useSpringMount-BWDnKIwh.js} +1 -1
- package/dist/useSpringPress-B1-Czp-V.js +36 -0
- package/dist/useViewTransition-CUI1nAgn.js +95 -0
- package/dist/{vendor-CKmzio-i.js → vendor-B2qOlYY3.js} +24 -11
- package/dist/virtual.d.ts +1 -0
- package/dist/virtual.js +225 -0
- package/dist/watercolor-dot.js +116 -73
- package/package.json +127 -7
- package/src/styles/animations.css +13 -3
- package/src/styles/cards.css +69 -0
- package/src/styles/completion-seal.css +131 -0
- package/src/styles/configurator.css +55 -0
- package/src/styles/critical-partition.mjs +33 -11
- package/src/styles/dock/cta-seat.css +87 -0
- package/src/styles/dock/density.css +153 -0
- package/src/styles/dock/layers.css +55 -7
- package/src/styles/dock/morph-bridge.css +54 -18
- package/src/styles/dock/morph.css +41 -33
- package/src/styles/dock/overflow.css +12 -0
- package/src/styles/dock/search.css +68 -0
- package/src/styles/dock/shape.css +124 -0
- package/src/styles/dock/shell.css +23 -5
- package/src/styles/dock/stack-rail.css +264 -0
- package/src/styles/dock-controls/dark-mode-toggle.css +17 -4
- package/src/styles/dock-controls/icon-button.css +14 -0
- package/src/styles/dock.css +11 -1
- package/src/styles/glass/accent-tone.css +66 -0
- package/src/styles/glass/control-surfaces.css +167 -0
- package/src/styles/glass/ladder.css +67 -17
- package/src/styles/glass/rim.css +19 -9
- package/src/styles/glass/surfaces.css +69 -148
- package/src/styles/glass.css +10 -0
- package/src/styles/index.css +15 -0
- package/src/styles/scroll-chrome.css +123 -0
- package/src/styles/scroll-driven.css +38 -0
- package/src/styles/segmented-tabs.css +50 -14
- package/src/styles/theme/bridges.css +22 -0
- package/src/styles/theme/radius.css +18 -1
- package/src/styles/tokens/color-radius.css +13 -3
- package/src/styles/tokens/dark-arm.css +39 -6
- package/src/styles/tokens/glass-deep.css +28 -3
- package/src/styles/tokens/glass-fx.css +44 -1
- package/src/styles/tokens/glass.css +91 -31
- package/src/styles/tokens/light-dark.css +1 -1
- package/src/styles/tokens/offsets.css +27 -0
- package/src/styles/tokens/property-regs.css +133 -0
- package/src/styles/tokens/scale-paper.css +73 -29
- package/src/styles/tokens/scheme-motion.css +44 -6
- package/src/styles/tokens/sizing-config.css +110 -0
- package/src/styles/tokens/sizing.css +55 -104
- package/src/styles/tokens.css +5 -0
- package/src/styles/typography/semantic.css +18 -5
- package/src/styles/utilities/a11y-overrides.css +20 -5
- package/src/styles/utilities/base-misc.css +52 -0
- package/src/styles/utilities/btn.css +14 -3
- package/src/styles/utilities/components.css +31 -6
- package/src/styles/utilities/metal.css +26 -0
- package/dist/ContextMenuContent-uWnVxDab.js +0 -85
- package/dist/DialogFooter-BZNX5aVr.js +0 -165
- package/dist/HoverCardContent-b2uaAMRR.js +0 -83
- package/dist/Input-h9v5LeXf.js +0 -55
- package/dist/NumberFieldContent-Cq7EzX-U.js +0 -138
- package/dist/Separator-pKF0oA4M.js +0 -37
- package/dist/Switch-CwYNf8bo.js +0 -47
- package/dist/TooltipProvider-rzvnXz60.js +0 -104
- package/dist/color-DweYl7pE.js +0 -86
- package/dist/components/custom/constellation/composables/useConstellationPointer.d.ts +0 -35
- package/dist/components/custom/constellation/constellationDraw.d.ts +0 -30
- package/dist/components/custom/dock/DockRail.vue.d.ts +0 -89
- package/dist/createCanvasLifecycle-CHD5XrDW.js +0 -82
- package/dist/procedural-color.wgsl-CToRHugY.js +0 -224
- package/dist/resolveCanvasColor-CUyHr5Uu.js +0 -11
- package/dist/styles/dock/rail-extend.css +0 -397
- package/dist/usePointerVelocityField-JbRd5CrM.js +0 -137
- package/dist/useRAFLoop-UYa4zoiN.js +0 -109
- package/dist/useSpringPress-BsqPr7FJ.js +0 -34
- package/src/styles/dock/rail-extend.css +0 -397
- /package/dist/{FadingScroll-DI6hQhT9.js → FadingScroll-DLRbH5Xr.js} +0 -0
- /package/dist/{HoverPopover-CbqbNwgU.js → HoverPopover-BcIxvyqZ.js} +0 -0
- /package/dist/{InstrumentChassis-Bx0F_q5g.js → InstrumentChassis-CxsHSyMj.js} +0 -0
- /package/dist/{PaperBackdrop-Vf3tlpvd.js → PaperBackdrop-CqwjnfLv.js} +0 -0
- /package/dist/{Progress-DctG61iZ.js → Progress-CnqB7gLm.js} +0 -0
- /package/dist/{ScrollingText-yOpBzFYq.js → ScrollingText-CTZbA_cG.js} +0 -0
- /package/dist/{SelectGroup-DwwjicPY.js → SelectGroup-BFiod7X2.js} +0 -0
- /package/dist/{compile-CQEFRTmx.js → compile-nZ1xVYTp.js} +0 -0
- /package/dist/{constants-BPOqYJAm.js → constants--C2DkLW8.js} +0 -0
- /package/dist/{math-BINYmg5z.js → math-Eb0w3LqU.js} +0 -0
- /package/dist/{prng-Za0Rmgag.js → prng-B-d9eYX8.js} +0 -0
- /package/dist/{slider-DVb4HXU-.js → slider-2A0rPn79.js} +0 -0
- /package/dist/{useGlobalDark-C28t0VWJ.js → useGlobalDark-CmjB9E32.js} +0 -0
- /package/dist/{useIntersectionPause-Bj4sPkMh.js → useIntersectionPause-Dbozzjgm.js} +0 -0
- /package/dist/{useInterval-CvK1mL92.js → useInterval-CNdua40W.js} +0 -0
- /package/dist/{useKeyboardShortcuts-BXWXOZ-9.js → useKeyboardShortcuts-BPRp33Im.js} +0 -0
- /package/dist/{useLiquidFlex-mZPMiNXh.js → useLiquidFlex-UmA6LMZu.js} +0 -0
- /package/dist/{useScrollProgress-D7bYU2RK.js → useScrollProgress-c3yxmH6a.js} +0 -0
- /package/dist/{useSortable-DHjtZSEd.js → useSortable-BVqlJRBR.js} +0 -0
- /package/dist/{useTextHighlight-CWEi25hf.js → useTextHighlight-NmBM5V_4.js} +0 -0
- /package/dist/{vReveal-Czsb7zQC.js → vReveal-D9k0jADx.js} +0 -0
- /package/dist/{vSpecular-CQFmLNzA.js → vSpecular-EX7loENl.js} +0 -0
|
@@ -21,7 +21,16 @@
|
|
|
21
21
|
* nuc0 : array<vec4<f32>, 6> off 240 (pos.x, pos.y, radius, paletteBias)
|
|
22
22
|
* nuc1 : array<vec4<f32>, 6> off 336 (valueBias, driftRadius, driftPhase, elong)
|
|
23
23
|
* nuc2 : array<vec4<f32>, 6> off 432 (angle, _, _, _)
|
|
24
|
-
*
|
|
24
|
+
* scalars4 : vec4<f32> off 528 (uStrokeAmount, uStrokeScale, uStrokeAnisotropy, uCanvasGrain)
|
|
25
|
+
* scalars5 : vec4<f32> off 544 (uWetEdge, uGranulation, uBrokenColor, uKuwaharaQ)
|
|
26
|
+
* kuwahara : vec4<f32> off 560 (uKuwaharaRadius, uKuwaharaSectors, _, _)
|
|
27
|
+
* total : 576 bytes (16-aligned)
|
|
28
|
+
*
|
|
29
|
+
* BC.W-VIZ-AURORA (T4) — the painterly-medium scalar lanes (scalars4/scalars5/kuwahara)
|
|
30
|
+
* are APPENDED after the arrays, so EVERY pre-existing offset is byte-identical and the
|
|
31
|
+
* smooth-default parity capture is byte-equivalent (these lanes pack 0 on a smooth config).
|
|
32
|
+
* The WGSL `Uniforms` struct (`aurora.wgsl.ts`) carries the SAME three trailing lanes —
|
|
33
|
+
* the typed-struct lockstep: a one-sided add reds the parity-ΔE.
|
|
25
34
|
*
|
|
26
35
|
* Y-origin: config authoring is CSS-top-origin (0 = top); the bridge flips Y at the
|
|
27
36
|
* uniform boundary (`flipY`), exactly as the GLSL `uniformBridge` does.
|
|
@@ -29,7 +38,7 @@
|
|
|
29
38
|
import { type AuroraConfig } from "../constants/presets";
|
|
30
39
|
import type { CursorState } from "./cursorModel";
|
|
31
40
|
/** The total uniform-buffer byte size (16-aligned). */
|
|
32
|
-
export declare const AURORA_WGPU_UNIFORM_BYTES =
|
|
41
|
+
export declare const AURORA_WGPU_UNIFORM_BYTES = 576;
|
|
33
42
|
/**
|
|
34
43
|
* The packed scratch buffer + the dual ArrayBuffer views. A slider drag refills this
|
|
35
44
|
* in place (no per-frame allocation) — mirroring the GLSL bridge's pre-allocated
|
|
@@ -15,11 +15,19 @@
|
|
|
15
15
|
import type { WebGPUCanvasFrame } from "../../../../composables/glass/webgpu/useWebGPUCanvas";
|
|
16
16
|
import { type CursorState } from "./cursorModel";
|
|
17
17
|
import type { AuroraConfig } from "../constants/presets";
|
|
18
|
+
import type { UsePointerVelocityField } from "../../../../composables/motion/usePointerVelocityField";
|
|
18
19
|
export interface AuroraWGPUSetupDeps {
|
|
19
20
|
canvas: HTMLCanvasElement;
|
|
20
21
|
cursor: CursorState;
|
|
21
22
|
getConfig: () => AuroraConfig;
|
|
22
23
|
getReducedMotion: () => boolean;
|
|
24
|
+
/**
|
|
25
|
+
* BC.W-VIZ-AURORA (T5) — the shared viz-pointer-physics field (BB.B4). FED
|
|
26
|
+
* `tick(deltaMs)` from the WGPU frame callback (the SAME field instance the WebGL2
|
|
27
|
+
* loop feeds — one source, no own rAF); the renderer reads `acceleration` for the
|
|
28
|
+
* iOS-27 gel snap-back.
|
|
29
|
+
*/
|
|
30
|
+
pointerField: UsePointerVelocityField;
|
|
23
31
|
}
|
|
24
32
|
/**
|
|
25
33
|
* Build the aurora `setupWGPU(device, context, format)` callback. Closes over the
|
|
@@ -115,6 +115,16 @@ export interface AuroraConfig {
|
|
|
115
115
|
impasto: number;
|
|
116
116
|
brokenColor: number;
|
|
117
117
|
canvasGrain: number;
|
|
118
|
+
/**
|
|
119
|
+
* BC.W-VIZ-AURORA (T4) — the anisotropic-Kuwahara painterly-finish knobs (the
|
|
120
|
+
* `medium:"kuwahara"` register; the WGSL primary's keystone). Optional; omitted =
|
|
121
|
+
* the recipe defaults (radius 0.010 procedural-patch units, q 4.0 the SOFT
|
|
122
|
+
* variance-weight exponent). Sectors are fixed at 8 (the soft-blend overlap floor —
|
|
123
|
+
* not an authoring axis). These ride the appended WGSL `kuwahara`/`scalars5` struct
|
|
124
|
+
* lanes; a smooth/non-kuwahara config leaves them at the default (a no-op).
|
|
125
|
+
*/
|
|
126
|
+
kuwaharaRadius?: number;
|
|
127
|
+
kuwaharaQ?: number;
|
|
118
128
|
/**
|
|
119
129
|
* AW.W4.2 — the impasto relight direction (the movable directional source the
|
|
120
130
|
* accumulated paint-height field catches). Optional; omitted = upper-left
|
|
@@ -166,6 +176,14 @@ export interface AuroraInstance extends AuroraCursorApi {
|
|
|
166
176
|
* 24). A no-op once armed or once `dispose()` has run.
|
|
167
177
|
*/
|
|
168
178
|
arm(): void;
|
|
179
|
+
/**
|
|
180
|
+
* BC.W-VIZ-AURORA (T2) — the device-resolved arm PROMISE. On the WebGPU
|
|
181
|
+
* backend `renderAt`/`arm` are no-ops until this resolves the async
|
|
182
|
+
* adapter→device→configure→setup prelude. A CAPTURE consumer (thumbnail bake)
|
|
183
|
+
* MUST `await armAsync()` before the first `renderAt` or the frame is BLANK
|
|
184
|
+
* (the dead-preview defect). The WebGL2 fallback resolves immediately.
|
|
185
|
+
*/
|
|
186
|
+
armAsync(): Promise<void>;
|
|
169
187
|
update(cfg: AuroraConfig): void;
|
|
170
188
|
/** Render a single deterministic frame at time `t` (seconds). Used for thumbnail bakes. */
|
|
171
189
|
renderAt(timeSec: number): void;
|
|
@@ -59,12 +59,22 @@ export interface ResolveRenderModeOptions {
|
|
|
59
59
|
* would arm WebGL. The single ESCAPE is `options.forceWebGLUnderSoftwareRaster`
|
|
60
60
|
* (default `false` — the guard is the safe default).
|
|
61
61
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
62
|
+
* BC.W-VIZ-AURORA (T1) — the DEAD-STATIC `"auto"` fall is RETIRED. The
|
|
63
|
+
* `lowConcurrency` (`hardwareConcurrency <= 4`) and `saveData` heuristics are
|
|
64
|
+
* GONE: they predate the offscreen-park + the `AV_AURORA_DPR_MAX` sub-2× cap +
|
|
65
|
+
* the demand-gate that make a full-viewport aurora cheap, and `<= 4` is
|
|
66
|
+
* false-positive-prone in 2026 (a capable base-M-series laptop / a throttled VM
|
|
67
|
+
* tab reports 4 logical cores → it got a FROZEN static gradient that reads as
|
|
68
|
+
* "the aurora is broken", USER-DEFECTS §E "renders SLOW"). So `"auto"` arms the
|
|
69
|
+
* GPU on every Chrome-113/Safari-26/Firefox-141 device.
|
|
70
|
+
*
|
|
71
|
+
* Reduced-motion is handled SOLELY by the substrate's live `matchMedia` freeze
|
|
72
|
+
* (`createCanvasLifecycle` paints ONE static frame then parks, and re-arms on
|
|
73
|
+
* un-reduce) — NOT a render-mode fall (the old `"css"` fall never armed WebGL,
|
|
74
|
+
* so an un-reduce mid-session could never wake it; the substrate freeze does).
|
|
75
|
+
* The ONLY `"css"` signal that remains is the genuine software-raster GUARD
|
|
76
|
+
* (the universal first leg above) — and even that is subsumed by
|
|
77
|
+
* `BC.W-WEBGPU-EVERYWHERE`'s try-WebGPU-then-rebuild-WebGL2 picker.
|
|
68
78
|
*
|
|
69
79
|
* THE CAPABLE PATH IS BYTE-UNTOUCHED: `isSoftwareWebGLRenderer()` returns `false`
|
|
70
80
|
* on a real GPU (or a null probe — never a false downgrade), so a forced
|
|
@@ -73,8 +83,6 @@ export interface ResolveRenderModeOptions {
|
|
|
73
83
|
* SSR / missing-API safe: if `navigator` / `window` / the probes are
|
|
74
84
|
* unavailable we ASSUME a capable device and resolve `"webgl"` (the warm wash
|
|
75
85
|
* still composites either way — the only question is whether it animates).
|
|
76
|
-
* `navigator.connection` is non-standard, so it is feature-probed via an
|
|
77
|
-
* optional-chained cast rather than a typed accessor.
|
|
78
86
|
*
|
|
79
87
|
* Called ONCE at component setup — the device tier is a mount-time decision,
|
|
80
88
|
* not a reactive one (a connection-type flip mid-session does not re-resolve;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const AURORA_MEDIUMS_WGSL = "\n// \u2500\u2500 Painterly luma + medium scalar accessors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst W_LUMA: vec3<f32> = vec3<f32>(0.2126, 0.7152, 0.0722);\n\nfn mStrokeAmount() -> f32 { return u.scalars4.x; }\nfn mStrokeScale() -> f32 { return u.scalars4.y; }\nfn mStrokeAnisotropy() -> f32 { return u.scalars4.z; }\nfn mCanvasGrain() -> f32 { return u.scalars4.w; }\nfn mWetEdge() -> f32 { return u.scalars5.x; }\nfn mGranulation() -> f32 { return u.scalars5.y; }\nfn mBrokenColor() -> f32 { return u.scalars5.z; }\nfn mKuwaharaQ() -> f32 { return u.scalars5.w; }\nfn mKuwaharaRadius() -> f32 { return u.kuwahara.x; }\n\n// \u2500\u2500 Base color re-sample (the edge-mask operator the mediums Sobel/sector-sample) \u2500\u2500\n// The WGSL twin of mediums.glsl.ts sampleBase: warp -> nuclei field -> palette -> value.\nfn sampleBase(p: vec2<f32>, t: f32) -> vec3<f32> {\n let pw = domainWarp(p, t);\n let nf = nucleiField(pw, t);\n var c = samplePalette(nf.paletteId);\n c = c * (1.0 + u.scalars0.z * nf.valueMod);\n return c;\n}\n\n// \u2500\u2500 Broken color (hue + chroma jitter at constant OKLCh L) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn brokenColorJitter(c: vec3<f32>, hueSeed: f32, valueSeed: f32, strength: f32) -> vec3<f32> {\n let amt = clamp(mBrokenColor() * strength, 0.0, 1.0);\n if (amt <= 0.001) { return c; }\n var lch = oklabToOklch(linOklab(c));\n lch.z = lch.z + (hueSeed - 0.5) * (32.0 * PI / 180.0) * amt;\n lch.y = max(lch.y * (1.0 + (valueSeed - 0.5) * 0.28 * amt), 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3<f32>(0.0));\n}\n\n// \u2500\u2500 Structure-tensor / edge-tangent-flow (AW.W4.1; the WGSL twin) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Returns vec3(tangent.x, tangent.y, A) \u2014 the minor-eigenvector edge-tangent + coherence.\nfn structureTensorField(p: vec2<f32>, t: f32, fallbackDir: vec2<f32>) -> vec3<f32> {\n let e = 0.0035;\n let l00 = dot(sampleBase(p + vec2<f32>(-e, -e), t), W_LUMA);\n let l10 = dot(sampleBase(p + vec2<f32>(0.0, -e), t), W_LUMA);\n let l20 = dot(sampleBase(p + vec2<f32>( e, -e), t), W_LUMA);\n let l01 = dot(sampleBase(p + vec2<f32>(-e, 0.0), t), W_LUMA);\n let l21 = dot(sampleBase(p + vec2<f32>( e, 0.0), t), W_LUMA);\n let l02 = dot(sampleBase(p + vec2<f32>(-e, e), t), W_LUMA);\n let l12 = dot(sampleBase(p + vec2<f32>(0.0, e), t), W_LUMA);\n let l22 = dot(sampleBase(p + vec2<f32>( e, e), t), W_LUMA);\n let Gx = (l20 + 2.0 * l21 + l22) - (l00 + 2.0 * l01 + l02);\n let Gy = (l02 + 2.0 * l12 + l22) - (l00 + 2.0 * l10 + l20);\n let Jxx = Gx * Gx;\n let Jyy = Gy * Gy;\n let Jxy = Gx * Gy;\n let tr = Jxx + Jyy;\n let disc = sqrt(max((Jxx - Jyy) * (Jxx - Jyy) + 4.0 * Jxy * Jxy, 0.0));\n let lambdaMaj = 0.5 * (tr + disc);\n let lambdaMin = 0.5 * (tr - disc);\n var tangent: vec2<f32>;\n if (disc < 1e-7) {\n tangent = fallbackDir;\n } else {\n let theta = 0.5 * atan2(2.0 * Jxy, Jxx - Jyy);\n tangent = vec2<f32>(-sin(theta), cos(theta));\n }\n var A = (lambdaMaj - lambdaMin) / (lambdaMaj + lambdaMin + 1e-6);\n A = clamp(A, 0.0, 1.0);\n if (dot(tangent, fallbackDir) < 0.0) { tangent = -tangent; }\n let blendW = pow(A, 0.28);\n let dir = normalize(mix(fallbackDir, tangent, blendW) + vec2<f32>(1e-6, 1e-6));\n return vec3<f32>(dir, A);\n}\n\n// \u2500\u2500 Flow field (the WGSL twin \u2014 the simple flow seed the medium fallback dirs use) \u2500\u2500\nfn flowField(p: vec2<f32>, t: f32) -> vec2<f32> {\n // Curl-noise-driven swirl (the medium-default dir; the full radial/swirl/diagonal\n // dispatch rides uniforms the WGSL struct does not carry \u2014 the procedural mediums\n // use this multi/curl seed as the fallback the structure tensor relaxes toward).\n let n = fbm(p * 2.0 + vec2<f32>(t * 0.02, t * 0.02));\n let a = n * 6.2831853;\n var dir = vec2<f32>(cos(a), sin(a));\n // Cursor swirl bias (matches the GLSL flow cursor term \u2014 the steady attraction).\n let cursorStrength = u.scalars3.y;\n let cursorRadius = u.scalars3.z;\n let cursor = u.cursor.xy;\n if (cursorStrength > 0.001) {\n let toCur = cursor - p;\n let d = length(toCur);\n let r = max(cursorRadius, 0.01);\n let w = exp(-(d * d) / (r * r * 0.5));\n let tangent = vec2<f32>(-toCur.y, toCur.x) / max(d, 1e-4);\n let a0 = atan2(dir.y, dir.x);\n let a1 = atan2(tangent.y, tangent.x);\n var da = a1 - a0;\n da = atan2(sin(da), cos(da));\n let aa = a0 + da * w * cursorStrength;\n dir = vec2<f32>(cos(aa), sin(aa));\n }\n return dir;\n}\n\n// \u2500\u2500 Watercolor \u2014 wet-edge cauliflowers + granulation + wash banding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn mediumWatercolor(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n var col = col0;\n let eps = 0.004;\n let cx1 = sampleBase(p + vec2<f32>(eps, 0.0), t);\n let cx2 = sampleBase(p - vec2<f32>(eps, 0.0), t);\n let cy1 = sampleBase(p + vec2<f32>(0.0, eps), t);\n let cy2 = sampleBase(p - vec2<f32>(0.0, eps), t);\n let gx = dot(cx1 - cx2, W_LUMA);\n let gy = dot(cy1 - cy2, W_LUMA);\n let edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n let mask = smoothstep(0.0, 2.5, edge);\n col = col * mix(1.0, 0.78, mask * mWetEdge());\n\n let paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n let pigLoad = 1.0 - dot(col, W_LUMA);\n col = col * (1.0 - mGranulation() * pigLoad * (paper - 0.5));\n\n let band = fbm(vec2<f32>(p.x * 1.5, p.y * 0.4));\n col = col * (1.0 + 0.04 * (band - 0.5));\n return col;\n}\n\n// \u2500\u2500 Pastel \u2014 anisotropic stroke noise along the flow + tooth grain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn mediumPastel(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n var col = col0;\n let flow = flowField(p, t);\n let perp = vec2<f32>(-flow.y, flow.x);\n let strokeScale = mStrokeScale();\n let along = dot(p, flow) * strokeScale;\n var across = dot(p, perp) * strokeScale;\n across = across + 0.03 * (vnoise(p * 260.0) - 0.5);\n\n let aniso = mix(1.0, 0.18, mStrokeAnisotropy());\n let stroke = fbm(vec2<f32>(along * aniso, across));\n col = col * mix(1.0, 0.82 + 0.32 * stroke, mStrokeAmount());\n\n let tooth = vnoise(p * 800.0);\n col = col * (1.0 - 0.08 * mStrokeAmount() * (tooth - 0.5));\n return col;\n}\n\n// \u2500\u2500 Crayon \u2014 DRY wax pigment on paper tooth (the tooth-multiply + scumble) \u2500\u2500\nfn mediumCrayon(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let flow = structureTensorField(p, t, flowField(p, t)).xy;\n let ang = atan2(flow.y, flow.x);\n let ca = cos(-ang);\n let sa = sin(-ang);\n let pr = vec2<f32>(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n let aniso = mix(0.45, 0.95, mStrokeAnisotropy());\n let scale = max(mStrokeScale() * 1.6, 180.0);\n\n let t1 = vnoise(vec2<f32>(pr.x * scale * aniso, pr.y * scale));\n let t2 = vnoise(vec2<f32>(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + vec2<f32>(11.0, 11.0));\n let t3 = vnoise(vec2<f32>(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + vec2<f32>(23.0, 23.0));\n let tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n\n let pressure = clamp(mStrokeAmount(), 0.0, 1.0);\n let bite = mix(0.18, 0.46, pressure);\n var result = col0 * (1.0 - bite * (0.5 - tooth));\n\n let scumbleMask = smoothstep(0.42, 0.78, vnoise(vec2<f32>(pr.x * scale * 0.55, pr.y * scale * 0.22) + vec2<f32>(7.0, 7.0)));\n let scumbleCoverage = scumbleMask * pressure * 0.7;\n let paper = col0 * mix(0.80, 0.94, pressure);\n result = mix(paper, result, scumbleCoverage);\n\n let pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n let pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + vec2<f32>(19.0, 19.0)));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + vec2<f32>(17.0, 17.0)),\n hash21(pigmentCell * 2.1 + vec2<f32>(31.0, 31.0)),\n 0.45 + 0.55 * pigmentMask\n );\n\n result = saturate3(result, 1.08);\n return result;\n}\n\n// \u2500\u2500 Kuwahara \u2014 the SOFT anisotropic generalized Kuwahara painterly finish \u2500\u2500\u2500\u2500\n// THE KEYSTONE (Kyprianidis 2010). Aurora is a PROCEDURAL field (no input texture) so the\n// operator runs over sampleBase directly: an elliptical kernel oriented along the structure\n// tensor (elongated along the edge-tangent, narrowed across by the anisotropy), 8 OVERLAPPING\n// sectors via SMOOTH gaussian-angular weights, blended by 1/(1+var^q) (the SOFT criterion \u2192\n// no 8-spoke pinwheel by construction). 4 rings \u00D7 8 angular taps = 32 procedural samples.\nfn mediumKuwahara(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let stf = structureTensorField(p, t, flowField(p, t));\n let tangent = stf.xy;\n let A = stf.z;\n // The studio knob drives the patch radius; clamp into the procedural patch band.\n let radius = clamp(mKuwaharaRadius() + mStrokeScale() * 0.00018, 0.006, 0.024);\n let aniso = mix(1.0, 0.34, clamp(A, 0.0, 1.0));\n let ax = tangent;\n let ay = vec2<f32>(-tangent.y, tangent.x);\n\n // Per-sector weighted sums (8 sectors). WGSL has no runtime-indexed local arrays of\n // vectors as cleanly as GLSL, so unroll the sector accumulators explicitly.\n var mSum: array<vec3<f32>, 8>;\n var sSum: array<vec3<f32>, 8>;\n var wSum: array<f32, 8>;\n for (var i = 0; i < 8; i = i + 1) {\n mSum[i] = vec3<f32>(0.0);\n sSum[i] = vec3<f32>(0.0);\n wSum[i] = 0.0;\n }\n\n let RINGS = 4;\n let TAPS = 8;\n for (var r = 1; r <= RINGS; r = r + 1) {\n let rr = f32(r) / f32(RINGS);\n let radial = 1.0 - 0.6 * rr;\n for (var a = 0; a < TAPS; a = a + 1) {\n let ang = 6.2831853 * f32(a) / f32(TAPS);\n let dirE = ax * cos(ang) + ay * (sin(ang) * aniso);\n let off = dirE * (radius * rr);\n let c = sampleBase(p + off, t);\n for (var k = 0; k < 8; k = k + 1) {\n let sectorAng = 6.2831853 * f32(k) / 8.0;\n var d = ang - sectorAng;\n d = atan2(sin(d), cos(d));\n let sw = exp(-d * d * 2.2) * radial;\n mSum[k] = mSum[k] + c * sw;\n sSum[k] = sSum[k] + c * c * sw;\n wSum[k] = wSum[k] + sw;\n }\n }\n }\n\n let q = max(mKuwaharaQ(), 1.0);\n var accum = vec3<f32>(0.0);\n var accumW = 0.0;\n for (var k = 0; k < 8; k = k + 1) {\n let wk = max(wSum[k], 1e-4);\n let mean = mSum[k] / wk;\n let var3 = max(sSum[k] / wk - mean * mean, vec3<f32>(0.0));\n let variance = dot(var3, W_LUMA);\n let sectorW = 1.0 / (1.0 + pow(variance * 320.0, q));\n accum = accum + mean * sectorW;\n accumW = accumW + sectorW;\n }\n var result = accum / max(accumW, 1e-4);\n\n // Anchor in near-flat zones so the smoothing only bites at edges.\n let center = sampleBase(p, t);\n result = mix(center, result, clamp(0.35 + 0.65 * A, 0.0, 1.0));\n\n // Faint canvas tooth + pigment-saturation so the flattened patches read as oil paint.\n let tooth = vnoise(p * 300.0) - 0.5;\n result = result * (1.0 + tooth * 0.06 * mCanvasGrain());\n result = saturate3(result, 1.04);\n\n return mix(col0, result, clamp(mStrokeAmount(), 0.0, 1.0));\n}\n\n// \u2500\u2500 Medium dispatch (the WGSL twin of the GLSL main() if-ladder) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// uMedium: 0 smooth \u00B7 1 pastel \u00B7 2 watercolor \u00B7 3 oil \u00B7 4 crayon \u00B7 5 vangogh \u00B7\n// 6 oil-pastel \u00B7 7 kuwahara. The cheap peer mediums (pastel/watercolor/crayon) +\n// kuwahara port their own bodies; the oil/vangogh/oil-pastel STROKE cascade renders\n// the anisotropic-Kuwahara PAINTERLY finish here (a real oil-paint read, never the\n// smooth core \u2014 the \"NO FALLBACKS on Safari\" mandate; the full per-dab Starry-Night\n// stroke WGSL is the booked W-AURORA-WGPU-MEDIUMS-STROKES tail).\nfn applyMedium(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let medium = u.ints1.x;\n if (medium == 1) { return mediumPastel(col, p, t); }\n if (medium == 2) { return mediumWatercolor(col, p, t); }\n if (medium == 4) { return mediumCrayon(col, p, t); }\n // oil(3) / vangogh(5) / oil-pastel(6) / kuwahara(7) all render the painterly\n // anisotropic-Kuwahara finish on the WGSL primary (the WebGL2 fallback carries the\n // full per-dab stroke cascade for oil/vangogh/oil-pastel).\n if (medium == 3 || medium == 5 || medium == 6 || medium == 7) {\n return mediumKuwahara(col, p, t);\n }\n return col;\n}\n";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AURORA_WGSL = "\nconst MAX_NUCLEI: i32 = 6;\nconst MAX_STOPS: i32 = 8;\nconst PI: f32 = 3.141592653589793;\n\n// \u2500\u2500 Uniforms (the typed-struct source-of-truth \u2014 see aurora.wgsl.uniforms.ts) \u2500\u2500\n// Scalars are packed into vec4 lanes to keep the std140/WGSL 16-byte struct alignment\n// trivial. nuc0[i] = (pos.x, pos.y, radius, paletteBias); nuc1[i] = (valueBias,\n// driftRadius, driftPhase, elong); nuc2[i] = (angle, _, _, _). palette[i].rgb is the\n// CPU-baked linear-sRGB stop.\nstruct Uniforms {\n // scalars0: (uTime, uSoftmaxBeta, uValueVariance, uWarpAmount)\n scalars0: vec4<f32>,\n // scalars1: (uWarpScale, uWarpDrift, uSaturation, uPaperGrain)\n scalars1: vec4<f32>,\n // scalars2: (uAlpha, uNucleiDrift, uPaletteDrift, uBreathDepth)\n scalars2: vec4<f32>,\n // scalars3: (uBreathPeriod, uCursorStrength, uCursorRadius, _pad)\n scalars3: vec4<f32>,\n // cursor: (uCursor.x, uCursor.y, _pad, _pad)\n cursor: vec4<f32>,\n // ints: (uStopCount, uNucleiCount, uWarpMode, uNoiseOctaves)\n ints0: vec4<i32>,\n // ints1: (uMedium, uHuePath, _, _)\n ints1: vec4<i32>,\n palette: array<vec4<f32>, 8>,\n nuc0: array<vec4<f32>, 6>,\n nuc1: array<vec4<f32>, 6>,\n nuc2: array<vec4<f32>, 6>,\n};\n\n@group(0) @binding(0) var<uniform> u: Uniforms;\n\n// \u2500\u2500 Authoring-scale \u2192 rad/sec lifts (byte-identical to aurora.frag.ts) \u2500\u2500\nconst K_NUCLEI: f32 = 14.0;\nconst K_PAL: f32 = 24.0;\nconst K_WARP: f32 = 5.0;\n\n// \u2500\u2500 Noise (aurora-local hash21/vnoise/fbm \u2014 legitimately divergent per AV.W2 \u00A73a) \u2500\u2500\nfn hash21(p0: vec2<f32>) -> f32 {\n var p = fract(p0 * vec2<f32>(123.34, 456.21));\n p = p + dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\n\nfn hash22(p0: vec2<f32>) -> vec2<f32> {\n let p = vec2<f32>(dot(p0, vec2<f32>(127.1, 311.7)),\n dot(p0, vec2<f32>(269.5, 183.3)));\n return fract(sin(p) * 43758.5453);\n}\n\nfn vnoise(p: vec2<f32>) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let uu = f * f * (3.0 - 2.0 * f);\n let a = hash21(i);\n let b = hash21(i + vec2<f32>(1.0, 0.0));\n let c = hash21(i + vec2<f32>(0.0, 1.0));\n let d = hash21(i + vec2<f32>(1.0, 1.0));\n return mix(mix(a, b, uu.x), mix(c, d, uu.x), uu.y);\n}\n\nconst FBM_ROT: mat2x2<f32> = mat2x2<f32>(0.8, 0.6, -0.6, 0.8);\n\nfn fbm(p0: vec2<f32>) -> f32 {\n var v = 0.0;\n var a = 0.5;\n var p = p0;\n let octaves = u.ints0.w;\n for (var i = 0; i < 5; i = i + 1) {\n if (i >= octaves) { break; }\n v = v + a * vnoise(p);\n p = FBM_ROT * p * 2.02;\n a = a * 0.5;\n }\n return v;\n}\n\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// value.js interpolateHue in the NORMALIZED-TURNS domain. methods: 0 shorter,\n// 1 longer, 2 increasing, 3 decreasing.\nfn interpolateHueTurns(h0: f32, h1: f32, t: f32, method: i32) -> f32 {\n let TAU = 2.0 * PI;\n var a = h0 / TAU;\n var b = h1 / TAU;\n let i = b - a;\n if (method == 0) { // shorter\n if (i > 0.5) { a = a + 1.0; } else if (i < -0.5) { b = b + 1.0; }\n } else if (method == 1) { // longer\n if (i > 0.0 && i < 0.5) { a = a + 1.0; } else if (i > -0.5 && i <= 0.0) { b = b + 1.0; }\n } else if (method == 2) { // increasing\n if (i < 0.0) { b = b + 1.0; }\n } else { // decreasing\n if (i > 0.0) { a = a + 1.0; }\n }\n var r = a + t * (b - a);\n r = fract(r); // fract is non-negative in WGSL\n return r * TAU; // back to radians\n}\n\n// Linear-sRGB endpoint pair \u2192 OKLab-rectangular interpolation \u2192 linear sRGB.\nfn mixPaletteOklab(linA: vec3<f32>, linB: vec3<f32>, t: f32) -> vec3<f32> {\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linA);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linB);\n let lab = mix(labA, labB, t);\n return oklabToLinearSrgb(lab);\n}\n\n// Linear-sRGB endpoint pair \u2192 OKLCh hue-arc interpolation \u2192 linear sRGB.\nfn mixPaletteOklchArc(linA: vec3<f32>, linB: vec3<f32>, t: f32, method: i32) -> vec3<f32> {\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linA);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linB);\n let lchA = oklabToOklch(labA);\n let lchB = oklabToOklch(labB);\n let L = mix(lchA.x, lchB.x, t);\n let C = mix(lchA.y, lchB.y, t);\n let H = interpolateHueTurns(lchA.z, lchB.z, t, method);\n return oklabToLinearSrgb(oklchToOklab(vec3<f32>(L, C, H)));\n}\n\n// The ramp dispatcher (the samplePalette splices it). tRaw is the raw inter-stop\n// parameter [0,1]; the smoothstep ease + the huePath branch live here so the ramp is\n// one source. increasing(2)/decreasing(3) take the OKLCh hue-arc; every other method\n// takes the OKLab-rectangular straight line.\nfn samplePaletteRamp(a: vec3<f32>, b: vec3<f32>, tRaw: f32, huePath: i32) -> vec3<f32> {\n let t = smoothstep(0.0, 1.0, tRaw);\n if (huePath == 2 || huePath == 3) {\n return mixPaletteOklchArc(a, b, t, huePath);\n }\n return mixPaletteOklab(a, b, t);\n}\n\n// Interleaved Gradient Noise (Jimenez) \u2014 display-space 1-LSB dither.\nfn ign(p: vec2<f32>) -> f32 {\n return fract(52.9829189 * fract(dot(p, vec2<f32>(0.06711056, 0.00583715))));\n}\n\n// Cellular / Worley f1.\nfn cellular(p: vec2<f32>) -> f32 {\n let i = floor(p);\n let f = fract(p);\n var m = 1e9;\n for (var y = -1; y <= 1; y = y + 1) {\n for (var x = -1; x <= 1; x = x + 1) {\n let g = vec2<f32>(f32(x), f32(y));\n let o = hash22(i + g);\n let r = g + o - f;\n m = min(m, dot(r, r));\n }\n }\n return sqrt(m);\n}\n\n// \u2500\u2500 Warp (Quilez double warp + the three warp modes + the cursor swirl) \u2500\u2500\nfn domainWarp(p: vec2<f32>, t: f32) -> vec2<f32> {\n let warpScale = u.scalars1.x;\n let warpDrift = u.scalars1.y;\n let warpAmount = u.scalars0.w;\n let warpMode = u.ints0.z;\n let cursorStrength = u.scalars3.y;\n let cursorRadius = u.scalars3.z;\n let cursor = u.cursor.xy;\n\n let q = vec2<f32>(fbm(p * warpScale + vec2<f32>(0.0, 0.0) + t * warpDrift * K_WARP),\n fbm(p * warpScale + vec2<f32>(5.2, 1.3) + t * warpDrift * K_WARP));\n let r = vec2<f32>(fbm(p * warpScale + 4.0 * q + vec2<f32>(1.7, 9.2)),\n fbm(p * warpScale + 4.0 * q + vec2<f32>(8.3, 2.8)));\n\n var warp = r;\n if (warpMode == 1) {\n // cellular \u2014 chunky territories\n let c1 = cellular(p * warpScale * 1.5 + vec2<f32>(t * warpDrift * K_WARP * 2.0, 0.0));\n let c2 = cellular(p * warpScale * 1.5 + vec2<f32>(11.0, 7.0 + t * warpDrift * K_WARP * 2.0));\n warp = vec2<f32>(c1, c2);\n } else if (warpMode == 2) {\n // hybrid \u2014 fbm + cellular averaged\n let c1 = cellular(p * warpScale * 1.2);\n let c2 = cellular(p * warpScale * 1.2 + vec2<f32>(11.0, 7.0));\n warp = mix(r, vec2<f32>(c1, c2), 0.5);\n }\n var warped = p + warpAmount * warp;\n\n // Cursor swirl \u2014 rotate p around the cursor with radial falloff.\n if (cursorStrength > 0.001) {\n let toP = p - cursor;\n let d = length(toP);\n let rr = max(cursorRadius, 0.01);\n let w = exp(-(d * d) / (rr * rr * 0.45));\n let ang = w * cursorStrength * 2.1;\n let ca = cos(ang);\n let sa = sin(ang);\n var rotated = vec2<f32>(ca * toP.x - sa * toP.y, sa * toP.x + ca * toP.y) + cursor;\n let pinch = w * cursorStrength * 0.08;\n rotated = mix(rotated, cursor, pinch);\n warped = mix(warped, rotated + warpAmount * warp * 0.7, w * cursorStrength);\n }\n return warped;\n}\n\n// \u2500\u2500 Palette LUT \u2500\u2500\nfn samplePalette(id0: f32) -> vec3<f32> {\n let id = clamp(id0, 0.0, 1.0);\n let stopCount = u.ints0.x;\n let scaled = id * f32(stopCount - 1);\n let i0 = i32(floor(scaled));\n let i1 = min(i0 + 1, stopCount - 1);\n let t = fract(scaled);\n return samplePaletteRamp(u.palette[i0].rgb, u.palette[i1].rgb, t, u.ints1.y);\n}\n\n// \u2500\u2500 Nuclei field (anisotropic Gaussian softmax + drift + palette drift) \u2500\u2500\nstruct NucleiResult { paletteId: f32, valueMod: f32 };\n\nfn nucleiField(p: vec2<f32>, t: f32) -> NucleiResult {\n let nucleiCount = u.ints0.y;\n let nucleiDrift = u.scalars2.y;\n let paletteDrift = u.scalars2.z;\n let softmaxBeta = u.scalars0.y;\n\n var accumBias = 0.0;\n var accumValue = 0.0;\n var accumW = 0.0;\n for (var i = 0; i < MAX_NUCLEI; i = i + 1) {\n if (i >= nucleiCount) { break; }\n let n0 = u.nuc0[i]; // pos.x, pos.y, radius, paletteBias\n let n1 = u.nuc1[i]; // valueBias, driftRadius, driftPhase, elong\n let angle = u.nuc2[i].x;\n let driftRadius = n1.y;\n let driftPhase = n1.z;\n let posI = n0.xy + driftRadius * vec2<f32>(\n cos(t * nucleiDrift * K_NUCLEI + driftPhase),\n sin(t * nucleiDrift * K_NUCLEI + driftPhase * 1.13)\n );\n let diff = p - posI;\n let ca = cos(angle);\n let sa = sin(angle);\n let local = vec2<f32>(ca * diff.x + sa * diff.y, -sa * diff.x + ca * diff.y);\n let along = local.x / max(n1.w, 0.01);\n let across = local.y;\n let d2 = along * along + across * across;\n let r = max(n0.z, 0.01);\n let w = exp(-softmaxBeta * d2 / (r * r));\n accumBias = accumBias + w * n0.w;\n accumValue = accumValue + w * n1.x;\n accumW = accumW + w;\n }\n var paletteId = accumBias / max(accumW, 1e-4);\n let valueMod = accumValue / max(accumW, 1e-4);\n\n let palAmp = clamp(paletteDrift * 6.0, 0.0, 0.06);\n paletteId = paletteId + palAmp * sin(t * paletteDrift * K_PAL);\n paletteId = clamp(paletteId, 0.0, 1.0);\n return NucleiResult(paletteId, valueMod);\n}\n\n// \u2500\u2500 Color utils (OKLCh saturation) \u2500\u2500\nfn linOklab(lin: vec3<f32>) -> vec3<f32> {\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\nfn saturate3(c: vec3<f32>, amt: f32) -> vec3<f32> {\n var lch = oklabToOklch(linOklab(c));\n lch.y = max(lch.y * amt, 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3<f32>(0.0));\n}\n\n// \u2500\u2500 PBR-Neutral tonemap (named aces() in the GLSL \u2014 the tonemap slot) \u2500\u2500\nfn aces(color0: vec3<f32>) -> vec3<f32> {\n let startCompression = 0.8 - 0.04;\n let desaturation = 0.15;\n var color = color0;\n let x = min(color.r, min(color.g, color.b));\n var offset = 0.04;\n if (x < 0.08) { offset = x - 6.25 * x * x; }\n color = color - offset;\n let peak = max(color.r, max(color.g, color.b));\n if (peak < startCompression) { return clamp(color, vec3<f32>(0.0), vec3<f32>(1.0)); }\n let d = 1.0 - startCompression;\n let newPeak = 1.0 - d * d / (peak + d - startCompression);\n color = color * (newPeak / peak);\n let g = 1.0 - 1.0 / (desaturation * (peak - newPeak) + 1.0);\n return clamp(mix(color, newPeak * vec3<f32>(1.0), g), vec3<f32>(0.0), vec3<f32>(1.0));\n}\n\n// \u2500\u2500 Full-screen triangle vertex stage (the pilot idiom \u2014 no vertex buffer) \u2500\u2500\nstruct VSOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) uv: vec2<f32>,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {\n // NDC corners of the covering triangle (-1,-1), (3,-1), (-1,3).\n var p = vec2<f32>(-1.0, -1.0);\n if (vi == 1u) { p = vec2<f32>(3.0, -1.0); }\n else if (vi == 2u) { p = vec2<f32>(-1.0, 3.0); }\n var out: VSOut;\n out.pos = vec4<f32>(p, 0.0, 1.0);\n // vUv = aPos * 0.5 + 0.5 (the aurora.vert convention).\n out.uv = p * 0.5 + 0.5;\n return out;\n}\n\n// \u2500\u2500 Fragment stage (the main() transcription) \u2500\u2500\n@fragment\nfn fs_main(in: VSOut) -> @location(0) vec4<f32> {\n let t = u.scalars0.x;\n let valueVariance = u.scalars0.z;\n let breathDepth = u.scalars2.w;\n let breathPeriod = u.scalars3.x;\n let saturation = u.scalars1.z;\n let paperGrain = u.scalars1.w;\n let alpha = u.scalars2.x;\n\n let uv = in.uv;\n let pN = uv;\n\n let p_warp = domainWarp(pN, t);\n\n let nf = nucleiField(p_warp, t);\n var col = samplePalette(nf.paletteId);\n col = col * (1.0 + valueVariance * nf.valueMod);\n\n // Breath \u2014 slow global luminance wobble.\n let breath = sin(t * 6.2831 / max(breathPeriod, 1.0));\n col = col * (1.0 + breathDepth * breath * 0.5);\n\n // The painterly mediums (uMedium 1-6) are the WebGL2 fallback's register; the WGSL\n // primary renders the smooth core for the default parity capture (the booked\n // W-AURORA-WGPU-MEDIUMS successor ports the painterly bodies).\n\n // Saturation trim.\n col = saturate3(col, saturation);\n\n // Tonemap + film grain.\n col = aces(col);\n // gl_FragCoord.xy \u2192 the device-pixel position (the WGSL @builtin(position)).\n let fragCoord = in.pos.xy;\n let grain = hash21(fragCoord + t * 17.0);\n col = col + (grain - 0.5) * paperGrain;\n\n col = clamp(col * 0.985 + 0.008, vec3<f32>(0.0), vec3<f32>(1.0));\n\n // MANDATORY OETF \u2014 closes the seam (the whole pipeline above runs in LINEAR).\n col = linearToSrgb(col);\n\n // 1-LSB triangular IGN dither in DISPLAY space, AFTER the OETF.\n col = col + (1.0 / 255.0) * (ign(fragCoord) - 0.5);\n\n // Premultiply on DISPLAY-space color (matches the WebGL2 path: OETF, then premul).\n return vec4<f32>(col * alpha, alpha);\n}\n";
|
|
1
|
+
export declare const AURORA_WGSL = "\nconst MAX_NUCLEI: i32 = 6;\nconst MAX_STOPS: i32 = 8;\nconst PI: f32 = 3.141592653589793;\n\n// \u2500\u2500 Uniforms (the typed-struct source-of-truth \u2014 see aurora.wgsl.uniforms.ts) \u2500\u2500\n// Scalars are packed into vec4 lanes to keep the std140/WGSL 16-byte struct alignment\n// trivial. nuc0[i] = (pos.x, pos.y, radius, paletteBias); nuc1[i] = (valueBias,\n// driftRadius, driftPhase, elong); nuc2[i] = (angle, _, _, _). palette[i].rgb is the\n// CPU-baked linear-sRGB stop.\nstruct Uniforms {\n // scalars0: (uTime, uSoftmaxBeta, uValueVariance, uWarpAmount)\n scalars0: vec4<f32>,\n // scalars1: (uWarpScale, uWarpDrift, uSaturation, uPaperGrain)\n scalars1: vec4<f32>,\n // scalars2: (uAlpha, uNucleiDrift, uPaletteDrift, uBreathDepth)\n scalars2: vec4<f32>,\n // scalars3: (uBreathPeriod, uCursorStrength, uCursorRadius, _pad)\n scalars3: vec4<f32>,\n // cursor: (uCursor.x, uCursor.y, _pad, _pad)\n cursor: vec4<f32>,\n // ints: (uStopCount, uNucleiCount, uWarpMode, uNoiseOctaves)\n ints0: vec4<i32>,\n // ints1: (uMedium, uHuePath, _, _)\n ints1: vec4<i32>,\n palette: array<vec4<f32>, 8>,\n nuc0: array<vec4<f32>, 6>,\n nuc1: array<vec4<f32>, 6>,\n nuc2: array<vec4<f32>, 6>,\n // BC.W-VIZ-AURORA (T4) \u2014 the painterly-medium scalar lanes. APPENDED after the\n // arrays so EVERY existing offset (and the smooth-default parity capture) is\n // byte-identical (these lanes are written 0 on a smooth config). scalars4:\n // (uStrokeAmount, uStrokeScale, uStrokeAnisotropy, uCanvasGrain). scalars5:\n // (uWetEdge, uGranulation, uBrokenColor, uKuwaharaQ). kuwahara: (uKuwaharaRadius,\n // uKuwaharaSectors, _, _) \u2014 the Kuwahara radius/sectors/q in lockstep with\n // packAuroraWGPUUniforms (the typed-struct discipline).\n scalars4: vec4<f32>,\n scalars5: vec4<f32>,\n kuwahara: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> u: Uniforms;\n\n// \u2500\u2500 Authoring-scale \u2192 rad/sec lifts (byte-identical to aurora.frag.ts) \u2500\u2500\nconst K_NUCLEI: f32 = 14.0;\nconst K_PAL: f32 = 24.0;\nconst K_WARP: f32 = 5.0;\n\n// \u2500\u2500 Noise (aurora-local hash21/vnoise/fbm \u2014 legitimately divergent per AV.W2 \u00A73a) \u2500\u2500\nfn hash21(p0: vec2<f32>) -> f32 {\n var p = fract(p0 * vec2<f32>(123.34, 456.21));\n p = p + dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\n\nfn hash22(p0: vec2<f32>) -> vec2<f32> {\n let p = vec2<f32>(dot(p0, vec2<f32>(127.1, 311.7)),\n dot(p0, vec2<f32>(269.5, 183.3)));\n return fract(sin(p) * 43758.5453);\n}\n\nfn vnoise(p: vec2<f32>) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let uu = f * f * (3.0 - 2.0 * f);\n let a = hash21(i);\n let b = hash21(i + vec2<f32>(1.0, 0.0));\n let c = hash21(i + vec2<f32>(0.0, 1.0));\n let d = hash21(i + vec2<f32>(1.0, 1.0));\n return mix(mix(a, b, uu.x), mix(c, d, uu.x), uu.y);\n}\n\nconst FBM_ROT: mat2x2<f32> = mat2x2<f32>(0.8, 0.6, -0.6, 0.8);\n\nfn fbm(p0: vec2<f32>) -> f32 {\n var v = 0.0;\n var a = 0.5;\n var p = p0;\n let octaves = u.ints0.w;\n for (var i = 0; i < 5; i = i + 1) {\n if (i >= octaves) { break; }\n v = v + a * vnoise(p);\n p = FBM_ROT * p * 2.02;\n a = a * 0.5;\n }\n return v;\n}\n\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// value.js interpolateHue in the NORMALIZED-TURNS domain. methods: 0 shorter,\n// 1 longer, 2 increasing, 3 decreasing.\nfn interpolateHueTurns(h0: f32, h1: f32, t: f32, method: i32) -> f32 {\n let TAU = 2.0 * PI;\n var a = h0 / TAU;\n var b = h1 / TAU;\n let i = b - a;\n if (method == 0) { // shorter\n if (i > 0.5) { a = a + 1.0; } else if (i < -0.5) { b = b + 1.0; }\n } else if (method == 1) { // longer\n if (i > 0.0 && i < 0.5) { a = a + 1.0; } else if (i > -0.5 && i <= 0.0) { b = b + 1.0; }\n } else if (method == 2) { // increasing\n if (i < 0.0) { b = b + 1.0; }\n } else { // decreasing\n if (i > 0.0) { a = a + 1.0; }\n }\n var r = a + t * (b - a);\n r = fract(r); // fract is non-negative in WGSL\n return r * TAU; // back to radians\n}\n\n// Linear-sRGB endpoint pair \u2192 OKLab-rectangular interpolation \u2192 linear sRGB.\nfn mixPaletteOklab(linA: vec3<f32>, linB: vec3<f32>, t: f32) -> vec3<f32> {\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linA);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linB);\n let lab = mix(labA, labB, t);\n return oklabToLinearSrgb(lab);\n}\n\n// Linear-sRGB endpoint pair \u2192 OKLCh hue-arc interpolation \u2192 linear sRGB.\nfn mixPaletteOklchArc(linA: vec3<f32>, linB: vec3<f32>, t: f32, method: i32) -> vec3<f32> {\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linA);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linB);\n let lchA = oklabToOklch(labA);\n let lchB = oklabToOklch(labB);\n let L = mix(lchA.x, lchB.x, t);\n let C = mix(lchA.y, lchB.y, t);\n let H = interpolateHueTurns(lchA.z, lchB.z, t, method);\n return oklabToLinearSrgb(oklchToOklab(vec3<f32>(L, C, H)));\n}\n\n// The ramp dispatcher (the samplePalette splices it). tRaw is the raw inter-stop\n// parameter [0,1]; the smoothstep ease + the huePath branch live here so the ramp is\n// one source. increasing(2)/decreasing(3) take the OKLCh hue-arc; every other method\n// takes the OKLab-rectangular straight line.\nfn samplePaletteRamp(a: vec3<f32>, b: vec3<f32>, tRaw: f32, huePath: i32) -> vec3<f32> {\n let t = smoothstep(0.0, 1.0, tRaw);\n if (huePath == 2 || huePath == 3) {\n return mixPaletteOklchArc(a, b, t, huePath);\n }\n return mixPaletteOklab(a, b, t);\n}\n\n// Interleaved Gradient Noise (Jimenez) \u2014 display-space 1-LSB dither.\nfn ign(p: vec2<f32>) -> f32 {\n return fract(52.9829189 * fract(dot(p, vec2<f32>(0.06711056, 0.00583715))));\n}\n\n// Cellular / Worley f1.\nfn cellular(p: vec2<f32>) -> f32 {\n let i = floor(p);\n let f = fract(p);\n var m = 1e9;\n for (var y = -1; y <= 1; y = y + 1) {\n for (var x = -1; x <= 1; x = x + 1) {\n let g = vec2<f32>(f32(x), f32(y));\n let o = hash22(i + g);\n let r = g + o - f;\n m = min(m, dot(r, r));\n }\n }\n return sqrt(m);\n}\n\n// \u2500\u2500 Warp (Quilez double warp + the three warp modes + the cursor swirl) \u2500\u2500\nfn domainWarp(p: vec2<f32>, t: f32) -> vec2<f32> {\n let warpScale = u.scalars1.x;\n let warpDrift = u.scalars1.y;\n let warpAmount = u.scalars0.w;\n let warpMode = u.ints0.z;\n let cursorStrength = u.scalars3.y;\n let cursorRadius = u.scalars3.z;\n let cursor = u.cursor.xy;\n\n let q = vec2<f32>(fbm(p * warpScale + vec2<f32>(0.0, 0.0) + t * warpDrift * K_WARP),\n fbm(p * warpScale + vec2<f32>(5.2, 1.3) + t * warpDrift * K_WARP));\n let r = vec2<f32>(fbm(p * warpScale + 4.0 * q + vec2<f32>(1.7, 9.2)),\n fbm(p * warpScale + 4.0 * q + vec2<f32>(8.3, 2.8)));\n\n var warp = r;\n if (warpMode == 1) {\n // cellular \u2014 chunky territories\n let c1 = cellular(p * warpScale * 1.5 + vec2<f32>(t * warpDrift * K_WARP * 2.0, 0.0));\n let c2 = cellular(p * warpScale * 1.5 + vec2<f32>(11.0, 7.0 + t * warpDrift * K_WARP * 2.0));\n warp = vec2<f32>(c1, c2);\n } else if (warpMode == 2) {\n // hybrid \u2014 fbm + cellular averaged\n let c1 = cellular(p * warpScale * 1.2);\n let c2 = cellular(p * warpScale * 1.2 + vec2<f32>(11.0, 7.0));\n warp = mix(r, vec2<f32>(c1, c2), 0.5);\n }\n var warped = p + warpAmount * warp;\n\n // Cursor swirl \u2014 rotate p around the cursor with radial falloff.\n if (cursorStrength > 0.001) {\n let toP = p - cursor;\n let d = length(toP);\n let rr = max(cursorRadius, 0.01);\n let w = exp(-(d * d) / (rr * rr * 0.45));\n let ang = w * cursorStrength * 2.1;\n let ca = cos(ang);\n let sa = sin(ang);\n var rotated = vec2<f32>(ca * toP.x - sa * toP.y, sa * toP.x + ca * toP.y) + cursor;\n let pinch = w * cursorStrength * 0.08;\n rotated = mix(rotated, cursor, pinch);\n warped = mix(warped, rotated + warpAmount * warp * 0.7, w * cursorStrength);\n }\n return warped;\n}\n\n// \u2500\u2500 Palette LUT \u2500\u2500\nfn samplePalette(id0: f32) -> vec3<f32> {\n let id = clamp(id0, 0.0, 1.0);\n let stopCount = u.ints0.x;\n let scaled = id * f32(stopCount - 1);\n let i0 = i32(floor(scaled));\n let i1 = min(i0 + 1, stopCount - 1);\n let t = fract(scaled);\n return samplePaletteRamp(u.palette[i0].rgb, u.palette[i1].rgb, t, u.ints1.y);\n}\n\n// \u2500\u2500 Nuclei field (anisotropic Gaussian softmax + drift + palette drift) \u2500\u2500\nstruct NucleiResult { paletteId: f32, valueMod: f32 };\n\nfn nucleiField(p: vec2<f32>, t: f32) -> NucleiResult {\n let nucleiCount = u.ints0.y;\n let nucleiDrift = u.scalars2.y;\n let paletteDrift = u.scalars2.z;\n let softmaxBeta = u.scalars0.y;\n\n var accumBias = 0.0;\n var accumValue = 0.0;\n var accumW = 0.0;\n for (var i = 0; i < MAX_NUCLEI; i = i + 1) {\n if (i >= nucleiCount) { break; }\n let n0 = u.nuc0[i]; // pos.x, pos.y, radius, paletteBias\n let n1 = u.nuc1[i]; // valueBias, driftRadius, driftPhase, elong\n let angle = u.nuc2[i].x;\n let driftRadius = n1.y;\n let driftPhase = n1.z;\n let posI = n0.xy + driftRadius * vec2<f32>(\n cos(t * nucleiDrift * K_NUCLEI + driftPhase),\n sin(t * nucleiDrift * K_NUCLEI + driftPhase * 1.13)\n );\n let diff = p - posI;\n let ca = cos(angle);\n let sa = sin(angle);\n let local = vec2<f32>(ca * diff.x + sa * diff.y, -sa * diff.x + ca * diff.y);\n let along = local.x / max(n1.w, 0.01);\n let across = local.y;\n let d2 = along * along + across * across;\n let r = max(n0.z, 0.01);\n let w = exp(-softmaxBeta * d2 / (r * r));\n accumBias = accumBias + w * n0.w;\n accumValue = accumValue + w * n1.x;\n accumW = accumW + w;\n }\n var paletteId = accumBias / max(accumW, 1e-4);\n let valueMod = accumValue / max(accumW, 1e-4);\n\n let palAmp = clamp(paletteDrift * 6.0, 0.0, 0.06);\n paletteId = paletteId + palAmp * sin(t * paletteDrift * K_PAL);\n paletteId = clamp(paletteId, 0.0, 1.0);\n return NucleiResult(paletteId, valueMod);\n}\n\n// \u2500\u2500 Color utils (OKLCh saturation) \u2500\u2500\nfn linOklab(lin: vec3<f32>) -> vec3<f32> {\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\nfn saturate3(c: vec3<f32>, amt: f32) -> vec3<f32> {\n var lch = oklabToOklch(linOklab(c));\n lch.y = max(lch.y * amt, 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3<f32>(0.0));\n}\n\n// \u2500\u2500 PBR-Neutral tonemap (named aces() in the GLSL \u2014 the tonemap slot) \u2500\u2500\nfn aces(color0: vec3<f32>) -> vec3<f32> {\n let startCompression = 0.8 - 0.04;\n let desaturation = 0.15;\n var color = color0;\n let x = min(color.r, min(color.g, color.b));\n var offset = 0.04;\n if (x < 0.08) { offset = x - 6.25 * x * x; }\n color = color - offset;\n let peak = max(color.r, max(color.g, color.b));\n if (peak < startCompression) { return clamp(color, vec3<f32>(0.0), vec3<f32>(1.0)); }\n let d = 1.0 - startCompression;\n let newPeak = 1.0 - d * d / (peak + d - startCompression);\n color = color * (newPeak / peak);\n let g = 1.0 - 1.0 / (desaturation * (peak - newPeak) + 1.0);\n return clamp(mix(color, newPeak * vec3<f32>(1.0), g), vec3<f32>(0.0), vec3<f32>(1.0));\n}\n\n// \u2500\u2500 BC.W-VIZ-AURORA (T4) \u2014 the painterly-medium bodies (the keystone WGSL port) \u2500\u2500\n// Spliced HERE, after every helper the mediums consume is defined (domainWarp,\n// nucleiField, samplePalette, linOklab, saturate3, oklabToOklch/oklchToOklab,\n// hash21/vnoise/fbm) and before the vertex/fragment stages. applyMedium(col,p,t)\n// is the dispatch the fragment stage calls.\n\n// \u2500\u2500 Painterly luma + medium scalar accessors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst W_LUMA: vec3<f32> = vec3<f32>(0.2126, 0.7152, 0.0722);\n\nfn mStrokeAmount() -> f32 { return u.scalars4.x; }\nfn mStrokeScale() -> f32 { return u.scalars4.y; }\nfn mStrokeAnisotropy() -> f32 { return u.scalars4.z; }\nfn mCanvasGrain() -> f32 { return u.scalars4.w; }\nfn mWetEdge() -> f32 { return u.scalars5.x; }\nfn mGranulation() -> f32 { return u.scalars5.y; }\nfn mBrokenColor() -> f32 { return u.scalars5.z; }\nfn mKuwaharaQ() -> f32 { return u.scalars5.w; }\nfn mKuwaharaRadius() -> f32 { return u.kuwahara.x; }\n\n// \u2500\u2500 Base color re-sample (the edge-mask operator the mediums Sobel/sector-sample) \u2500\u2500\n// The WGSL twin of mediums.glsl.ts sampleBase: warp -> nuclei field -> palette -> value.\nfn sampleBase(p: vec2<f32>, t: f32) -> vec3<f32> {\n let pw = domainWarp(p, t);\n let nf = nucleiField(pw, t);\n var c = samplePalette(nf.paletteId);\n c = c * (1.0 + u.scalars0.z * nf.valueMod);\n return c;\n}\n\n// \u2500\u2500 Broken color (hue + chroma jitter at constant OKLCh L) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn brokenColorJitter(c: vec3<f32>, hueSeed: f32, valueSeed: f32, strength: f32) -> vec3<f32> {\n let amt = clamp(mBrokenColor() * strength, 0.0, 1.0);\n if (amt <= 0.001) { return c; }\n var lch = oklabToOklch(linOklab(c));\n lch.z = lch.z + (hueSeed - 0.5) * (32.0 * PI / 180.0) * amt;\n lch.y = max(lch.y * (1.0 + (valueSeed - 0.5) * 0.28 * amt), 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3<f32>(0.0));\n}\n\n// \u2500\u2500 Structure-tensor / edge-tangent-flow (AW.W4.1; the WGSL twin) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Returns vec3(tangent.x, tangent.y, A) \u2014 the minor-eigenvector edge-tangent + coherence.\nfn structureTensorField(p: vec2<f32>, t: f32, fallbackDir: vec2<f32>) -> vec3<f32> {\n let e = 0.0035;\n let l00 = dot(sampleBase(p + vec2<f32>(-e, -e), t), W_LUMA);\n let l10 = dot(sampleBase(p + vec2<f32>(0.0, -e), t), W_LUMA);\n let l20 = dot(sampleBase(p + vec2<f32>( e, -e), t), W_LUMA);\n let l01 = dot(sampleBase(p + vec2<f32>(-e, 0.0), t), W_LUMA);\n let l21 = dot(sampleBase(p + vec2<f32>( e, 0.0), t), W_LUMA);\n let l02 = dot(sampleBase(p + vec2<f32>(-e, e), t), W_LUMA);\n let l12 = dot(sampleBase(p + vec2<f32>(0.0, e), t), W_LUMA);\n let l22 = dot(sampleBase(p + vec2<f32>( e, e), t), W_LUMA);\n let Gx = (l20 + 2.0 * l21 + l22) - (l00 + 2.0 * l01 + l02);\n let Gy = (l02 + 2.0 * l12 + l22) - (l00 + 2.0 * l10 + l20);\n let Jxx = Gx * Gx;\n let Jyy = Gy * Gy;\n let Jxy = Gx * Gy;\n let tr = Jxx + Jyy;\n let disc = sqrt(max((Jxx - Jyy) * (Jxx - Jyy) + 4.0 * Jxy * Jxy, 0.0));\n let lambdaMaj = 0.5 * (tr + disc);\n let lambdaMin = 0.5 * (tr - disc);\n var tangent: vec2<f32>;\n if (disc < 1e-7) {\n tangent = fallbackDir;\n } else {\n let theta = 0.5 * atan2(2.0 * Jxy, Jxx - Jyy);\n tangent = vec2<f32>(-sin(theta), cos(theta));\n }\n var A = (lambdaMaj - lambdaMin) / (lambdaMaj + lambdaMin + 1e-6);\n A = clamp(A, 0.0, 1.0);\n if (dot(tangent, fallbackDir) < 0.0) { tangent = -tangent; }\n let blendW = pow(A, 0.28);\n let dir = normalize(mix(fallbackDir, tangent, blendW) + vec2<f32>(1e-6, 1e-6));\n return vec3<f32>(dir, A);\n}\n\n// \u2500\u2500 Flow field (the WGSL twin \u2014 the simple flow seed the medium fallback dirs use) \u2500\u2500\nfn flowField(p: vec2<f32>, t: f32) -> vec2<f32> {\n // Curl-noise-driven swirl (the medium-default dir; the full radial/swirl/diagonal\n // dispatch rides uniforms the WGSL struct does not carry \u2014 the procedural mediums\n // use this multi/curl seed as the fallback the structure tensor relaxes toward).\n let n = fbm(p * 2.0 + vec2<f32>(t * 0.02, t * 0.02));\n let a = n * 6.2831853;\n var dir = vec2<f32>(cos(a), sin(a));\n // Cursor swirl bias (matches the GLSL flow cursor term \u2014 the steady attraction).\n let cursorStrength = u.scalars3.y;\n let cursorRadius = u.scalars3.z;\n let cursor = u.cursor.xy;\n if (cursorStrength > 0.001) {\n let toCur = cursor - p;\n let d = length(toCur);\n let r = max(cursorRadius, 0.01);\n let w = exp(-(d * d) / (r * r * 0.5));\n let tangent = vec2<f32>(-toCur.y, toCur.x) / max(d, 1e-4);\n let a0 = atan2(dir.y, dir.x);\n let a1 = atan2(tangent.y, tangent.x);\n var da = a1 - a0;\n da = atan2(sin(da), cos(da));\n let aa = a0 + da * w * cursorStrength;\n dir = vec2<f32>(cos(aa), sin(aa));\n }\n return dir;\n}\n\n// \u2500\u2500 Watercolor \u2014 wet-edge cauliflowers + granulation + wash banding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn mediumWatercolor(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n var col = col0;\n let eps = 0.004;\n let cx1 = sampleBase(p + vec2<f32>(eps, 0.0), t);\n let cx2 = sampleBase(p - vec2<f32>(eps, 0.0), t);\n let cy1 = sampleBase(p + vec2<f32>(0.0, eps), t);\n let cy2 = sampleBase(p - vec2<f32>(0.0, eps), t);\n let gx = dot(cx1 - cx2, W_LUMA);\n let gy = dot(cy1 - cy2, W_LUMA);\n let edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n let mask = smoothstep(0.0, 2.5, edge);\n col = col * mix(1.0, 0.78, mask * mWetEdge());\n\n let paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n let pigLoad = 1.0 - dot(col, W_LUMA);\n col = col * (1.0 - mGranulation() * pigLoad * (paper - 0.5));\n\n let band = fbm(vec2<f32>(p.x * 1.5, p.y * 0.4));\n col = col * (1.0 + 0.04 * (band - 0.5));\n return col;\n}\n\n// \u2500\u2500 Pastel \u2014 anisotropic stroke noise along the flow + tooth grain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn mediumPastel(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n var col = col0;\n let flow = flowField(p, t);\n let perp = vec2<f32>(-flow.y, flow.x);\n let strokeScale = mStrokeScale();\n let along = dot(p, flow) * strokeScale;\n var across = dot(p, perp) * strokeScale;\n across = across + 0.03 * (vnoise(p * 260.0) - 0.5);\n\n let aniso = mix(1.0, 0.18, mStrokeAnisotropy());\n let stroke = fbm(vec2<f32>(along * aniso, across));\n col = col * mix(1.0, 0.82 + 0.32 * stroke, mStrokeAmount());\n\n let tooth = vnoise(p * 800.0);\n col = col * (1.0 - 0.08 * mStrokeAmount() * (tooth - 0.5));\n return col;\n}\n\n// \u2500\u2500 Crayon \u2014 DRY wax pigment on paper tooth (the tooth-multiply + scumble) \u2500\u2500\nfn mediumCrayon(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let flow = structureTensorField(p, t, flowField(p, t)).xy;\n let ang = atan2(flow.y, flow.x);\n let ca = cos(-ang);\n let sa = sin(-ang);\n let pr = vec2<f32>(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n let aniso = mix(0.45, 0.95, mStrokeAnisotropy());\n let scale = max(mStrokeScale() * 1.6, 180.0);\n\n let t1 = vnoise(vec2<f32>(pr.x * scale * aniso, pr.y * scale));\n let t2 = vnoise(vec2<f32>(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + vec2<f32>(11.0, 11.0));\n let t3 = vnoise(vec2<f32>(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + vec2<f32>(23.0, 23.0));\n let tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n\n let pressure = clamp(mStrokeAmount(), 0.0, 1.0);\n let bite = mix(0.18, 0.46, pressure);\n var result = col0 * (1.0 - bite * (0.5 - tooth));\n\n let scumbleMask = smoothstep(0.42, 0.78, vnoise(vec2<f32>(pr.x * scale * 0.55, pr.y * scale * 0.22) + vec2<f32>(7.0, 7.0)));\n let scumbleCoverage = scumbleMask * pressure * 0.7;\n let paper = col0 * mix(0.80, 0.94, pressure);\n result = mix(paper, result, scumbleCoverage);\n\n let pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n let pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + vec2<f32>(19.0, 19.0)));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + vec2<f32>(17.0, 17.0)),\n hash21(pigmentCell * 2.1 + vec2<f32>(31.0, 31.0)),\n 0.45 + 0.55 * pigmentMask\n );\n\n result = saturate3(result, 1.08);\n return result;\n}\n\n// \u2500\u2500 Kuwahara \u2014 the SOFT anisotropic generalized Kuwahara painterly finish \u2500\u2500\u2500\u2500\n// THE KEYSTONE (Kyprianidis 2010). Aurora is a PROCEDURAL field (no input texture) so the\n// operator runs over sampleBase directly: an elliptical kernel oriented along the structure\n// tensor (elongated along the edge-tangent, narrowed across by the anisotropy), 8 OVERLAPPING\n// sectors via SMOOTH gaussian-angular weights, blended by 1/(1+var^q) (the SOFT criterion \u2192\n// no 8-spoke pinwheel by construction). 4 rings \u00D7 8 angular taps = 32 procedural samples.\nfn mediumKuwahara(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let stf = structureTensorField(p, t, flowField(p, t));\n let tangent = stf.xy;\n let A = stf.z;\n // The studio knob drives the patch radius; clamp into the procedural patch band.\n let radius = clamp(mKuwaharaRadius() + mStrokeScale() * 0.00018, 0.006, 0.024);\n let aniso = mix(1.0, 0.34, clamp(A, 0.0, 1.0));\n let ax = tangent;\n let ay = vec2<f32>(-tangent.y, tangent.x);\n\n // Per-sector weighted sums (8 sectors). WGSL has no runtime-indexed local arrays of\n // vectors as cleanly as GLSL, so unroll the sector accumulators explicitly.\n var mSum: array<vec3<f32>, 8>;\n var sSum: array<vec3<f32>, 8>;\n var wSum: array<f32, 8>;\n for (var i = 0; i < 8; i = i + 1) {\n mSum[i] = vec3<f32>(0.0);\n sSum[i] = vec3<f32>(0.0);\n wSum[i] = 0.0;\n }\n\n let RINGS = 4;\n let TAPS = 8;\n for (var r = 1; r <= RINGS; r = r + 1) {\n let rr = f32(r) / f32(RINGS);\n let radial = 1.0 - 0.6 * rr;\n for (var a = 0; a < TAPS; a = a + 1) {\n let ang = 6.2831853 * f32(a) / f32(TAPS);\n let dirE = ax * cos(ang) + ay * (sin(ang) * aniso);\n let off = dirE * (radius * rr);\n let c = sampleBase(p + off, t);\n for (var k = 0; k < 8; k = k + 1) {\n let sectorAng = 6.2831853 * f32(k) / 8.0;\n var d = ang - sectorAng;\n d = atan2(sin(d), cos(d));\n let sw = exp(-d * d * 2.2) * radial;\n mSum[k] = mSum[k] + c * sw;\n sSum[k] = sSum[k] + c * c * sw;\n wSum[k] = wSum[k] + sw;\n }\n }\n }\n\n let q = max(mKuwaharaQ(), 1.0);\n var accum = vec3<f32>(0.0);\n var accumW = 0.0;\n for (var k = 0; k < 8; k = k + 1) {\n let wk = max(wSum[k], 1e-4);\n let mean = mSum[k] / wk;\n let var3 = max(sSum[k] / wk - mean * mean, vec3<f32>(0.0));\n let variance = dot(var3, W_LUMA);\n let sectorW = 1.0 / (1.0 + pow(variance * 320.0, q));\n accum = accum + mean * sectorW;\n accumW = accumW + sectorW;\n }\n var result = accum / max(accumW, 1e-4);\n\n // Anchor in near-flat zones so the smoothing only bites at edges.\n let center = sampleBase(p, t);\n result = mix(center, result, clamp(0.35 + 0.65 * A, 0.0, 1.0));\n\n // Faint canvas tooth + pigment-saturation so the flattened patches read as oil paint.\n let tooth = vnoise(p * 300.0) - 0.5;\n result = result * (1.0 + tooth * 0.06 * mCanvasGrain());\n result = saturate3(result, 1.04);\n\n return mix(col0, result, clamp(mStrokeAmount(), 0.0, 1.0));\n}\n\n// \u2500\u2500 Medium dispatch (the WGSL twin of the GLSL main() if-ladder) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// uMedium: 0 smooth \u00B7 1 pastel \u00B7 2 watercolor \u00B7 3 oil \u00B7 4 crayon \u00B7 5 vangogh \u00B7\n// 6 oil-pastel \u00B7 7 kuwahara. The cheap peer mediums (pastel/watercolor/crayon) +\n// kuwahara port their own bodies; the oil/vangogh/oil-pastel STROKE cascade renders\n// the anisotropic-Kuwahara PAINTERLY finish here (a real oil-paint read, never the\n// smooth core \u2014 the \"NO FALLBACKS on Safari\" mandate; the full per-dab Starry-Night\n// stroke WGSL is the booked W-AURORA-WGPU-MEDIUMS-STROKES tail).\nfn applyMedium(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let medium = u.ints1.x;\n if (medium == 1) { return mediumPastel(col, p, t); }\n if (medium == 2) { return mediumWatercolor(col, p, t); }\n if (medium == 4) { return mediumCrayon(col, p, t); }\n // oil(3) / vangogh(5) / oil-pastel(6) / kuwahara(7) all render the painterly\n // anisotropic-Kuwahara finish on the WGSL primary (the WebGL2 fallback carries the\n // full per-dab stroke cascade for oil/vangogh/oil-pastel).\n if (medium == 3 || medium == 5 || medium == 6 || medium == 7) {\n return mediumKuwahara(col, p, t);\n }\n return col;\n}\n\n\n// \u2500\u2500 Full-screen triangle vertex stage (the pilot idiom \u2014 no vertex buffer) \u2500\u2500\nstruct VSOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) uv: vec2<f32>,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {\n // NDC corners of the covering triangle (-1,-1), (3,-1), (-1,3).\n var p = vec2<f32>(-1.0, -1.0);\n if (vi == 1u) { p = vec2<f32>(3.0, -1.0); }\n else if (vi == 2u) { p = vec2<f32>(-1.0, 3.0); }\n var out: VSOut;\n out.pos = vec4<f32>(p, 0.0, 1.0);\n // vUv = aPos * 0.5 + 0.5 (the aurora.vert convention).\n out.uv = p * 0.5 + 0.5;\n return out;\n}\n\n// \u2500\u2500 Fragment stage (the main() transcription) \u2500\u2500\n@fragment\nfn fs_main(in: VSOut) -> @location(0) vec4<f32> {\n let t = u.scalars0.x;\n let valueVariance = u.scalars0.z;\n let breathDepth = u.scalars2.w;\n let breathPeriod = u.scalars3.x;\n let saturation = u.scalars1.z;\n let paperGrain = u.scalars1.w;\n let alpha = u.scalars2.x;\n\n let uv = in.uv;\n let pN = uv;\n\n let p_warp = domainWarp(pN, t);\n\n let nf = nucleiField(p_warp, t);\n var col = samplePalette(nf.paletteId);\n col = col * (1.0 + valueVariance * nf.valueMod);\n\n // Breath \u2014 slow global luminance wobble.\n let breath = sin(t * 6.2831 / max(breathPeriod, 1.0));\n col = col * (1.0 + breathDepth * breath * 0.5);\n\n // BC.W-VIZ-AURORA (T4) \u2014 the painterly-medium dispatch. uMedium 0 (smooth) is a\n // no-op pass-through so the DEFAULT smooth config is byte-identical to the prior\n // WGSL primary (the parity capture); uMedium 1-7 render the ported painterly\n // bodies (pastel/watercolor/crayon/kuwahara) or the anisotropic-Kuwahara finish\n // (oil/vangogh/oil-pastel) \u2014 never a silent smooth degrade on Safari 26.\n col = applyMedium(col, pN, t);\n\n // Saturation trim.\n col = saturate3(col, saturation);\n\n // Tonemap + film grain.\n col = aces(col);\n // gl_FragCoord.xy \u2192 the device-pixel position (the WGSL @builtin(position)).\n let fragCoord = in.pos.xy;\n let grain = hash21(fragCoord + t * 17.0);\n col = col + (grain - 0.5) * paperGrain;\n\n col = clamp(col * 0.985 + 0.008, vec3<f32>(0.0), vec3<f32>(1.0));\n\n // MANDATORY OETF \u2014 closes the seam (the whole pipeline above runs in LINEAR).\n col = linearToSrgb(col);\n\n // 1-LSB triangular IGN dither in DISPLAY space, AFTER the OETF.\n col = col + (1.0 / 255.0) * (ign(fragCoord) - 0.5);\n\n // Premultiply on DISPLAY-space color (matches the WebGL2 path: OETF, then premul).\n return vec4<f32>(col * alpha, alpha);\n}\n";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type OklchStop } from "../../../../composables/color";
|
|
2
|
+
/**
|
|
3
|
+
* Walk the anchor ramp to the single OKLCh stop at parameter `t` (0..1) via the
|
|
4
|
+
* shorter-hue arc — the segment is located, then value.js's `mixColors` runs the
|
|
5
|
+
* OKLCh/shorter-hue perceptual mix at the in-segment fraction (the single-point
|
|
6
|
+
* twin of `sampleColorRamp`; ONE color source — value.js). L/C lerp in OKLCh and
|
|
7
|
+
* the hue walks the SHORTER arc, so the warm→cool midpoint keeps its chroma.
|
|
8
|
+
*
|
|
9
|
+
* Deterministic + DOM-free (SSR-safe; mirrors `deriveBlobPalette`).
|
|
10
|
+
*/
|
|
11
|
+
export declare function spectrumAt(stops: readonly OklchStop[], t: number): OklchStop;
|
|
12
|
+
/**
|
|
13
|
+
* Walk a CONCRETE-anchor ramp to `count` evenly-spaced HEX stops — the perceptual
|
|
14
|
+
* (OKLCH/shorter-hue) cold path. The caller (`spectrumStops` in the sync shell) has
|
|
15
|
+
* already screened out `var(--…)` ramps, so every stop here is a CONCRETE CSS color
|
|
16
|
+
* the leaf's `cssToOklch` parses to OKLCh; the walk carries no chroma trough.
|
|
17
|
+
*
|
|
18
|
+
* Each adjacent anchor pair is one `sampleColorRamp` segment (the value.js grid
|
|
19
|
+
* sampler walked SHORTER-hue in OKLCh); the per-segment grids concatenate (shared
|
|
20
|
+
* boundary deduped) into one continuous `count`-stop ramp. The endpoints anchor the
|
|
21
|
+
* input exactly; the hue walks the shorter arc end-to-end, so no segment greys out.
|
|
22
|
+
*
|
|
23
|
+
* @returns one HEX (`#rrggbb`) per stop.
|
|
24
|
+
*/
|
|
25
|
+
export declare function walkConcreteSpectrum(stops: readonly string[], count: number): string[];
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
/** A consumer-supplied callback the COLD path calls once the perceptual walk resolves. */
|
|
2
|
+
export type SpectrumUpgrade = (stops: string[]) => void;
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Produce `samples` evenly-spaced HEX/CSS stops across the anchor ramp — the conic
|
|
5
|
+
* gradient's color stops. CSS `var(--…)` tokens (the LIBRARY-default brand ramp) are
|
|
6
|
+
* passed through UNTOUCHED (they resolve in the cascade at paint — the value.js-free
|
|
7
|
+
* fast path). CONCRETE color anchors return the synchronous interim immediately AND,
|
|
8
|
+
* when `onUpgrade` is supplied, load `./spectrum-walk` and upgrade to the OKLCH/
|
|
9
|
+
* shorter-hue perceptual walk once the dynamic chunk resolves.
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Produce `samples` evenly-spaced HEX stops across the anchor ramp — the conic
|
|
14
|
-
* gradient's color stops, carrying no chroma trough across the sweep. The anchor
|
|
15
|
-
* stops are CSS color strings parsed via the leaf's `cssToOklch`; CSS `var(--…)`
|
|
16
|
-
* tokens (the LIBRARY-default brand ramp) are passed through UNTOUCHED (they
|
|
17
|
-
* resolve in the cascade at paint — the helper only walks CONCRETE color anchors).
|
|
11
|
+
* `spectrumStops` is SYNCHRONOUS (the SFC's `computed` cannot await). The dynamic
|
|
12
|
+
* boundary lives INSIDE it (the value.js branch fires `import()` and returns the
|
|
13
|
+
* synchronous interim), never on the export signature.
|
|
18
14
|
*
|
|
19
|
-
* @returns one
|
|
20
|
-
*
|
|
15
|
+
* @returns one stop per sample — `var()` strings for a token ramp, the raw anchors
|
|
16
|
+
* for the concrete-anchor synchronous interim (upgraded to HEX via `onUpgrade`).
|
|
21
17
|
*/
|
|
22
|
-
export declare function spectrumStops(stops: readonly string[], samples?: number): string[];
|
|
18
|
+
export declare function spectrumStops(stops: readonly string[], samples?: number, onUpgrade?: SpectrumUpgrade): string[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { default as BorderProgress } from "./BorderProgress.vue";
|
|
2
2
|
export type { BorderProgressProps } from "./BorderProgress.vue";
|
|
3
3
|
export type { BorderProgressCoverage, BorderProgressMilestone, BorderProgressMilestoneEvent, } from "./constants";
|
|
4
|
-
export {
|
|
4
|
+
export { spectrumStops, type SpectrumUpgrade } from "./composables/useBorderSpectrum";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CompletionSealShape } from "./constants";
|
|
2
|
+
export interface CompletionSealProps {
|
|
3
|
+
/** The seal glyph — `check` (default) | `ring` | `wordmark`. */
|
|
4
|
+
shape?: CompletionSealShape;
|
|
5
|
+
/**
|
|
6
|
+
* The accessible completion announcement (the `role="status"`/`aria-live` text — a
|
|
7
|
+
* screen reader hears "Speedtest complete", etc.). The gold mark is the visual.
|
|
8
|
+
*/
|
|
9
|
+
label?: string;
|
|
10
|
+
/**
|
|
11
|
+
* The draw trigger — the consumer fires it on completion. When it flips truthy the
|
|
12
|
+
* seal draws ONCE. Default (unset) plays on mount (the seal draws when it appears —
|
|
13
|
+
* the canonical completion moment).
|
|
14
|
+
*/
|
|
15
|
+
play?: boolean;
|
|
16
|
+
/** Pass-through class on the root. */
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
declare const __VLS_export: import("vue").DefineComponent<CompletionSealProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<CompletionSealProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
declare const _default: typeof __VLS_export;
|
|
21
|
+
export default _default;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
export interface UseCompletionSealOptions {
|
|
3
|
+
/**
|
|
4
|
+
* The draw trigger. When it flips truthy the seal draws ONCE (the consumer fires it
|
|
5
|
+
* on completion). A `Ref` or a getter; default plays on mount (`play` unset → the
|
|
6
|
+
* seal draws when it appears, the canonical completion moment).
|
|
7
|
+
*/
|
|
8
|
+
play?: Ref<boolean | undefined> | (() => boolean | undefined);
|
|
9
|
+
/**
|
|
10
|
+
* Honor `prefers-reduced-motion: reduce` — under reduce the seal appears INSTANTLY
|
|
11
|
+
* fully-drawn (no stroke animation, no glint sweep). Default true. (Never forced
|
|
12
|
+
* false — the PRM-safe-by-construction fence.)
|
|
13
|
+
*/
|
|
14
|
+
respectReducedMotion?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface UseCompletionSeal {
|
|
17
|
+
/** The reactive `data-play` seam — bind it to `:data-play` on `.completion-seal`. */
|
|
18
|
+
playing: Readonly<Ref<boolean>>;
|
|
19
|
+
/** True once the seal has drawn (or snapped under PRM) — the static held state. */
|
|
20
|
+
drawn: Readonly<Ref<boolean>>;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the motion is suppressed (PRM) — the SFC snaps `--seal-draw: 100%` and
|
|
23
|
+
* omits the `data-play` arm so the recipe's no-preference gate never fires.
|
|
24
|
+
*/
|
|
25
|
+
reduced: Readonly<Ref<boolean>>;
|
|
26
|
+
/** Imperatively (re-)fire the one-shot draw (a second completion event). */
|
|
27
|
+
draw: () => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The one-shot completion-seal draw. Arms `playing` (the `data-play` seam) for the
|
|
31
|
+
* single draw pass, then settles to the static `drawn` state. Under reduced motion it
|
|
32
|
+
* never arms — `reduced` is true and the seal is the instant fully-drawn mark.
|
|
33
|
+
*
|
|
34
|
+
* Deterministic + SSR-safe (the matchMedia guard) — the recipe owns the paint, this
|
|
35
|
+
* leaf owns the boolean lifecycle.
|
|
36
|
+
*/
|
|
37
|
+
export declare function useCompletionSeal(options?: UseCompletionSealOptions): UseCompletionSeal;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seal glyph axis — the mark the gold stroke draws:
|
|
3
|
+
* - `check` (default) — a checkmark (the canonical "done" garnish).
|
|
4
|
+
* - `ring` — a ring/circle around the completion (a seal around the event).
|
|
5
|
+
* - `wordmark` — a self-contained seal mark (a stylized seal glyph).
|
|
6
|
+
*
|
|
7
|
+
* All three share the ONE gold-draw mechanism (the `--seal-draw` stroke-reveal on the
|
|
8
|
+
* `--seal-ink` gold stroke) — a `shape`-scoped path, NOT a second recipe. The seal is
|
|
9
|
+
* the GOLD-DRAW completion register, distinct from the HandMark hand-voice family.
|
|
10
|
+
*/
|
|
11
|
+
export type CompletionSealShape = "check" | "ring" | "wordmark";
|
|
12
|
+
/** The default seal glyph — the checkmark "done" garnish. */
|
|
13
|
+
export declare const COMPLETION_SEAL_DEFAULT_SHAPE: CompletionSealShape;
|
|
14
|
+
/**
|
|
15
|
+
* The normalized SVG viewBox the glyphs are authored in. The mark `pathLength="100"`
|
|
16
|
+
* normalizes every glyph's draw length so the `stroke-dashoffset` wipe is path-agnostic
|
|
17
|
+
* (the recipe reads `--seal-draw` 0..100% against the normalized length).
|
|
18
|
+
*/
|
|
19
|
+
export declare const COMPLETION_SEAL_VIEWBOX = "0 0 64 64";
|
|
20
|
+
/**
|
|
21
|
+
* The per-shape SVG path/element the gold stroke draws. `check`/`wordmark` are <path>
|
|
22
|
+
* `d`-strings; `ring` is drawn as a <circle> (the SFC branches on shape). Authored in
|
|
23
|
+
* the 64×64 viewBox, `pathLength="100"` normalized at the element.
|
|
24
|
+
*/
|
|
25
|
+
export declare const COMPLETION_SEAL_PATHS: Record<Exclude<CompletionSealShape, "ring">, string>;
|
|
26
|
+
/**
|
|
27
|
+
* The `ring` glyph geometry (a <circle>) — the centre + radius in the 64×64 viewBox.
|
|
28
|
+
* A near-perimeter ring so the seal reads hero-scale.
|
|
29
|
+
*/
|
|
30
|
+
export declare const COMPLETION_SEAL_RING: {
|
|
31
|
+
readonly cx: 32;
|
|
32
|
+
readonly cy: 32;
|
|
33
|
+
readonly r: 24;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The SPRING register the draw rides (SPRING-EASE / W-MOTION-CANON P2 enter-bouncy):
|
|
37
|
+
* - the SETTLE leg (the spatial overshoot) rides `--spring-bouncy` (an ENTER may
|
|
38
|
+
* overshoot on arrival);
|
|
39
|
+
* - the DRAW + GLINT legs ride the `--spring-snappy` CLOCK (the snappy settle duration)
|
|
40
|
+
* so the gold inks crisply, not languidly.
|
|
41
|
+
* The seal mints NO new spring token — it READS the SPRING-EASE register (the no-new-clock
|
|
42
|
+
* fence). These names are the recorded register choice; the actual curves live in the
|
|
43
|
+
* cascade (tokens/scheme-motion.css) and the recipe (completion-seal.css) reads them.
|
|
44
|
+
*/
|
|
45
|
+
export declare const COMPLETION_SEAL_SETTLE_SPRING = "--spring-bouncy";
|
|
46
|
+
export declare const COMPLETION_SEAL_DRAW_SPRING = "--spring-snappy";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { default as CompletionSeal } from "./CompletionSeal.vue";
|
|
2
|
+
export type { CompletionSealProps } from "./CompletionSeal.vue";
|
|
3
|
+
export type { CompletionSealShape } from "./constants";
|
|
4
|
+
export { useCompletionSeal, type UseCompletionSeal, type UseCompletionSealOptions, } from "./composables/useCompletionSeal";
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import type { WebGLCanvasFrame } from "../../../../composables/glass/webgl/useWebGLCanvas";
|
|
2
2
|
import type { OklchStop } from "../../../../composables/color";
|
|
3
3
|
import { type ConcentricConfig } from "../constants";
|
|
4
|
+
import type { RingCenter } from "./ringField";
|
|
4
5
|
export interface ConcentricGLSetupDeps {
|
|
5
6
|
canvas: HTMLCanvasElement;
|
|
6
7
|
config: ConcentricConfig;
|
|
7
8
|
getPalette: () => OklchStop[];
|
|
9
|
+
getCenters: () => RingCenter[];
|
|
8
10
|
shouldContinue: () => boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The per-frame pointer hook — useConcentric advances the shared pointer field +
|
|
13
|
+
* injects the transient cursor center here (the no-own-rAF discipline). ZERO own rAF.
|
|
14
|
+
*/
|
|
15
|
+
onFrame?: (timeSec: number) => void;
|
|
9
16
|
}
|
|
10
17
|
/** Build the `setupGL(gl)` callback the `createGpuSubstrate` WebGL2 path invokes. */
|
|
11
18
|
export declare function createConcentricGLSetup(deps: ConcentricGLSetupDeps): (gl: WebGL2RenderingContext) => WebGLCanvasFrame;
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import type { WebGPUCanvasFrame } from "../../../../composables/glass/webgpu/useWebGPUCanvas";
|
|
2
2
|
import type { OklchStop } from "../../../../composables/color";
|
|
3
3
|
import type { ConcentricConfig } from "../constants";
|
|
4
|
+
import type { RingCenter } from "./ringField";
|
|
4
5
|
export interface ConcentricWGPUSetupDeps {
|
|
5
6
|
canvas: HTMLCanvasElement;
|
|
6
7
|
config: ConcentricConfig;
|
|
7
8
|
/** The resolved (ColorResolver) palette stops as OKLCh — re-read each frame so a live edit reaches the buffer. */
|
|
8
9
|
getPalette: () => OklchStop[];
|
|
10
|
+
/** The render centers (author + the transient pointer center) — re-read each frame. */
|
|
11
|
+
getCenters: () => RingCenter[];
|
|
9
12
|
/** Demand-gate (the renderer's quiescence layer — substrate-agnostic). */
|
|
10
13
|
shouldContinue: () => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The per-frame pointer hook — useConcentric advances the shared `usePointerVelocityField`
|
|
16
|
+
* here (the no-own-rAF discipline: the renderer's frame loop FEEDS `tick(delta)`) and
|
|
17
|
+
* mutates `config.centers` to inject the transient cursor ring-family. ZERO own rAF.
|
|
18
|
+
*/
|
|
19
|
+
onFrame?: (timeSec: number) => void;
|
|
11
20
|
}
|
|
12
21
|
/** Build the concentric `setupWGPU(device, context, format)` callback. */
|
|
13
22
|
export declare function createConcentricWGPUSetup(deps: ConcentricWGPUSetupDeps): (device: GPUDevice, context: GPUCanvasContext, format: GPUTextureFormat) => WebGPUCanvasFrame;
|
|
@@ -4,41 +4,82 @@ export interface Vec2 {
|
|
|
4
4
|
y: number;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* One radial ring component. `amplitude` A_i, `wavelength` λ_i (so k_i = 2π/λ_i),
|
|
8
|
-
* φ_i. `ω_i = √(g·k_i)` is derived from the dispersion relation, not stored (the
|
|
9
|
-
* dispersion the flow field's Gerstner sum uses — the ONE suite dispersion law).
|
|
7
|
+
* One CLEAN radial ring component. `amplitude` A_i, `wavelength` λ_i (so k_i = 2π/λ_i),
|
|
8
|
+
* `phase` φ_i. `ω_i = √(g·k_i)` is derived from the dispersion relation, not stored (the
|
|
9
|
+
* SAME dispersion the flow field's Gerstner sum uses — the ONE suite dispersion law). A
|
|
10
|
+
* family carries 1–2 clean low frequencies, NEVER a turbulence octave ladder (the noise
|
|
11
|
+
* amplifier that read as a blur).
|
|
10
12
|
*/
|
|
11
13
|
export interface RingComponent {
|
|
12
14
|
amplitude: number;
|
|
13
15
|
wavelength: number;
|
|
14
16
|
phase: number;
|
|
15
17
|
}
|
|
16
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* A ring origin in domain space [-1,1]² (multi-center → interference). `rotAlpha` is the
|
|
20
|
+
* per-family tilt of the ellipsoidal frame (radians) — two families at different tilts cross
|
|
21
|
+
* into rich moiré. It reuses the spare `centers[j].w` uniform lane (no byte-layout change).
|
|
22
|
+
*/
|
|
17
23
|
export interface RingCenter {
|
|
18
24
|
x: number;
|
|
19
25
|
y: number;
|
|
20
26
|
/** Per-center weight (a far center contributes a fainter ring family). */
|
|
21
27
|
weight: number;
|
|
28
|
+
/** The per-family ellipsoid tilt (radians); 0 = the un-rotated frame. */
|
|
29
|
+
rotAlpha: number;
|
|
22
30
|
}
|
|
23
31
|
/** Deep-water gravity constant (normalized; scales the dispersion ω — shared with the flow field). */
|
|
24
32
|
export declare const RING_GRAVITY = 9.81;
|
|
25
33
|
/**
|
|
26
|
-
* The ellipsoidal radial distance ‖p − c‖_e = sqrt((dx/a)² + (dy/b)²)
|
|
27
|
-
* (
|
|
28
|
-
*
|
|
34
|
+
* The ellipsoidal radial distance ‖p − c‖_e = sqrt((dx/a)² + (dy/b)²) in the family's
|
|
35
|
+
* ROTATED frame R(−α): rotate (p − c) by −α, then scale each axis by (a,b). The axis ratio
|
|
36
|
+
* tilts the rings into ellipses; the rotation tilts the ellipse so crossing families read as
|
|
37
|
+
* distinct waves. The single radial metric the WGSL shader transcribes.
|
|
29
38
|
*/
|
|
39
|
+
export declare function ellipsoidalRadiusRot(p: Vec2, center: RingCenter, axisA: number, axisB: number): number;
|
|
40
|
+
/** The un-rotated ellipsoidal radius (rotAlpha = 0). Kept for the gate's axis-ratio assert. */
|
|
30
41
|
export declare function ellipsoidalRadius(p: Vec2, center: RingCenter, axisA: number, axisB: number): number;
|
|
31
42
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
43
|
+
* |∇r| of the rotated ellipsoidal radius r = ‖R(−α)(p − c)‖_e. By the chain rule, with
|
|
44
|
+
* u = R(−α)(p − c) and the scaled coordinates (ux/a, uy/b):
|
|
45
|
+
*
|
|
46
|
+
* ∂r/∂p = Rᵀ · ( ux/a² , uy/b² ) / r → |∇r| = sqrt((ux/a²)² + (uy/b²)²) / r
|
|
47
|
+
*
|
|
48
|
+
* (the rotation Rᵀ is orthonormal so it does not change the magnitude). At r→0 the gradient
|
|
49
|
+
* is ill-defined; ε-clamp the denominator. This is the closed-form magnitude the IQ
|
|
50
|
+
* distance-estimation de = |sin(phase)| / (|cos(phase)|·k·|∇r|) divides by.
|
|
51
|
+
*/
|
|
52
|
+
export declare function ellipsoidalGradMag(p: Vec2, center: RingCenter, axisA: number, axisB: number): number;
|
|
53
|
+
/**
|
|
54
|
+
* The radial-Fourier ring-interference value f(p,t) ∈ ℝ — the multi-center weighted sum of
|
|
55
|
+
* CLEAN radial sinusoids. The SINGLE math source the WGSL `fs_main` + the GLSL fallback
|
|
56
|
+
* transcribe. `speed` scales the dispersion ω (the ring-travel speed). Returns the
|
|
57
|
+
* un-normalized field value (the BEAT ENVELOPE the shader tints through the palette).
|
|
36
58
|
*/
|
|
37
59
|
export declare function sampleRingField(p: Vec2, timeSec: number, centers: readonly RingCenter[], rings: readonly RingComponent[], axisA: number, axisB: number, speed: number): number;
|
|
38
60
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
61
|
+
* The IQ gradient-normalized distance-to-isoline INK at p — the per-pixel line strength,
|
|
62
|
+
* the union (brightest-wins `max`) over every (family × ring) crest. For a sinusoid
|
|
63
|
+
* s = sin(phase), de ≈ |s| / |∇s| = |sin| / (|cos|·k·|∇r|) is the analytic (domain-units)
|
|
64
|
+
* distance to the nearest crest everywhere; the SHADER converts it to a constant PIXEL
|
|
65
|
+
* width via fwidth(phase): dePx = de·gradPhase / fwidth(phase) = |sin|/(|cos|·fwidth(phase)),
|
|
66
|
+
* then line = 1 − smoothstep(lineHalfW, lineHalfW+aa, dePx) with lineHalfW/aa in pixels. The
|
|
67
|
+
* JS takes `phaseDeriv` as the per-pixel phase derivative (the fwidth(phase) analogue) so a
|
|
68
|
+
* node round-trip matches the shader exactly; a bare call uses a representative default.
|
|
69
|
+
*
|
|
70
|
+
* Returns { ink, env } — ink ∈ [0,1] the line strength (the alpha/brightness), env the raw
|
|
71
|
+
* beat-envelope value (the hue driver, identical to sampleRingField).
|
|
72
|
+
*/
|
|
73
|
+
export declare function ringIsolineInk(p: Vec2, timeSec: number, centers: readonly RingCenter[], rings: readonly RingComponent[], axisA: number, axisB: number, speed: number, lineHalfW: number, aa: number, phaseDeriv?: number): {
|
|
74
|
+
ink: number;
|
|
75
|
+
env: number;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Build a CLEAN beating ring FAMILY — 1–2 low-frequency radial sinusoids, NOT a turbulence
|
|
79
|
+
* octave ladder. `baseWavelength` ≈ 0.18 sets the ring spacing; a second harmonic at
|
|
80
|
+
* 0.5×base (optional) adds a subtle inner-ring texture without busying the field. The
|
|
81
|
+
* amplitudes are FLAT (energy is not the point — clean lines are), so each crest paints a
|
|
82
|
+
* crisp isoline. Distinct families get slightly different base wavelengths (a small Δλ) so
|
|
83
|
+
* they BEAT into the moiré envelope — the "distinct waves."
|
|
43
84
|
*/
|
|
44
|
-
export declare function
|
|
85
|
+
export declare function buildRingFamily(baseWavelength: number, harmonics?: number, phaseSeed?: number): RingComponent[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OklchStop } from "../../../../composables/color";
|
|
2
|
+
import type { RingCenter } from "./ringField";
|
|
2
3
|
import { type ConcentricConfig } from "../constants";
|
|
3
4
|
export declare const CONCENTRIC_UNIFORM_BYTES: number;
|
|
4
5
|
/** The field-amplitude normalization scale (field → [0,1] for the palette ramp). */
|
|
@@ -10,4 +11,4 @@ export interface ConcentricUniformScratch {
|
|
|
10
11
|
}
|
|
11
12
|
export declare function createConcentricScratch(): ConcentricUniformScratch;
|
|
12
13
|
/** Pack the concentric uniforms in place. */
|
|
13
|
-
export declare function packConcentricUniforms(scratch: ConcentricUniformScratch, config: ConcentricConfig, timeSec: number, aspect: number, palette: OklchStop[]): ConcentricUniformScratch;
|
|
14
|
+
export declare function packConcentricUniforms(scratch: ConcentricUniformScratch, config: ConcentricConfig, timeSec: number, aspect: number, palette: OklchStop[], centersOverride?: readonly RingCenter[]): ConcentricUniformScratch;
|