@shohojdhara/atomix 0.2.3 → 0.2.5
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/README.md +19 -0
- package/dist/atomix.css +1703 -1544
- package/dist/atomix.min.css +4 -4
- package/dist/index.d.ts +1465 -963
- package/dist/index.esm.js +16289 -25908
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +15650 -21780
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/themes/applemix.css +15008 -0
- package/dist/themes/applemix.min.css +72 -0
- package/dist/themes/boomdevs.css +1608 -1450
- package/dist/themes/boomdevs.min.css +5 -5
- package/dist/themes/esrar.css +1702 -1543
- package/dist/themes/esrar.min.css +4 -4
- package/dist/themes/flashtrade.css +15159 -0
- package/dist/themes/flashtrade.min.css +86 -0
- package/dist/themes/mashroom.css +1699 -1540
- package/dist/themes/mashroom.min.css +7 -7
- package/dist/themes/shaj-default.css +1693 -1534
- package/dist/themes/shaj-default.min.css +4 -4
- package/package.json +6 -17
- package/src/components/Accordion/Accordion.stories.tsx +662 -21
- package/src/components/Accordion/Accordion.tsx +21 -14
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +106 -72
- package/src/components/AtomixGlass/AtomixGlass.tsx +529 -1195
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +400 -0
- package/src/components/AtomixGlass/GlassFilter.tsx +156 -0
- package/src/components/AtomixGlass/README.md +124 -2
- package/src/components/AtomixGlass/atomixGLass.old.tsx +1266 -0
- package/src/components/AtomixGlass/glass-utils.ts +263 -0
- package/src/components/AtomixGlass/shader-utils.ts +792 -68
- package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1250 -0
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +5768 -0
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +1065 -0
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +1129 -0
- package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +395 -0
- package/src/components/AtomixGlass/stories/shared-components.tsx +301 -0
- package/src/components/AtomixGlass/utils.ts +3 -3
- package/src/components/Avatar/Avatar.tsx +3 -0
- package/src/components/Avatar/AvatarGroup.tsx +2 -1
- package/src/components/Badge/Badge.stories.tsx +76 -55
- package/src/components/Badge/Badge.tsx +12 -14
- package/src/components/Breadcrumb/Breadcrumb.tsx +23 -4
- package/src/components/Button/Button.stories.tsx +501 -20
- package/src/components/Button/Button.tsx +5 -8
- package/src/components/Callout/Callout.stories.tsx +86 -35
- package/src/components/Callout/Callout.tsx +31 -9
- package/src/components/Card/Card.stories.tsx +565 -2
- package/src/components/Card/Card.tsx +15 -4
- package/src/components/Card/ElevationCard.tsx +2 -0
- package/src/components/Chart/AnimatedChart.tsx +179 -156
- package/src/components/Chart/AreaChart.tsx +123 -12
- package/src/components/Chart/BarChart.tsx +91 -100
- package/src/components/Chart/BaseChart.tsx +80 -0
- package/src/components/Chart/BubbleChart.tsx +114 -290
- package/src/components/Chart/CandlestickChart.tsx +282 -622
- package/src/components/Chart/Chart.stories.tsx +576 -179
- package/src/components/Chart/Chart.tsx +374 -75
- package/src/components/Chart/ChartRenderer.tsx +371 -220
- package/src/components/Chart/ChartToolbar.tsx +372 -61
- package/src/components/Chart/ChartTooltip.tsx +33 -18
- package/src/components/Chart/DonutChart.tsx +172 -254
- package/src/components/Chart/FunnelChart.tsx +169 -240
- package/src/components/Chart/GaugeChart.tsx +224 -392
- package/src/components/Chart/HeatmapChart.tsx +302 -440
- package/src/components/Chart/LineChart.tsx +148 -103
- package/src/components/Chart/MultiAxisChart.tsx +267 -395
- package/src/components/Chart/PieChart.tsx +114 -64
- package/src/components/Chart/RadarChart.tsx +202 -218
- package/src/components/Chart/ScatterChart.tsx +111 -97
- package/src/components/Chart/TreemapChart.tsx +147 -222
- package/src/components/Chart/WaterfallChart.tsx +253 -291
- package/src/components/Chart/index.ts +11 -9
- package/src/components/Chart/types.ts +85 -9
- package/src/components/Chart/utils.ts +66 -0
- package/src/components/ColorModeToggle/ColorModeToggle.tsx +6 -3
- package/src/components/Countdown/Countdown.tsx +4 -0
- package/src/components/DataTable/DataTable.tsx +2 -1
- package/src/components/DatePicker/DatePicker.stories.tsx +689 -12
- package/src/components/DatePicker/DatePicker.tsx +3 -9
- package/src/components/DatePicker/types.ts +5 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +32 -25
- package/src/components/Dropdown/Dropdown.tsx +26 -28
- package/src/components/EdgePanel/EdgePanel.stories.tsx +473 -2
- package/src/components/EdgePanel/EdgePanel.tsx +101 -13
- package/src/components/Footer/Footer.stories.tsx +187 -60
- package/src/components/Footer/Footer.test.tsx +134 -0
- package/src/components/Footer/Footer.tsx +133 -34
- package/src/components/Footer/FooterLink.tsx +1 -1
- package/src/components/Footer/FooterSection.tsx +53 -36
- package/src/components/Footer/FooterSocialLink.tsx +32 -29
- package/src/components/Footer/README.md +82 -3
- package/src/components/Footer/index.ts +1 -1
- package/src/components/Form/Checkbox.stories.tsx +13 -5
- package/src/components/Form/Checkbox.tsx +3 -6
- package/src/components/Form/Form.stories.tsx +10 -3
- package/src/components/Form/Form.tsx +2 -0
- package/src/components/Form/FormGroup.tsx +2 -1
- package/src/components/Form/Input.stories.tsx +12 -11
- package/src/components/Form/Input.tsx +97 -95
- package/src/components/Form/Radio.stories.tsx +22 -7
- package/src/components/Form/Radio.tsx +3 -6
- package/src/components/Form/Select.stories.tsx +21 -6
- package/src/components/Form/Select.tsx +3 -5
- package/src/components/Form/Textarea.stories.tsx +13 -11
- package/src/components/Form/Textarea.tsx +88 -86
- package/src/components/Hero/Hero.stories.tsx +2 -3
- package/src/components/Hero/Hero.tsx +5 -6
- package/src/components/Icon/Icon.tsx +12 -1
- package/src/components/List/List.tsx +2 -1
- package/src/components/List/ListGroup.tsx +2 -1
- package/src/components/Messages/Messages.stories.tsx +113 -0
- package/src/components/Messages/Messages.tsx +52 -9
- package/src/components/Modal/Modal.stories.tsx +48 -32
- package/src/components/Modal/Modal.tsx +19 -24
- package/src/components/Navigation/Menu/MegaMenu.tsx +2 -2
- package/src/components/Navigation/Menu/Menu.tsx +2 -2
- package/src/components/Navigation/Nav/Nav.stories.tsx +469 -0
- package/src/components/Navigation/Nav/Nav.tsx +22 -4
- package/src/components/Navigation/Nav/NavDropdown.tsx +10 -1
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +413 -0
- package/src/components/Navigation/Navbar/Navbar.tsx +70 -29
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +340 -0
- package/src/components/Navigation/SideMenu/SideMenu.tsx +29 -2
- package/src/components/Pagination/Pagination.stories.tsx +13 -6
- package/src/components/Pagination/Pagination.tsx +7 -6
- package/src/components/PhotoViewer/PhotoViewer.tsx +2 -1
- package/src/components/Popover/Popover.stories.tsx +32 -24
- package/src/components/Popover/Popover.tsx +4 -1
- package/src/components/ProductReview/ProductReview.tsx +8 -2
- package/src/components/Progress/Progress.tsx +19 -3
- package/src/components/Rating/Rating.stories.tsx +11 -6
- package/src/components/Rating/Rating.tsx +3 -5
- package/src/components/River/River.tsx +5 -5
- package/src/components/SectionIntro/SectionIntro.tsx +8 -2
- package/src/components/Slider/Slider.stories.tsx +4 -4
- package/src/components/Slider/Slider.tsx +4 -3
- package/src/components/Spinner/Spinner.tsx +19 -3
- package/src/components/Steps/Steps.stories.tsx +5 -4
- package/src/components/Steps/Steps.tsx +8 -5
- package/src/components/Tab/Tab.stories.tsx +4 -3
- package/src/components/Tab/Tab.tsx +8 -6
- package/src/components/Testimonial/Testimonial.tsx +8 -2
- package/src/components/Todo/Todo.tsx +2 -1
- package/src/components/Toggle/Toggle.stories.tsx +5 -4
- package/src/components/Toggle/Toggle.tsx +8 -5
- package/src/components/Tooltip/Tooltip.stories.tsx +40 -30
- package/src/components/Tooltip/Tooltip.tsx +9 -2
- package/src/components/Upload/Upload.stories.tsx +252 -0
- package/src/components/Upload/Upload.tsx +92 -53
- package/src/components/VideoPlayer/VideoPlayer.tsx +3 -1
- package/src/components/index.ts +0 -4
- package/src/layouts/Grid/Grid.stories.tsx +10 -23
- package/src/layouts/Grid/Grid.tsx +20 -1
- package/src/layouts/Grid/GridCol.tsx +76 -48
- package/src/lib/composables/useAtomixGlass.ts +861 -44
- package/src/lib/composables/useBarChart.ts +21 -4
- package/src/lib/composables/useChart.ts +227 -370
- package/src/lib/composables/useChartExport.ts +19 -78
- package/src/lib/composables/useChartToolbar.ts +11 -21
- package/src/lib/composables/useEdgePanel.ts +125 -71
- package/src/lib/composables/useFooter.ts +3 -3
- package/src/lib/composables/useGlassContainer.ts +16 -7
- package/src/lib/composables/useLineChart.ts +11 -2
- package/src/lib/composables/usePieChart.ts +4 -14
- package/src/lib/composables/useRiver.ts +5 -0
- package/src/lib/composables/useSlider.ts +62 -24
- package/src/lib/composables/useVideoPlayer.ts +60 -63
- package/src/lib/constants/components.ts +147 -32
- package/src/lib/types/components.ts +355 -25
- package/src/lib/utils/displacement-generator.ts +55 -49
- package/src/lib/utils/icons.ts +1 -1
- package/src/lib/utils/index.ts +16 -10
- package/src/styles/01-settings/_settings.accordion.scss +19 -19
- package/src/styles/01-settings/_settings.animations.scss +5 -5
- package/src/styles/01-settings/_settings.avatar-group.scss +1 -1
- package/src/styles/01-settings/_settings.avatar.scss +17 -17
- package/src/styles/01-settings/_settings.background.scss +0 -3
- package/src/styles/01-settings/_settings.badge.scss +1 -1
- package/src/styles/01-settings/_settings.breadcrumb.scss +1 -1
- package/src/styles/01-settings/_settings.card.scss +1 -1
- package/src/styles/01-settings/_settings.chart.scss +65 -2
- package/src/styles/01-settings/_settings.dropdown.scss +1 -1
- package/src/styles/01-settings/_settings.edge-panel.scss +1 -1
- package/src/styles/01-settings/_settings.footer.scss +35 -42
- package/src/styles/01-settings/_settings.input.scss +1 -1
- package/src/styles/01-settings/_settings.list.scss +1 -1
- package/src/styles/01-settings/_settings.rating.scss +1 -1
- package/src/styles/01-settings/_settings.tabs.scss +1 -1
- package/src/styles/01-settings/_settings.upload.scss +6 -5
- package/src/styles/02-tools/_tools.animations.scss +4 -5
- package/src/styles/02-tools/_tools.background.scss +1 -13
- package/src/styles/02-tools/_tools.glass.scss +0 -1
- package/src/styles/02-tools/_tools.utility-api.scss +91 -48
- package/src/styles/03-generic/_generic.root.scss +1 -4
- package/src/styles/04-elements/_elements.body.scss +0 -1
- package/src/styles/06-components/_components.atomix-glass.scss +249 -0
- package/src/styles/06-components/_components.badge.scss +8 -23
- package/src/styles/06-components/_components.button.scss +8 -3
- package/src/styles/06-components/_components.callout.scss +10 -5
- package/src/styles/06-components/_components.card.scss +2 -14
- package/src/styles/06-components/_components.chart.scss +969 -1449
- package/src/styles/06-components/_components.dropdown.scss +19 -7
- package/src/styles/06-components/_components.edge-panel.scss +103 -0
- package/src/styles/06-components/_components.footer.scss +166 -85
- package/src/styles/06-components/_components.input.scss +8 -9
- package/src/styles/06-components/_components.list.scss +1 -0
- package/src/styles/06-components/_components.messages.scss +176 -0
- package/src/styles/06-components/_components.modal.scss +16 -4
- package/src/styles/06-components/_components.navbar.scss +12 -1
- package/src/styles/06-components/_components.side-menu.scss +5 -0
- package/src/styles/06-components/_components.skeleton.scss +8 -6
- package/src/styles/06-components/_components.upload.scss +219 -4
- package/src/styles/06-components/old.chart.styles.scss +1 -30
- package/src/styles/99-utilities/_index.scss +1 -0
- package/src/styles/99-utilities/_utilities.glass-fixes.scss +1 -0
- package/src/styles/99-utilities/_utilities.scss +1 -1
- package/src/components/AtomixGlass/AtomixGlass.stories.tsx +0 -3011
- package/src/components/AtomixGlass/AtomixGlassComprehensivePreview.stories.tsx +0 -1369
- package/src/components/Chart/AdvancedChart.tsx +0 -624
- package/src/components/Chart/LineChartNew.tsx +0 -167
- package/src/components/Chart/RealTimeChart.tsx +0 -436
- package/src/components/DatePicker/DatePicker copy.tsx +0 -551
|
@@ -8,44 +8,718 @@ export interface Vec2 {
|
|
|
8
8
|
export interface ShaderOptions {
|
|
9
9
|
width: number;
|
|
10
10
|
height: number;
|
|
11
|
-
fragment: (uv: Vec2,
|
|
11
|
+
fragment: (uv: Vec2, mousePosition?: Vec2) => Vec2;
|
|
12
12
|
mousePosition?: Vec2;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
// Constants
|
|
16
|
+
const MIN_SCALE = 1;
|
|
17
|
+
const EDGE_SMOOTHING_FACTOR = 0.2;
|
|
18
|
+
const EDGE_FADE_PIXELS = 2;
|
|
19
|
+
const NORMALIZATION_CLAMP = { min: 0, max: 255 };
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
// Apple-style liquid glass constants
|
|
22
|
+
const FLUID_VISCOSITY = 0.85;
|
|
23
|
+
const REFRACTION_INTENSITY = 1.2;
|
|
24
|
+
const CHROMATIC_SPREAD = 0.015;
|
|
25
|
+
const TIME_SCALE = 0.0008;
|
|
26
|
+
const DEPTH_LAYERS = 3;
|
|
27
|
+
const ORGANIC_FLOW_SCALE = 12;
|
|
28
|
+
const RADIAL_DISTORTION_STRENGTH = 0.4;
|
|
29
|
+
|
|
30
|
+
// Enhanced error handling constants
|
|
31
|
+
const MAX_CANVAS_DIMENSION = 4096;
|
|
32
|
+
const MIN_CANVAS_DIMENSION = 1;
|
|
33
|
+
const DEFAULT_CANVAS_WIDTH = 256;
|
|
34
|
+
const DEFAULT_CANVAS_HEIGHT = 256;
|
|
35
|
+
|
|
36
|
+
// Utility functions
|
|
37
|
+
const smoothStep = (a: number, b: number, t: number): number => {
|
|
38
|
+
// Add input validation
|
|
39
|
+
if (typeof a !== 'number' || typeof b !== 'number' || typeof t !== 'number') {
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const clamped = Math.max(0, Math.min(1, (t - a) / (b - a)));
|
|
44
|
+
return clamped * clamped * (3 - 2 * clamped);
|
|
45
|
+
};
|
|
23
46
|
|
|
24
|
-
|
|
47
|
+
const calculateLength = (x: number, y: number): number => {
|
|
48
|
+
// Add input validation and error handling
|
|
49
|
+
if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) {
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Prevent potential overflow
|
|
54
|
+
const maxX = Math.max(Math.abs(x), Math.abs(y));
|
|
55
|
+
if (maxX === 0) return 0;
|
|
56
|
+
|
|
57
|
+
const scaledX = x / maxX;
|
|
58
|
+
const scaledY = y / maxX;
|
|
59
|
+
return maxX * Math.sqrt(scaledX * scaledX + scaledY * scaledY);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const roundedRectSDF = (
|
|
25
63
|
x: number,
|
|
26
64
|
y: number,
|
|
27
65
|
width: number,
|
|
28
66
|
height: number,
|
|
29
67
|
radius: number
|
|
30
|
-
): number {
|
|
68
|
+
): number => {
|
|
69
|
+
// Add input validation
|
|
70
|
+
if (typeof x !== 'number' || typeof y !== 'number' ||
|
|
71
|
+
typeof width !== 'number' || typeof height !== 'number' ||
|
|
72
|
+
typeof radius !== 'number') {
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
31
76
|
const qx = Math.abs(x) - width + radius;
|
|
32
77
|
const qy = Math.abs(y) - height + radius;
|
|
33
|
-
return Math.min(Math.max(qx, qy), 0) +
|
|
34
|
-
}
|
|
78
|
+
return Math.min(Math.max(qx, qy), 0) + calculateLength(Math.max(qx, 0), Math.max(qy, 0)) - radius;
|
|
79
|
+
};
|
|
35
80
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
81
|
+
const createTexture = (x: number, y: number): Vec2 => {
|
|
82
|
+
// Add input validation and clamping
|
|
83
|
+
const clampedX = typeof x === 'number' && !isNaN(x) ? Math.max(0, Math.min(1, x)) : 0.5;
|
|
84
|
+
const clampedY = typeof y === 'number' && !isNaN(y) ? Math.max(0, Math.min(1, y)) : 0.5;
|
|
85
|
+
return { x: clampedX, y: clampedY };
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Validation helpers
|
|
89
|
+
const validateVec2 = (vec: Vec2): boolean => {
|
|
90
|
+
return (
|
|
91
|
+
vec && typeof vec.x === 'number' && typeof vec.y === 'number' && !isNaN(vec.x) && !isNaN(vec.y)
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const clampValue = (value: number, min: number, max: number): number => {
|
|
96
|
+
// Add input validation
|
|
97
|
+
if (typeof value !== 'number' || typeof min !== 'number' || typeof max !== 'number') {
|
|
98
|
+
return min;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (isNaN(value)) return min;
|
|
102
|
+
if (isNaN(min)) return 0;
|
|
103
|
+
if (isNaN(max)) return 1;
|
|
104
|
+
|
|
105
|
+
return Math.max(min, Math.min(max, value));
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Advanced easing functions for Apple-style smooth animations
|
|
109
|
+
const easeInOutCubic = (t: number): number => {
|
|
110
|
+
// Add input validation
|
|
111
|
+
if (typeof t !== 'number' || isNaN(t)) {
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
116
|
+
return clampedT < 0.5 ? 4 * clampedT * clampedT * clampedT : 1 - Math.pow(-2 * clampedT + 2, 3) / 2;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const easeOutQuart = (t: number): number => {
|
|
120
|
+
// Add input validation
|
|
121
|
+
if (typeof t !== 'number' || isNaN(t)) {
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
126
|
+
return 1 - Math.pow(1 - clampedT, 4);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Perlin-like noise for organic distortion
|
|
130
|
+
const noise2D = (x: number, y: number): number => {
|
|
131
|
+
// Add input validation
|
|
132
|
+
if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) {
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const X = Math.floor(x) & 255;
|
|
137
|
+
const Y = Math.floor(y) & 255;
|
|
138
|
+
|
|
139
|
+
const xf = x - Math.floor(x);
|
|
140
|
+
const yf = y - Math.floor(y);
|
|
141
|
+
|
|
142
|
+
const u = easeInOutCubic(xf);
|
|
143
|
+
const v = easeInOutCubic(yf);
|
|
144
|
+
|
|
145
|
+
// Simple hash-based pseudo-random
|
|
146
|
+
const hash = (i: number, j: number): number => {
|
|
147
|
+
// Add input validation
|
|
148
|
+
if (typeof i !== 'number' || typeof j !== 'number') {
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const n = i + j * 57;
|
|
153
|
+
// Use a more stable hash function
|
|
154
|
+
const hashed = Math.sin(n * 12.9898 + 78.233) * 43758.5453;
|
|
155
|
+
return hashed - Math.floor(hashed);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const a = hash(X, Y);
|
|
159
|
+
const b = hash(X + 1, Y);
|
|
160
|
+
const c = hash(X, Y + 1);
|
|
161
|
+
const d = hash(X + 1, Y + 1);
|
|
162
|
+
|
|
163
|
+
const x1 = a + u * (b - a);
|
|
164
|
+
const x2 = c + u * (d - c);
|
|
165
|
+
|
|
166
|
+
return x1 + v * (x2 - x1);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Multi-octave noise for complex organic patterns
|
|
170
|
+
const fbm = (x: number, y: number, octaves: number = 4): number => {
|
|
171
|
+
// Add input validation
|
|
172
|
+
if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) {
|
|
173
|
+
return 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Clamp octaves to prevent performance issues
|
|
177
|
+
const clampedOctaves = Math.max(1, Math.min(8, Math.floor(octaves)));
|
|
178
|
+
|
|
179
|
+
let value = 0;
|
|
180
|
+
let amplitude = 0.5;
|
|
181
|
+
let frequency = 1;
|
|
182
|
+
|
|
183
|
+
for (let i = 0; i < clampedOctaves; i++) {
|
|
184
|
+
value += amplitude * noise2D(x * frequency, y * frequency);
|
|
185
|
+
frequency *= 2;
|
|
186
|
+
amplitude *= 0.5;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return value;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Radial distortion for glass-like refraction
|
|
193
|
+
const calculateRadialDistortion = (x: number, y: number, strength: number): Vec2 => {
|
|
194
|
+
// Add input validation
|
|
195
|
+
if (typeof x !== 'number' || typeof y !== 'number' || typeof strength !== 'number' ||
|
|
196
|
+
isNaN(x) || isNaN(y) || isNaN(strength)) {
|
|
197
|
+
return { x: 0, y: 0 };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const distance = calculateLength(x, y);
|
|
201
|
+
const distortion = Math.pow(Math.min(distance, 10), 2) * strength; // Limit distance to prevent extreme values
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
x: x * (1 + distortion),
|
|
205
|
+
y: y * (1 + distortion),
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// Chromatic aberration calculation
|
|
210
|
+
const calculateChromaticOffset = (x: number, y: number, intensity: number): Vec2 => {
|
|
211
|
+
// Add input validation
|
|
212
|
+
if (typeof x !== 'number' || typeof y !== 'number' || typeof intensity !== 'number' ||
|
|
213
|
+
isNaN(x) || isNaN(y) || isNaN(intensity)) {
|
|
214
|
+
return { x: 0, y: 0 };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const distance = calculateLength(x, y);
|
|
218
|
+
// Prevent division by zero and extreme values
|
|
219
|
+
if (distance === 0) {
|
|
220
|
+
return { x: 0, y: 0 };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const angle = Math.atan2(y, x);
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
x: Math.cos(angle) * distance * intensity,
|
|
227
|
+
y: Math.sin(angle) * distance * intensity,
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// Advanced caustic pattern generator for glass refraction
|
|
232
|
+
const calculateCaustics = (x: number, y: number, time: number, intensity: number = 1): number => {
|
|
233
|
+
// Add input validation
|
|
234
|
+
if (typeof x !== 'number' || typeof y !== 'number' || typeof time !== 'number' ||
|
|
235
|
+
typeof intensity !== 'number' || isNaN(x) || isNaN(y) || isNaN(time) || isNaN(intensity)) {
|
|
236
|
+
return 0.5; // Return middle value on error
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const scale = 8;
|
|
240
|
+
const speed = 2;
|
|
241
|
+
|
|
242
|
+
// Multiple caustic layers for realistic light refraction
|
|
243
|
+
const caustic1 = Math.sin(x * scale + time * speed) * Math.cos(y * scale - time * speed);
|
|
244
|
+
const caustic2 =
|
|
245
|
+
Math.sin((x + 0.5) * scale * 1.3 - time * speed * 0.8) *
|
|
246
|
+
Math.cos((y - 0.3) * scale * 1.3 + time * speed * 0.8);
|
|
247
|
+
const caustic3 =
|
|
248
|
+
Math.sin((x - 0.3) * scale * 0.7 + time * speed * 1.2) *
|
|
249
|
+
Math.cos((y + 0.4) * scale * 0.7 - time * speed * 1.2);
|
|
250
|
+
|
|
251
|
+
// Combine caustic layers with varying intensities
|
|
252
|
+
const combined = caustic1 * 0.5 + caustic2 * 0.3 + caustic3 * 0.2;
|
|
253
|
+
|
|
254
|
+
// Apply intensity and normalize to 0-1 range
|
|
255
|
+
return (combined + 1) * 0.5 * intensity;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// Spectral dispersion for rainbow-like chromatic effects
|
|
259
|
+
const calculateSpectralDispersion = (
|
|
260
|
+
x: number,
|
|
261
|
+
y: number,
|
|
262
|
+
angle: number,
|
|
263
|
+
intensity: number
|
|
264
|
+
): { r: Vec2; g: Vec2; b: Vec2 } => {
|
|
265
|
+
// Add input validation
|
|
266
|
+
if (typeof x !== 'number' || typeof y !== 'number' || typeof angle !== 'number' ||
|
|
267
|
+
typeof intensity !== 'number' || isNaN(x) || isNaN(y) || isNaN(angle) || isNaN(intensity)) {
|
|
268
|
+
return {
|
|
269
|
+
r: { x: 0, y: 0 },
|
|
270
|
+
g: { x: 0, y: 0 },
|
|
271
|
+
b: { x: 0, y: 0 }
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const distance = calculateLength(x, y);
|
|
276
|
+
const dispersionStrength = Math.min(distance * intensity, 1); // Limit strength to prevent extreme values
|
|
277
|
+
|
|
278
|
+
// Different wavelengths refract at different angles (like a prism)
|
|
279
|
+
const redOffset = dispersionStrength * 0.8;
|
|
280
|
+
const greenOffset = dispersionStrength * 1.0;
|
|
281
|
+
const blueOffset = dispersionStrength * 1.2;
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
r: {
|
|
285
|
+
x: Math.cos(angle) * redOffset,
|
|
286
|
+
y: Math.sin(angle) * redOffset,
|
|
287
|
+
},
|
|
288
|
+
g: {
|
|
289
|
+
x: Math.cos(angle) * greenOffset,
|
|
290
|
+
y: Math.sin(angle) * greenOffset,
|
|
291
|
+
},
|
|
292
|
+
b: {
|
|
293
|
+
x: Math.cos(angle) * blueOffset,
|
|
294
|
+
y: Math.sin(angle) * blueOffset,
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// Parallax depth offset calculation for multi-layer effects
|
|
300
|
+
const calculateParallaxOffset = (
|
|
301
|
+
x: number,
|
|
302
|
+
y: number,
|
|
303
|
+
depth: number,
|
|
304
|
+
mouseX: number = 0,
|
|
305
|
+
mouseY: number = 0
|
|
306
|
+
): Vec2 => {
|
|
307
|
+
// Add input validation
|
|
308
|
+
if (typeof x !== 'number' || typeof y !== 'number' || typeof depth !== 'number' ||
|
|
309
|
+
typeof mouseX !== 'number' || typeof mouseY !== 'number' ||
|
|
310
|
+
isNaN(x) || isNaN(y) || isNaN(depth) || isNaN(mouseX) || isNaN(mouseY)) {
|
|
311
|
+
return { x: 0, y: 0 };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const parallaxStrength = Math.min(0.02 * depth, 0.1); // Limit strength to prevent extreme values
|
|
315
|
+
|
|
316
|
+
// Calculate offset based on view angle (simulated by mouse position)
|
|
317
|
+
const offsetX = (x - mouseX) * parallaxStrength;
|
|
318
|
+
const offsetY = (y - mouseY) * parallaxStrength;
|
|
319
|
+
|
|
320
|
+
return { x: offsetX, y: offsetY };
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
// Volumetric density for depth perception and scattering
|
|
324
|
+
const calculateVolumetricDensity = (x: number, y: number, depth: number, time: number): number => {
|
|
325
|
+
// Add input validation
|
|
326
|
+
if (typeof x !== 'number' || typeof y !== 'number' || typeof depth !== 'number' ||
|
|
327
|
+
typeof time !== 'number' || isNaN(x) || isNaN(y) || isNaN(depth) || isNaN(time)) {
|
|
328
|
+
return 0.5; // Return middle value on error
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const noiseValue = fbm(x * 5 + time * 0.5, y * 5 - time * 0.5, 3);
|
|
332
|
+
const depthFalloff = Math.exp(-Math.max(0, depth) * 2); // Ensure depth is not negative
|
|
333
|
+
|
|
334
|
+
return noiseValue * depthFalloff * 0.5 + 0.5;
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// Advanced turbulence for organic glass distortion
|
|
338
|
+
const calculateTurbulence = (x: number, y: number, time: number, octaves: number = 5): number => {
|
|
339
|
+
// Add input validation
|
|
340
|
+
if (typeof x !== 'number' || typeof y !== 'number' || typeof time !== 'number' ||
|
|
341
|
+
typeof octaves !== 'number' || isNaN(x) || isNaN(y) || isNaN(time) || isNaN(octaves)) {
|
|
342
|
+
return 0;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Clamp octaves to prevent performance issues
|
|
346
|
+
const clampedOctaves = Math.max(1, Math.min(8, Math.floor(octaves)));
|
|
347
|
+
|
|
348
|
+
let turbulence = 0;
|
|
349
|
+
let amplitude = 1;
|
|
350
|
+
let frequency = 1;
|
|
351
|
+
|
|
352
|
+
for (let i = 0; i < clampedOctaves; i++) {
|
|
353
|
+
const noiseVal = Math.abs(noise2D(x * frequency + time, y * frequency - time));
|
|
354
|
+
turbulence += noiseVal * amplitude;
|
|
355
|
+
frequency *= 2;
|
|
356
|
+
amplitude *= 0.5;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return turbulence;
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// Micro-surface detail for high-quality glass texture
|
|
363
|
+
const calculateMicroSurface = (x: number, y: number, time: number): number => {
|
|
364
|
+
// Add input validation
|
|
365
|
+
if (typeof x !== 'number' || typeof y !== 'number' || typeof time !== 'number' ||
|
|
366
|
+
isNaN(x) || isNaN(y) || isNaN(time)) {
|
|
367
|
+
return 0.5; // Return middle value on error
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const highFreqNoise = fbm(x * 40 + time * 0.3, y * 40 - time * 0.3, 6);
|
|
371
|
+
const microDetail = fbm(x * 80, y * 80, 4);
|
|
372
|
+
|
|
373
|
+
return (highFreqNoise * 0.7 + microDetail * 0.3) * 0.5;
|
|
374
|
+
};
|
|
39
375
|
|
|
40
376
|
// Shader fragment functions for different effects
|
|
41
377
|
export const fragmentShaders = {
|
|
42
|
-
liquidGlass: (uv: Vec2): Vec2 => {
|
|
378
|
+
liquidGlass: (uv: Vec2, mousePosition?: Vec2): Vec2 => {
|
|
379
|
+
if (!validateVec2(uv)) {
|
|
380
|
+
return { x: 0.5, y: 0.5 };
|
|
381
|
+
}
|
|
382
|
+
|
|
43
383
|
const ix = uv.x - 0.5;
|
|
44
384
|
const iy = uv.y - 0.5;
|
|
45
|
-
const
|
|
46
|
-
|
|
385
|
+
const time = Date.now() * TIME_SCALE;
|
|
386
|
+
|
|
387
|
+
// Enhanced distortion with mouse influence
|
|
388
|
+
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
389
|
+
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
390
|
+
const mouseDistance = calculateLength(mouseX, mouseY);
|
|
391
|
+
const mouseFalloff = easeOutQuart(1 - Math.min(mouseDistance * 2, 1));
|
|
392
|
+
|
|
393
|
+
// Multi-layered organic distortion using FBM noise
|
|
394
|
+
const noiseScale = ORGANIC_FLOW_SCALE;
|
|
395
|
+
const organicFlow =
|
|
396
|
+
fbm(
|
|
397
|
+
(ix + mouseX * 0.5) * noiseScale + time,
|
|
398
|
+
(iy + mouseY * 0.5) * noiseScale + time * 0.7,
|
|
399
|
+
3
|
|
400
|
+
) - 0.5;
|
|
401
|
+
|
|
402
|
+
// Enhanced Apple-like liquid distortion with rounded rect SDF
|
|
403
|
+
const distanceToEdge = roundedRectSDF(ix, iy, 0.4, 0.3, 0.35);
|
|
404
|
+
const baseDisplacement = smoothStep(0.8, 0, distanceToEdge - 0.05);
|
|
405
|
+
|
|
406
|
+
// Radial distortion for glass-like refraction
|
|
407
|
+
const radialDist = calculateRadialDistortion(ix, iy, RADIAL_DISTORTION_STRENGTH * 0.1);
|
|
408
|
+
const refractionX = (radialDist.x - ix) * REFRACTION_INTENSITY * baseDisplacement;
|
|
409
|
+
const refractionY = (radialDist.y - iy) * REFRACTION_INTENSITY * baseDisplacement;
|
|
410
|
+
|
|
411
|
+
// Apple-style liquid flow with time-based animation
|
|
412
|
+
const flowX = Math.sin((ix + mouseX * 2) * 8 + time * 2) * 0.018;
|
|
413
|
+
const flowY = Math.cos((iy + mouseY * 2) * 8 + time * 1.5) * 0.018;
|
|
414
|
+
|
|
415
|
+
// Multi-directional ripples with mouse influence
|
|
416
|
+
const ripple1 = Math.sin((ix - mouseX) * 12 + (iy - mouseY) * 12 + time * 3) * 0.015;
|
|
417
|
+
const ripple2 = Math.cos((ix + mouseX) * 10 - (iy - mouseY) * 10 - time * 2) * 0.012;
|
|
418
|
+
const rippleEffect = (ripple1 + ripple2) * mouseFalloff * mouseDistance;
|
|
419
|
+
|
|
420
|
+
// Depth-based layering for premium glass effect
|
|
421
|
+
const depthLayer1 = Math.sin(ix * 15 + time) * Math.cos(iy * 15 - time) * 0.008;
|
|
422
|
+
const depthLayer2 = Math.sin(ix * 20 - time * 0.5) * Math.cos(iy * 20 + time * 0.5) * 0.006;
|
|
423
|
+
const depthEffect = (depthLayer1 + depthLayer2) * baseDisplacement;
|
|
424
|
+
|
|
425
|
+
// Combine all distortion effects with fluid viscosity
|
|
426
|
+
const liquidFlow = (flowX + flowY + organicFlow * 0.025) * FLUID_VISCOSITY;
|
|
427
|
+
const totalDistortionX = refractionX + liquidFlow + rippleEffect + depthEffect;
|
|
428
|
+
const totalDistortionY = refractionY + liquidFlow * 0.8 + rippleEffect * 0.9 + depthEffect;
|
|
429
|
+
|
|
430
|
+
// Apply chromatic aberration for premium glass look
|
|
431
|
+
const chromaticOffset = calculateChromaticOffset(ix, iy, CHROMATIC_SPREAD * baseDisplacement);
|
|
432
|
+
|
|
433
|
+
const displacement = baseDisplacement * 1.15;
|
|
47
434
|
const scaled = smoothStep(0, 1, displacement);
|
|
48
|
-
|
|
435
|
+
|
|
436
|
+
// Final position with all effects combined
|
|
437
|
+
const finalX = ix + totalDistortionX + chromaticOffset.x * 0.5;
|
|
438
|
+
const finalY = iy + totalDistortionY + chromaticOffset.y * 0.5;
|
|
439
|
+
|
|
440
|
+
return createTexture(
|
|
441
|
+
clampValue(finalX * scaled + 0.5, 0, 1),
|
|
442
|
+
clampValue(finalY * scaled + 0.5, 0, 1)
|
|
443
|
+
);
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
// Premium Apple-style fluid glass with enhanced organic flow
|
|
447
|
+
appleFluid: (uv: Vec2, mousePosition?: Vec2): Vec2 => {
|
|
448
|
+
if (!validateVec2(uv)) {
|
|
449
|
+
return { x: 0.5, y: 0.5 };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const ix = uv.x - 0.5;
|
|
453
|
+
const iy = uv.y - 0.5;
|
|
454
|
+
const time = Date.now() * TIME_SCALE * 0.6;
|
|
455
|
+
|
|
456
|
+
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
457
|
+
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
458
|
+
const mouseDistance = calculateLength(mouseX, mouseY);
|
|
459
|
+
const mouseFalloff = easeOutQuart(1 - Math.min(mouseDistance * 1.5, 1));
|
|
460
|
+
|
|
461
|
+
// High-quality organic distortion with multiple octaves
|
|
462
|
+
const organicX = fbm((ix + mouseX * 0.3) * 10 + time, (iy + mouseY * 0.3) * 10, 5) - 0.5;
|
|
463
|
+
const organicY = fbm((ix - mouseX * 0.3) * 10, (iy - mouseY * 0.3) * 10 + time * 0.8, 5) - 0.5;
|
|
464
|
+
|
|
465
|
+
// Smooth rounded rectangle mask
|
|
466
|
+
const distanceToEdge = roundedRectSDF(ix, iy, 0.42, 0.32, 0.38);
|
|
467
|
+
const mask = smoothStep(0.85, -0.1, distanceToEdge);
|
|
468
|
+
|
|
469
|
+
// Fluid dynamics simulation
|
|
470
|
+
const fluidVelocityX = Math.sin(ix * 6 + time * 2) * Math.cos(iy * 4 - time) * 0.025;
|
|
471
|
+
const fluidVelocityY = Math.cos(ix * 4 - time) * Math.sin(iy * 6 + time * 2) * 0.025;
|
|
472
|
+
|
|
473
|
+
// Mouse-driven vortex effect
|
|
474
|
+
const vortexAngle = Math.atan2(iy - mouseY, ix - mouseX);
|
|
475
|
+
const vortexStrength = mouseFalloff * mouseDistance * 0.08;
|
|
476
|
+
const vortexX = Math.cos(vortexAngle + time) * vortexStrength;
|
|
477
|
+
const vortexY = Math.sin(vortexAngle + time) * vortexStrength;
|
|
478
|
+
|
|
479
|
+
// Combine effects with premium smoothing
|
|
480
|
+
const totalX = ix + (organicX * 0.035 + fluidVelocityX + vortexX) * mask;
|
|
481
|
+
const totalY = iy + (organicY * 0.035 + fluidVelocityY + vortexY) * mask;
|
|
482
|
+
|
|
483
|
+
return createTexture(clampValue(totalX + 0.5, 0, 1), clampValue(totalY + 0.5, 0, 1));
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
// High-end glass with advanced refraction and depth
|
|
487
|
+
premiumGlass: (uv: Vec2, mousePosition?: Vec2): Vec2 => {
|
|
488
|
+
if (!validateVec2(uv)) {
|
|
489
|
+
return { x: 0.5, y: 0.5 };
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const ix = uv.x - 0.5;
|
|
493
|
+
const iy = uv.y - 0.5;
|
|
494
|
+
const time = Date.now() * TIME_SCALE * 0.4;
|
|
495
|
+
|
|
496
|
+
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
497
|
+
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
498
|
+
const mouseDistance = calculateLength(mouseX, mouseY);
|
|
499
|
+
|
|
500
|
+
// Advanced radial distortion with depth
|
|
501
|
+
const centerDistance = calculateLength(ix, iy);
|
|
502
|
+
const refractionStrength = Math.pow(Math.min(centerDistance, 1), 1.5) * 0.3; // Limit centerDistance
|
|
503
|
+
const refractionAngle = Math.atan2(iy, ix);
|
|
504
|
+
|
|
505
|
+
// Multi-layer depth effect
|
|
506
|
+
let depthX = 0;
|
|
507
|
+
let depthY = 0;
|
|
508
|
+
for (let layer = 0; layer < DEPTH_LAYERS; layer++) {
|
|
509
|
+
const layerScale = (layer + 1) * 5;
|
|
510
|
+
const layerTime = time * (1 + layer * 0.3);
|
|
511
|
+
const layerStrength = 0.01 / (layer + 1);
|
|
512
|
+
|
|
513
|
+
depthX += Math.sin(ix * layerScale + layerTime) * layerStrength;
|
|
514
|
+
depthY += Math.cos(iy * layerScale - layerTime) * layerStrength;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Glass refraction with mouse influence
|
|
518
|
+
const refractionX = Math.cos(refractionAngle) * refractionStrength * (1 + mouseDistance * 0.5);
|
|
519
|
+
const refractionY = Math.sin(refractionAngle) * refractionStrength * (1 + mouseDistance * 0.5);
|
|
520
|
+
|
|
521
|
+
// Subtle organic movement
|
|
522
|
+
const organicNoise = fbm(ix * 8 + time, iy * 8 - time, 2) - 0.5;
|
|
523
|
+
|
|
524
|
+
// Edge-aware distortion
|
|
525
|
+
const distanceToEdge = roundedRectSDF(ix, iy, 0.43, 0.33, 0.36);
|
|
526
|
+
const edgeMask = smoothStep(0.9, -0.05, distanceToEdge);
|
|
527
|
+
|
|
528
|
+
const finalX = ix + (refractionX + depthX + organicNoise * 0.015) * edgeMask;
|
|
529
|
+
const finalY = iy + (refractionY + depthY + organicNoise * 0.015) * edgeMask;
|
|
530
|
+
|
|
531
|
+
return createTexture(clampValue(finalX + 0.5, 0, 1), clampValue(finalY + 0.5, 0, 1));
|
|
532
|
+
},
|
|
533
|
+
|
|
534
|
+
// Metallic liquid effect with shimmer
|
|
535
|
+
liquidMetal: (uv: Vec2, mousePosition?: Vec2): Vec2 => {
|
|
536
|
+
if (!validateVec2(uv)) {
|
|
537
|
+
return { x: 0.5, y: 0.5 };
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const ix = uv.x - 0.5;
|
|
541
|
+
const iy = uv.y - 0.5;
|
|
542
|
+
const time = Date.now() * TIME_SCALE * 1.2;
|
|
543
|
+
|
|
544
|
+
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
545
|
+
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
546
|
+
|
|
547
|
+
// Metallic wave patterns
|
|
548
|
+
const wave1 = Math.sin(ix * 20 + time * 4) * Math.cos(iy * 15 - time * 3) * 0.02;
|
|
549
|
+
const wave2 = Math.cos(ix * 15 - time * 2) * Math.sin(iy * 20 + time * 5) * 0.015;
|
|
550
|
+
|
|
551
|
+
// Shimmer effect
|
|
552
|
+
const shimmer = fbm(ix * 25 + time * 2, iy * 25 - time * 2, 4) * 0.025;
|
|
553
|
+
|
|
554
|
+
// Mouse interaction with metallic flow
|
|
555
|
+
const flowAngle = Math.atan2(iy - mouseY, ix - mouseX);
|
|
556
|
+
const flowDistance = calculateLength(ix - mouseX, iy - mouseY);
|
|
557
|
+
const flowEffect =
|
|
558
|
+
Math.sin(flowDistance * 15 - time * 6) *
|
|
559
|
+
0.02 *
|
|
560
|
+
easeOutQuart(1 - Math.min(flowDistance * 2, 1));
|
|
561
|
+
|
|
562
|
+
const distanceToEdge = roundedRectSDF(ix, iy, 0.41, 0.31, 0.37);
|
|
563
|
+
const mask = smoothStep(0.88, -0.08, distanceToEdge);
|
|
564
|
+
|
|
565
|
+
const totalX = ix + (wave1 + shimmer + Math.cos(flowAngle) * flowEffect) * mask;
|
|
566
|
+
const totalY = iy + (wave2 + shimmer * 0.8 + Math.sin(flowAngle) * flowEffect) * mask;
|
|
567
|
+
|
|
568
|
+
return createTexture(clampValue(totalX + 0.5, 0, 1), clampValue(totalY + 0.5, 0, 1));
|
|
569
|
+
},
|
|
570
|
+
|
|
571
|
+
// basiBasi - Expert Premium Glass Shader
|
|
572
|
+
// The most advanced shader with caustics, spectral dispersion, parallax depth, and volumetric effects
|
|
573
|
+
basiBasi: (uv: Vec2, mousePosition?: Vec2): Vec2 => {
|
|
574
|
+
if (!validateVec2(uv)) {
|
|
575
|
+
return { x: 0.5, y: 0.5 };
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const ix = uv.x - 0.5;
|
|
579
|
+
const iy = uv.y - 0.5;
|
|
580
|
+
const time = Date.now() * TIME_SCALE * 0.5;
|
|
581
|
+
|
|
582
|
+
// Mouse interaction setup
|
|
583
|
+
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
584
|
+
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
585
|
+
const mouseDistance = calculateLength(mouseX, mouseY);
|
|
586
|
+
const mouseFalloff = easeOutQuart(1 - Math.min(mouseDistance * 1.2, 1));
|
|
587
|
+
|
|
588
|
+
// === CAUSTIC LIGHT PATTERNS ===
|
|
589
|
+
// Simulate light refraction through glass creating caustic patterns
|
|
590
|
+
const causticIntensity = calculateCaustics(ix, iy, time, 0.8);
|
|
591
|
+
const causticDistortion = (causticIntensity - 0.5) * 0.02;
|
|
592
|
+
|
|
593
|
+
// === SPECTRAL DISPERSION ===
|
|
594
|
+
// Rainbow-like chromatic effects from light splitting
|
|
595
|
+
const refractionAngle = Math.atan2(iy, ix);
|
|
596
|
+
const spectralDispersion = calculateSpectralDispersion(ix, iy, refractionAngle, 0.025);
|
|
597
|
+
|
|
598
|
+
// Average the RGB channels for displacement (simulating prism effect)
|
|
599
|
+
const spectralX =
|
|
600
|
+
(spectralDispersion.r.x + spectralDispersion.g.x + spectralDispersion.b.x) / 3;
|
|
601
|
+
const spectralY =
|
|
602
|
+
(spectralDispersion.r.y + spectralDispersion.g.y + spectralDispersion.b.y) / 3;
|
|
603
|
+
|
|
604
|
+
// === MULTI-LAYER PARALLAX DEPTH ===
|
|
605
|
+
// Create depth perception with 7 layers
|
|
606
|
+
let parallaxX = 0;
|
|
607
|
+
let parallaxY = 0;
|
|
608
|
+
const numParallaxLayers = 7;
|
|
609
|
+
|
|
610
|
+
for (let layer = 0; layer < numParallaxLayers; layer++) {
|
|
611
|
+
const depth = (layer + 1) / numParallaxLayers;
|
|
612
|
+
const parallaxOffset = calculateParallaxOffset(ix, iy, depth, mouseX, mouseY);
|
|
613
|
+
|
|
614
|
+
// Layer-specific distortion
|
|
615
|
+
const layerNoise =
|
|
616
|
+
fbm(
|
|
617
|
+
(ix + parallaxOffset.x) * (8 + layer * 2) + time * (0.5 + layer * 0.1),
|
|
618
|
+
(iy + parallaxOffset.y) * (8 + layer * 2) - time * (0.5 + layer * 0.1),
|
|
619
|
+
3
|
|
620
|
+
) - 0.5;
|
|
621
|
+
|
|
622
|
+
const layerWeight = 1 / (layer + 1);
|
|
623
|
+
parallaxX += (parallaxOffset.x + layerNoise * 0.01) * layerWeight;
|
|
624
|
+
parallaxY += (parallaxOffset.y + layerNoise * 0.01) * layerWeight;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Normalize parallax effect
|
|
628
|
+
parallaxX /= numParallaxLayers;
|
|
629
|
+
parallaxY /= numParallaxLayers;
|
|
630
|
+
|
|
631
|
+
// === VOLUMETRIC SCATTERING ===
|
|
632
|
+
// Simulate light scattering through glass volume
|
|
633
|
+
const volumetricDensity = calculateVolumetricDensity(ix, iy, 0.5, time);
|
|
634
|
+
const scatteringX = Math.cos(refractionAngle) * volumetricDensity * 0.015;
|
|
635
|
+
const scatteringY = Math.sin(refractionAngle) * volumetricDensity * 0.015;
|
|
636
|
+
|
|
637
|
+
// === ADVANCED TURBULENCE ===
|
|
638
|
+
// High-quality organic distortion
|
|
639
|
+
const turbulence = calculateTurbulence(ix * 6, iy * 6, time, 6);
|
|
640
|
+
const turbulenceX = Math.cos(turbulence * Math.PI * 2) * 0.012;
|
|
641
|
+
const turbulenceY = Math.sin(turbulence * Math.PI * 2) * 0.012;
|
|
642
|
+
|
|
643
|
+
// === MICRO-SURFACE DETAIL ===
|
|
644
|
+
// Fine glass texture details
|
|
645
|
+
const microSurface = calculateMicroSurface(ix, iy, time);
|
|
646
|
+
const microDetailX = (microSurface - 0.5) * 0.008;
|
|
647
|
+
const microDetailY = (microSurface - 0.5) * 0.008;
|
|
648
|
+
|
|
649
|
+
// === ADVANCED RADIAL REFRACTION ===
|
|
650
|
+
// Enhanced glass-like refraction with depth
|
|
651
|
+
const centerDistance = calculateLength(ix, iy);
|
|
652
|
+
const refractionStrength = Math.pow(Math.min(centerDistance, 1), 1.8) * 0.35; // Limit centerDistance
|
|
653
|
+
const dynamicRefraction = refractionStrength * (1 + mouseFalloff * mouseDistance * 0.8);
|
|
654
|
+
|
|
655
|
+
const refractionX = Math.cos(refractionAngle) * dynamicRefraction;
|
|
656
|
+
const refractionY = Math.sin(refractionAngle) * dynamicRefraction;
|
|
657
|
+
|
|
658
|
+
// === MOUSE-DRIVEN VORTEX ===
|
|
659
|
+
// Interactive swirl effect
|
|
660
|
+
const vortexAngle = Math.atan2(iy - mouseY, ix - mouseX);
|
|
661
|
+
const vortexDistance = calculateLength(ix - mouseX, iy - mouseY);
|
|
662
|
+
const vortexStrength = mouseFalloff * Math.sin(vortexDistance * 10 - time * 3) * 0.025;
|
|
663
|
+
const vortexX = Math.cos(vortexAngle + time * 2) * vortexStrength;
|
|
664
|
+
const vortexY = Math.sin(vortexAngle + time * 2) * vortexStrength;
|
|
665
|
+
|
|
666
|
+
// === FLUID DYNAMICS ===
|
|
667
|
+
// Liquid-like flow patterns
|
|
668
|
+
const fluidX =
|
|
669
|
+
Math.sin(ix * 10 + mouseX * 5 + time * 2.5) * Math.cos(iy * 8 - time * 2) * 0.018;
|
|
670
|
+
const fluidY =
|
|
671
|
+
Math.cos(ix * 8 - time * 2) * Math.sin(iy * 10 + mouseY * 5 + time * 2.5) * 0.018;
|
|
672
|
+
|
|
673
|
+
// === RIPPLE EFFECTS ===
|
|
674
|
+
// Multi-directional wave propagation
|
|
675
|
+
const ripple1 = Math.sin(Math.min(centerDistance, 10) * 15 - time * 4) * 0.012; // Limit centerDistance
|
|
676
|
+
const ripple2 = Math.cos(Math.min(centerDistance, 10) * 20 + time * 3) * 0.008; // Limit centerDistance
|
|
677
|
+
const rippleEffect = (ripple1 + ripple2) * mouseFalloff;
|
|
678
|
+
const rippleX = Math.cos(refractionAngle) * rippleEffect;
|
|
679
|
+
const rippleY = Math.sin(refractionAngle) * rippleEffect;
|
|
680
|
+
|
|
681
|
+
// === EDGE-AWARE MASKING ===
|
|
682
|
+
// Smooth rounded rectangle with premium edge handling
|
|
683
|
+
const distanceToEdge = roundedRectSDF(ix, iy, 0.44, 0.34, 0.39);
|
|
684
|
+
const edgeMask = smoothStep(0.92, -0.12, distanceToEdge);
|
|
685
|
+
const edgeSoftness = smoothStep(0.85, 0.1, distanceToEdge);
|
|
686
|
+
|
|
687
|
+
// === COMBINE ALL EFFECTS ===
|
|
688
|
+
// Layer all distortions with proper weighting
|
|
689
|
+
const totalDistortionX =
|
|
690
|
+
(refractionX * 1.2 +
|
|
691
|
+
spectralX * 0.8 +
|
|
692
|
+
parallaxX * 1.5 +
|
|
693
|
+
scatteringX * 0.9 +
|
|
694
|
+
turbulenceX * 1.0 +
|
|
695
|
+
microDetailX * 0.6 +
|
|
696
|
+
vortexX * 1.3 +
|
|
697
|
+
fluidX * 1.1 +
|
|
698
|
+
rippleX * 0.7 +
|
|
699
|
+
causticDistortion) *
|
|
700
|
+
edgeMask *
|
|
701
|
+
edgeSoftness;
|
|
702
|
+
|
|
703
|
+
const totalDistortionY =
|
|
704
|
+
(refractionY * 1.2 +
|
|
705
|
+
spectralY * 0.8 +
|
|
706
|
+
parallaxY * 1.5 +
|
|
707
|
+
scatteringY * 0.9 +
|
|
708
|
+
turbulenceY * 1.0 +
|
|
709
|
+
microDetailY * 0.6 +
|
|
710
|
+
vortexY * 1.3 +
|
|
711
|
+
fluidY * 1.1 +
|
|
712
|
+
rippleY * 0.7 +
|
|
713
|
+
causticDistortion * 0.8) *
|
|
714
|
+
edgeMask *
|
|
715
|
+
edgeSoftness;
|
|
716
|
+
|
|
717
|
+
// === FINAL POSITION ===
|
|
718
|
+
// Apply all distortions with smooth falloff
|
|
719
|
+
const finalX = ix + totalDistortionX * 0.85; // Scale down for subtlety
|
|
720
|
+
const finalY = iy + totalDistortionY * 0.85;
|
|
721
|
+
|
|
722
|
+
return createTexture(clampValue(finalX + 0.5, 0, 1), clampValue(finalY + 0.5, 0, 1));
|
|
49
723
|
},
|
|
50
724
|
};
|
|
51
725
|
|
|
@@ -57,84 +731,134 @@ export class ShaderDisplacementGenerator {
|
|
|
57
731
|
private canvasDPI = 1;
|
|
58
732
|
|
|
59
733
|
constructor(private options: ShaderOptions) {
|
|
734
|
+
if (!this.validateOptions(options)) {
|
|
735
|
+
throw new Error('Invalid shader options provided');
|
|
736
|
+
}
|
|
737
|
+
|
|
60
738
|
this.canvas = document.createElement('canvas');
|
|
61
|
-
|
|
62
|
-
this.canvas.
|
|
739
|
+
// Enhanced validation for canvas dimensions
|
|
740
|
+
this.canvas.width = Math.max(MIN_CANVAS_DIMENSION,
|
|
741
|
+
Math.min(MAX_CANVAS_DIMENSION,
|
|
742
|
+
Math.round(options.width * this.canvasDPI || DEFAULT_CANVAS_WIDTH)));
|
|
743
|
+
this.canvas.height = Math.max(MIN_CANVAS_DIMENSION,
|
|
744
|
+
Math.min(MAX_CANVAS_DIMENSION,
|
|
745
|
+
Math.round(options.height * this.canvasDPI || DEFAULT_CANVAS_HEIGHT)));
|
|
63
746
|
this.canvas.style.display = 'none';
|
|
64
747
|
|
|
65
748
|
const context = this.canvas.getContext('2d');
|
|
66
749
|
if (!context) {
|
|
67
|
-
throw new Error('Could not get 2D context');
|
|
750
|
+
throw new Error('AtomixGlass: Could not get 2D canvas context');
|
|
68
751
|
}
|
|
69
752
|
this.context = context;
|
|
70
753
|
}
|
|
71
754
|
|
|
755
|
+
private validateOptions(options: ShaderOptions): boolean {
|
|
756
|
+
try {
|
|
757
|
+
return (
|
|
758
|
+
options &&
|
|
759
|
+
typeof options.width === 'number' &&
|
|
760
|
+
options.width > 0 &&
|
|
761
|
+
options.width <= MAX_CANVAS_DIMENSION &&
|
|
762
|
+
typeof options.height === 'number' &&
|
|
763
|
+
options.height > 0 &&
|
|
764
|
+
options.height <= MAX_CANVAS_DIMENSION &&
|
|
765
|
+
typeof options.fragment === 'function'
|
|
766
|
+
);
|
|
767
|
+
} catch (e) {
|
|
768
|
+
// Graceful error handling
|
|
769
|
+
return false;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
72
773
|
updateShader(mousePosition?: Vec2): string {
|
|
73
|
-
|
|
74
|
-
|
|
774
|
+
try {
|
|
775
|
+
const w = this.options.width * this.canvasDPI;
|
|
776
|
+
const h = this.options.height * this.canvasDPI;
|
|
75
777
|
|
|
76
|
-
|
|
77
|
-
|
|
778
|
+
let maxScale = 0;
|
|
779
|
+
const rawValues: number[] = [];
|
|
78
780
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
781
|
+
// Calculate displacement values with enhanced smoothing
|
|
782
|
+
for (let y = 0; y < h; y++) {
|
|
783
|
+
for (let x = 0; x < w; x++) {
|
|
784
|
+
const uv: Vec2 = { x: x / w, y: y / h };
|
|
83
785
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
786
|
+
const pos = this.options.fragment(uv, mousePosition);
|
|
787
|
+
let dx = pos.x * w - x;
|
|
788
|
+
let dy = pos.y * h - y;
|
|
87
789
|
|
|
88
|
-
|
|
89
|
-
|
|
790
|
+
// Apply edge smoothing for Apple-like effect
|
|
791
|
+
const edgeX = Math.min(x / w, (w - x) / w) * 2;
|
|
792
|
+
const edgeY = Math.min(y / h, (h - y) / h) * 2;
|
|
793
|
+
const edgeFactor = Math.min(edgeX, edgeY);
|
|
794
|
+
|
|
795
|
+
dx *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
|
|
796
|
+
dy *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
|
|
797
|
+
|
|
798
|
+
maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy));
|
|
799
|
+
rawValues.push(dx, dy);
|
|
800
|
+
}
|
|
90
801
|
}
|
|
91
|
-
}
|
|
92
802
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
maxScale = Math.max(maxScale, 1); // Ensure minimum scale to prevent over-normalization
|
|
96
|
-
} else {
|
|
97
|
-
maxScale = 1;
|
|
98
|
-
}
|
|
803
|
+
// Improved normalization to prevent artifacts while maintaining intensity
|
|
804
|
+
maxScale = Math.max(maxScale, MIN_SCALE);
|
|
99
805
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
806
|
+
// Create ImageData and fill it
|
|
807
|
+
const imageData = this.context.createImageData(w, h);
|
|
808
|
+
const data = imageData.data;
|
|
103
809
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
810
|
+
// Convert to image data with smoother normalization
|
|
811
|
+
let rawIndex = 0;
|
|
812
|
+
for (let y = 0; y < h; y++) {
|
|
813
|
+
for (let x = 0; x < w; x++) {
|
|
814
|
+
const dx = rawValues[rawIndex++] || 0;
|
|
815
|
+
const dy = rawValues[rawIndex++] || 0;
|
|
110
816
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
817
|
+
// Smooth the displacement values at edges to prevent hard transitions
|
|
818
|
+
const edgeDistance = Math.min(x, y, w - x - 1, h - y - 1);
|
|
819
|
+
const edgeFactor = Math.min(1, edgeDistance / EDGE_FADE_PIXELS);
|
|
114
820
|
|
|
115
|
-
|
|
116
|
-
|
|
821
|
+
const smoothedDx = dx * edgeFactor;
|
|
822
|
+
const smoothedDy = dy * edgeFactor;
|
|
117
823
|
|
|
118
|
-
|
|
119
|
-
|
|
824
|
+
const r = smoothedDx / maxScale + 0.5;
|
|
825
|
+
const g = smoothedDy / maxScale + 0.5;
|
|
120
826
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
827
|
+
const pixelIndex = (y * w + x) * 4;
|
|
828
|
+
data[pixelIndex] = clampValue(r * 255, NORMALIZATION_CLAMP.min, NORMALIZATION_CLAMP.max); // Red channel (X displacement)
|
|
829
|
+
data[pixelIndex + 1] = clampValue(
|
|
830
|
+
g * 255,
|
|
831
|
+
NORMALIZATION_CLAMP.min,
|
|
832
|
+
NORMALIZATION_CLAMP.max
|
|
833
|
+
); // Green channel (Y displacement)
|
|
834
|
+
data[pixelIndex + 2] = clampValue(
|
|
835
|
+
g * 255,
|
|
836
|
+
NORMALIZATION_CLAMP.min,
|
|
837
|
+
NORMALIZATION_CLAMP.max
|
|
838
|
+
); // Blue channel (Y displacement for SVG filter compatibility)
|
|
839
|
+
data[pixelIndex + 3] = 255; // Alpha channel
|
|
840
|
+
}
|
|
126
841
|
}
|
|
127
|
-
}
|
|
128
842
|
|
|
129
|
-
|
|
130
|
-
|
|
843
|
+
this.context.putImageData(imageData, 0, 0);
|
|
844
|
+
return this.canvas.toDataURL();
|
|
845
|
+
} catch (error) {
|
|
846
|
+
// Graceful fallback on error
|
|
847
|
+
console.warn('ShaderDisplacementGenerator: Error generating shader map, using fallback', error);
|
|
848
|
+
return ''; // Return empty string as fallback
|
|
849
|
+
}
|
|
131
850
|
}
|
|
132
851
|
|
|
133
852
|
destroy(): void {
|
|
134
|
-
|
|
853
|
+
try {
|
|
854
|
+
this.canvas.remove();
|
|
855
|
+
} catch (e) {
|
|
856
|
+
// Silently handle cleanup errors
|
|
857
|
+
console.warn('ShaderDisplacementGenerator: Error during cleanup', e);
|
|
858
|
+
}
|
|
135
859
|
}
|
|
136
860
|
|
|
137
861
|
getScale(): number {
|
|
138
862
|
return this.canvasDPI;
|
|
139
863
|
}
|
|
140
|
-
}
|
|
864
|
+
}
|