@shohojdhara/atomix 0.2.4 → 0.2.6
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 +1300 -1418
- package/dist/atomix.min.css +3 -3
- package/dist/index.d.ts +1259 -874
- package/dist/index.esm.js +16256 -26366
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +15691 -22295
- 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 +15036 -0
- package/dist/themes/applemix.min.css +72 -0
- package/dist/themes/boomdevs.css +1300 -1419
- package/dist/themes/boomdevs.min.css +3 -3
- package/dist/themes/esrar.css +1301 -1419
- package/dist/themes/esrar.min.css +3 -3
- package/dist/themes/flashtrade.css +15187 -0
- package/dist/themes/flashtrade.min.css +86 -0
- package/dist/themes/mashroom.css +1299 -1417
- package/dist/themes/mashroom.min.css +5 -5
- package/dist/themes/shaj-default.css +1300 -1418
- package/dist/themes/shaj-default.min.css +3 -3
- package/package.json +6 -17
- package/src/components/Accordion/Accordion.stories.tsx +4 -26
- package/src/components/Accordion/Accordion.tsx +21 -12
- package/src/components/AtomixGlass/AtomixGlass.test.tsx +106 -72
- package/src/components/AtomixGlass/AtomixGlass.tsx +485 -1215
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +399 -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 +404 -236
- package/src/components/AtomixGlass/{AtomixGlass.stories.tsx → stories/AtomixGlass.stories.tsx} +55 -35
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +57 -89
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +149 -149
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +95 -32
- package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +0 -2
- package/src/components/AtomixGlass/stories/shared-components.tsx +9 -18
- 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 +74 -54
- package/src/components/Badge/Badge.tsx +8 -12
- package/src/components/Breadcrumb/Breadcrumb.tsx +23 -4
- package/src/components/Button/Button.tsx +3 -5
- package/src/components/Callout/Callout.stories.tsx +86 -35
- package/src/components/Callout/Callout.tsx +4 -0
- package/src/components/Card/Card.stories.tsx +89 -85
- 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.stories.tsx +121 -11
- package/src/components/ColorModeToggle/ColorModeToggle.tsx +149 -45
- package/src/components/ColorModeToggle/index.ts +1 -1
- package/src/components/Countdown/Countdown.tsx +4 -0
- package/src/components/DataTable/DataTable.tsx +2 -1
- package/src/components/DatePicker/DatePicker.stories.tsx +0 -11
- 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 +13 -15
- package/src/components/EdgePanel/EdgePanel.tsx +20 -5
- 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.tsx +3 -2
- package/src/components/Modal/Modal.stories.tsx +48 -34
- package/src/components/Modal/Modal.tsx +19 -23
- package/src/components/Navigation/Menu/MegaMenu.tsx +2 -2
- package/src/components/Navigation/Menu/Menu.tsx +2 -2
- package/src/components/Navigation/Nav/Nav.tsx +6 -1
- package/src/components/Navigation/Nav/NavDropdown.tsx +10 -1
- package/src/components/Navigation/Navbar/Navbar.tsx +4 -1
- package/src/components/Navigation/SideMenu/SideMenu.tsx +3 -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 +2 -1
- 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 +2 -1
- 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 +13 -6
- package/src/lib/composables/useChart.ts +17 -13
- package/src/lib/composables/useChartExport.ts +19 -78
- package/src/lib/composables/useChartToolbar.ts +0 -1
- package/src/lib/composables/useEdgePanel.ts +111 -103
- package/src/lib/composables/useFooter.ts +3 -3
- package/src/lib/composables/useGlassContainer.ts +16 -7
- package/src/lib/composables/useLineChart.ts +8 -1
- 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 +146 -32
- package/src/lib/types/components.ts +258 -10
- 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 +1 -4
- 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.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 +42 -34
- 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 +217 -39
- package/src/styles/06-components/_components.badge.scss +6 -8
- package/src/styles/06-components/_components.button.scss +8 -3
- 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.color-mode-toggle.scss +43 -6
- package/src/styles/06-components/_components.dropdown.scss +19 -7
- package/src/styles/06-components/_components.edge-panel.scss +4 -2
- 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.modal.scss +5 -3
- 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/_utilities.opacity.scss +1 -1
- package/src/styles/99-utilities/_utilities.scss +1 -1
- 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
|
@@ -27,14 +27,36 @@ const DEPTH_LAYERS = 3;
|
|
|
27
27
|
const ORGANIC_FLOW_SCALE = 12;
|
|
28
28
|
const RADIAL_DISTORTION_STRENGTH = 0.4;
|
|
29
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
|
+
|
|
30
36
|
// Utility functions
|
|
31
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
|
+
|
|
32
43
|
const clamped = Math.max(0, Math.min(1, (t - a) / (b - a)));
|
|
33
44
|
return clamped * clamped * (3 - 2 * clamped);
|
|
34
45
|
};
|
|
35
46
|
|
|
36
47
|
const calculateLength = (x: number, y: number): number => {
|
|
37
|
-
|
|
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);
|
|
38
60
|
};
|
|
39
61
|
|
|
40
62
|
const roundedRectSDF = (
|
|
@@ -44,80 +66,140 @@ const roundedRectSDF = (
|
|
|
44
66
|
height: number,
|
|
45
67
|
radius: number
|
|
46
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
|
+
|
|
47
76
|
const qx = Math.abs(x) - width + radius;
|
|
48
77
|
const qy = Math.abs(y) - height + radius;
|
|
49
78
|
return Math.min(Math.max(qx, qy), 0) + calculateLength(Math.max(qx, 0), Math.max(qy, 0)) - radius;
|
|
50
79
|
};
|
|
51
80
|
|
|
52
|
-
const createTexture = (x: number, y: number): Vec2 =>
|
|
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
|
+
};
|
|
53
87
|
|
|
54
88
|
// Validation helpers
|
|
55
89
|
const validateVec2 = (vec: Vec2): boolean => {
|
|
56
|
-
return
|
|
57
|
-
|
|
90
|
+
return (
|
|
91
|
+
vec && typeof vec.x === 'number' && typeof vec.y === 'number' && !isNaN(vec.x) && !isNaN(vec.y)
|
|
92
|
+
);
|
|
58
93
|
};
|
|
59
94
|
|
|
60
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
|
+
|
|
61
105
|
return Math.max(min, Math.min(max, value));
|
|
62
106
|
};
|
|
63
107
|
|
|
64
108
|
// Advanced easing functions for Apple-style smooth animations
|
|
65
109
|
const easeInOutCubic = (t: number): number => {
|
|
66
|
-
|
|
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;
|
|
67
117
|
};
|
|
68
118
|
|
|
69
119
|
const easeOutQuart = (t: number): number => {
|
|
70
|
-
|
|
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);
|
|
71
127
|
};
|
|
72
128
|
|
|
73
129
|
// Perlin-like noise for organic distortion
|
|
74
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
|
+
|
|
75
136
|
const X = Math.floor(x) & 255;
|
|
76
137
|
const Y = Math.floor(y) & 255;
|
|
77
|
-
|
|
138
|
+
|
|
78
139
|
const xf = x - Math.floor(x);
|
|
79
140
|
const yf = y - Math.floor(y);
|
|
80
|
-
|
|
141
|
+
|
|
81
142
|
const u = easeInOutCubic(xf);
|
|
82
143
|
const v = easeInOutCubic(yf);
|
|
83
|
-
|
|
144
|
+
|
|
84
145
|
// Simple hash-based pseudo-random
|
|
85
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
|
+
|
|
86
152
|
const n = i + j * 57;
|
|
87
|
-
|
|
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);
|
|
88
156
|
};
|
|
89
|
-
|
|
157
|
+
|
|
90
158
|
const a = hash(X, Y);
|
|
91
159
|
const b = hash(X + 1, Y);
|
|
92
160
|
const c = hash(X, Y + 1);
|
|
93
161
|
const d = hash(X + 1, Y + 1);
|
|
94
|
-
|
|
162
|
+
|
|
95
163
|
const x1 = a + u * (b - a);
|
|
96
164
|
const x2 = c + u * (d - c);
|
|
97
|
-
|
|
165
|
+
|
|
98
166
|
return x1 + v * (x2 - x1);
|
|
99
167
|
};
|
|
100
168
|
|
|
101
169
|
// Multi-octave noise for complex organic patterns
|
|
102
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
|
+
|
|
103
179
|
let value = 0;
|
|
104
180
|
let amplitude = 0.5;
|
|
105
181
|
let frequency = 1;
|
|
106
|
-
|
|
107
|
-
for (let i = 0; i <
|
|
182
|
+
|
|
183
|
+
for (let i = 0; i < clampedOctaves; i++) {
|
|
108
184
|
value += amplitude * noise2D(x * frequency, y * frequency);
|
|
109
185
|
frequency *= 2;
|
|
110
186
|
amplitude *= 0.5;
|
|
111
187
|
}
|
|
112
|
-
|
|
188
|
+
|
|
113
189
|
return value;
|
|
114
190
|
};
|
|
115
191
|
|
|
116
192
|
// Radial distortion for glass-like refraction
|
|
117
193
|
const calculateRadialDistortion = (x: number, y: number, strength: number): Vec2 => {
|
|
118
|
-
|
|
119
|
-
|
|
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
|
+
}
|
|
120
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
|
+
|
|
121
203
|
return {
|
|
122
204
|
x: x * (1 + distortion),
|
|
123
205
|
y: y * (1 + distortion),
|
|
@@ -126,9 +208,20 @@ const calculateRadialDistortion = (x: number, y: number, strength: number): Vec2
|
|
|
126
208
|
|
|
127
209
|
// Chromatic aberration calculation
|
|
128
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
|
+
|
|
129
217
|
const distance = calculateLength(x, y);
|
|
130
|
-
|
|
218
|
+
// Prevent division by zero and extreme values
|
|
219
|
+
if (distance === 0) {
|
|
220
|
+
return { x: 0, y: 0 };
|
|
221
|
+
}
|
|
131
222
|
|
|
223
|
+
const angle = Math.atan2(y, x);
|
|
224
|
+
|
|
132
225
|
return {
|
|
133
226
|
x: Math.cos(angle) * distance * intensity,
|
|
134
227
|
y: Math.sin(angle) * distance * intensity,
|
|
@@ -137,38 +230,56 @@ const calculateChromaticOffset = (x: number, y: number, intensity: number): Vec2
|
|
|
137
230
|
|
|
138
231
|
// Advanced caustic pattern generator for glass refraction
|
|
139
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
|
+
|
|
140
239
|
const scale = 8;
|
|
141
240
|
const speed = 2;
|
|
142
|
-
|
|
241
|
+
|
|
143
242
|
// Multiple caustic layers for realistic light refraction
|
|
144
243
|
const caustic1 = Math.sin(x * scale + time * speed) * Math.cos(y * scale - time * speed);
|
|
145
|
-
const caustic2 =
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
+
|
|
150
251
|
// Combine caustic layers with varying intensities
|
|
151
|
-
const combined =
|
|
152
|
-
|
|
252
|
+
const combined = caustic1 * 0.5 + caustic2 * 0.3 + caustic3 * 0.2;
|
|
253
|
+
|
|
153
254
|
// Apply intensity and normalize to 0-1 range
|
|
154
255
|
return (combined + 1) * 0.5 * intensity;
|
|
155
256
|
};
|
|
156
257
|
|
|
157
258
|
// Spectral dispersion for rainbow-like chromatic effects
|
|
158
259
|
const calculateSpectralDispersion = (
|
|
159
|
-
x: number,
|
|
160
|
-
y: number,
|
|
161
|
-
angle: number,
|
|
260
|
+
x: number,
|
|
261
|
+
y: number,
|
|
262
|
+
angle: number,
|
|
162
263
|
intensity: number
|
|
163
264
|
): { r: Vec2; g: Vec2; b: Vec2 } => {
|
|
164
|
-
|
|
165
|
-
|
|
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
|
+
}
|
|
166
274
|
|
|
275
|
+
const distance = calculateLength(x, y);
|
|
276
|
+
const dispersionStrength = Math.min(distance * intensity, 1); // Limit strength to prevent extreme values
|
|
277
|
+
|
|
167
278
|
// Different wavelengths refract at different angles (like a prism)
|
|
168
279
|
const redOffset = dispersionStrength * 0.8;
|
|
169
280
|
const greenOffset = dispersionStrength * 1.0;
|
|
170
281
|
const blueOffset = dispersionStrength * 1.2;
|
|
171
|
-
|
|
282
|
+
|
|
172
283
|
return {
|
|
173
284
|
r: {
|
|
174
285
|
x: Math.cos(angle) * redOffset,
|
|
@@ -187,55 +298,78 @@ const calculateSpectralDispersion = (
|
|
|
187
298
|
|
|
188
299
|
// Parallax depth offset calculation for multi-layer effects
|
|
189
300
|
const calculateParallaxOffset = (
|
|
190
|
-
x: number,
|
|
191
|
-
y: number,
|
|
192
|
-
depth: number,
|
|
193
|
-
mouseX: number = 0,
|
|
301
|
+
x: number,
|
|
302
|
+
y: number,
|
|
303
|
+
depth: number,
|
|
304
|
+
mouseX: number = 0,
|
|
194
305
|
mouseY: number = 0
|
|
195
306
|
): Vec2 => {
|
|
196
|
-
|
|
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
|
+
}
|
|
197
313
|
|
|
314
|
+
const parallaxStrength = Math.min(0.02 * depth, 0.1); // Limit strength to prevent extreme values
|
|
315
|
+
|
|
198
316
|
// Calculate offset based on view angle (simulated by mouse position)
|
|
199
317
|
const offsetX = (x - mouseX) * parallaxStrength;
|
|
200
318
|
const offsetY = (y - mouseY) * parallaxStrength;
|
|
201
|
-
|
|
319
|
+
|
|
202
320
|
return { x: offsetX, y: offsetY };
|
|
203
321
|
};
|
|
204
322
|
|
|
205
323
|
// Volumetric density for depth perception and scattering
|
|
206
|
-
const calculateVolumetricDensity = (
|
|
207
|
-
|
|
208
|
-
y
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const noiseValue = fbm(x * 5 + time * 0.5, y * 5 - time * 0.5, 3);
|
|
213
|
-
const depthFalloff = Math.exp(-depth * 2);
|
|
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
|
+
}
|
|
214
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
|
+
|
|
215
334
|
return noiseValue * depthFalloff * 0.5 + 0.5;
|
|
216
335
|
};
|
|
217
336
|
|
|
218
337
|
// Advanced turbulence for organic glass distortion
|
|
219
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
|
+
|
|
220
348
|
let turbulence = 0;
|
|
221
349
|
let amplitude = 1;
|
|
222
350
|
let frequency = 1;
|
|
223
|
-
|
|
224
|
-
for (let i = 0; i <
|
|
351
|
+
|
|
352
|
+
for (let i = 0; i < clampedOctaves; i++) {
|
|
225
353
|
const noiseVal = Math.abs(noise2D(x * frequency + time, y * frequency - time));
|
|
226
354
|
turbulence += noiseVal * amplitude;
|
|
227
355
|
frequency *= 2;
|
|
228
356
|
amplitude *= 0.5;
|
|
229
357
|
}
|
|
230
|
-
|
|
358
|
+
|
|
231
359
|
return turbulence;
|
|
232
360
|
};
|
|
233
361
|
|
|
234
362
|
// Micro-surface detail for high-quality glass texture
|
|
235
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
|
+
|
|
236
370
|
const highFreqNoise = fbm(x * 40 + time * 0.3, y * 40 - time * 0.3, 6);
|
|
237
371
|
const microDetail = fbm(x * 80, y * 80, 4);
|
|
238
|
-
|
|
372
|
+
|
|
239
373
|
return (highFreqNoise * 0.7 + microDetail * 0.3) * 0.5;
|
|
240
374
|
};
|
|
241
375
|
|
|
@@ -245,65 +379,66 @@ export const fragmentShaders = {
|
|
|
245
379
|
if (!validateVec2(uv)) {
|
|
246
380
|
return { x: 0.5, y: 0.5 };
|
|
247
381
|
}
|
|
248
|
-
|
|
382
|
+
|
|
249
383
|
const ix = uv.x - 0.5;
|
|
250
384
|
const iy = uv.y - 0.5;
|
|
251
385
|
const time = Date.now() * TIME_SCALE;
|
|
252
|
-
|
|
386
|
+
|
|
253
387
|
// Enhanced distortion with mouse influence
|
|
254
388
|
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
255
389
|
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
256
390
|
const mouseDistance = calculateLength(mouseX, mouseY);
|
|
257
391
|
const mouseFalloff = easeOutQuart(1 - Math.min(mouseDistance * 2, 1));
|
|
258
|
-
|
|
392
|
+
|
|
259
393
|
// Multi-layered organic distortion using FBM noise
|
|
260
394
|
const noiseScale = ORGANIC_FLOW_SCALE;
|
|
261
|
-
const organicFlow =
|
|
262
|
-
(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
+
|
|
267
402
|
// Enhanced Apple-like liquid distortion with rounded rect SDF
|
|
268
403
|
const distanceToEdge = roundedRectSDF(ix, iy, 0.4, 0.3, 0.35);
|
|
269
404
|
const baseDisplacement = smoothStep(0.8, 0, distanceToEdge - 0.05);
|
|
270
|
-
|
|
405
|
+
|
|
271
406
|
// Radial distortion for glass-like refraction
|
|
272
407
|
const radialDist = calculateRadialDistortion(ix, iy, RADIAL_DISTORTION_STRENGTH * 0.1);
|
|
273
408
|
const refractionX = (radialDist.x - ix) * REFRACTION_INTENSITY * baseDisplacement;
|
|
274
409
|
const refractionY = (radialDist.y - iy) * REFRACTION_INTENSITY * baseDisplacement;
|
|
275
|
-
|
|
410
|
+
|
|
276
411
|
// Apple-style liquid flow with time-based animation
|
|
277
412
|
const flowX = Math.sin((ix + mouseX * 2) * 8 + time * 2) * 0.018;
|
|
278
413
|
const flowY = Math.cos((iy + mouseY * 2) * 8 + time * 1.5) * 0.018;
|
|
279
|
-
|
|
414
|
+
|
|
280
415
|
// Multi-directional ripples with mouse influence
|
|
281
416
|
const ripple1 = Math.sin((ix - mouseX) * 12 + (iy - mouseY) * 12 + time * 3) * 0.015;
|
|
282
417
|
const ripple2 = Math.cos((ix + mouseX) * 10 - (iy - mouseY) * 10 - time * 2) * 0.012;
|
|
283
418
|
const rippleEffect = (ripple1 + ripple2) * mouseFalloff * mouseDistance;
|
|
284
|
-
|
|
419
|
+
|
|
285
420
|
// Depth-based layering for premium glass effect
|
|
286
421
|
const depthLayer1 = Math.sin(ix * 15 + time) * Math.cos(iy * 15 - time) * 0.008;
|
|
287
422
|
const depthLayer2 = Math.sin(ix * 20 - time * 0.5) * Math.cos(iy * 20 + time * 0.5) * 0.006;
|
|
288
423
|
const depthEffect = (depthLayer1 + depthLayer2) * baseDisplacement;
|
|
289
|
-
|
|
424
|
+
|
|
290
425
|
// Combine all distortion effects with fluid viscosity
|
|
291
426
|
const liquidFlow = (flowX + flowY + organicFlow * 0.025) * FLUID_VISCOSITY;
|
|
292
427
|
const totalDistortionX = refractionX + liquidFlow + rippleEffect + depthEffect;
|
|
293
428
|
const totalDistortionY = refractionY + liquidFlow * 0.8 + rippleEffect * 0.9 + depthEffect;
|
|
294
|
-
|
|
429
|
+
|
|
295
430
|
// Apply chromatic aberration for premium glass look
|
|
296
431
|
const chromaticOffset = calculateChromaticOffset(ix, iy, CHROMATIC_SPREAD * baseDisplacement);
|
|
297
|
-
|
|
432
|
+
|
|
298
433
|
const displacement = baseDisplacement * 1.15;
|
|
299
434
|
const scaled = smoothStep(0, 1, displacement);
|
|
300
|
-
|
|
435
|
+
|
|
301
436
|
// Final position with all effects combined
|
|
302
437
|
const finalX = ix + totalDistortionX + chromaticOffset.x * 0.5;
|
|
303
438
|
const finalY = iy + totalDistortionY + chromaticOffset.y * 0.5;
|
|
304
|
-
|
|
439
|
+
|
|
305
440
|
return createTexture(
|
|
306
|
-
clampValue(finalX * scaled + 0.5, 0, 1),
|
|
441
|
+
clampValue(finalX * scaled + 0.5, 0, 1),
|
|
307
442
|
clampValue(finalY * scaled + 0.5, 0, 1)
|
|
308
443
|
);
|
|
309
444
|
},
|
|
@@ -313,42 +448,39 @@ export const fragmentShaders = {
|
|
|
313
448
|
if (!validateVec2(uv)) {
|
|
314
449
|
return { x: 0.5, y: 0.5 };
|
|
315
450
|
}
|
|
316
|
-
|
|
451
|
+
|
|
317
452
|
const ix = uv.x - 0.5;
|
|
318
453
|
const iy = uv.y - 0.5;
|
|
319
454
|
const time = Date.now() * TIME_SCALE * 0.6;
|
|
320
|
-
|
|
455
|
+
|
|
321
456
|
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
322
457
|
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
323
458
|
const mouseDistance = calculateLength(mouseX, mouseY);
|
|
324
459
|
const mouseFalloff = easeOutQuart(1 - Math.min(mouseDistance * 1.5, 1));
|
|
325
|
-
|
|
460
|
+
|
|
326
461
|
// High-quality organic distortion with multiple octaves
|
|
327
462
|
const organicX = fbm((ix + mouseX * 0.3) * 10 + time, (iy + mouseY * 0.3) * 10, 5) - 0.5;
|
|
328
463
|
const organicY = fbm((ix - mouseX * 0.3) * 10, (iy - mouseY * 0.3) * 10 + time * 0.8, 5) - 0.5;
|
|
329
|
-
|
|
464
|
+
|
|
330
465
|
// Smooth rounded rectangle mask
|
|
331
466
|
const distanceToEdge = roundedRectSDF(ix, iy, 0.42, 0.32, 0.38);
|
|
332
467
|
const mask = smoothStep(0.85, -0.1, distanceToEdge);
|
|
333
|
-
|
|
468
|
+
|
|
334
469
|
// Fluid dynamics simulation
|
|
335
470
|
const fluidVelocityX = Math.sin(ix * 6 + time * 2) * Math.cos(iy * 4 - time) * 0.025;
|
|
336
471
|
const fluidVelocityY = Math.cos(ix * 4 - time) * Math.sin(iy * 6 + time * 2) * 0.025;
|
|
337
|
-
|
|
472
|
+
|
|
338
473
|
// Mouse-driven vortex effect
|
|
339
474
|
const vortexAngle = Math.atan2(iy - mouseY, ix - mouseX);
|
|
340
475
|
const vortexStrength = mouseFalloff * mouseDistance * 0.08;
|
|
341
476
|
const vortexX = Math.cos(vortexAngle + time) * vortexStrength;
|
|
342
477
|
const vortexY = Math.sin(vortexAngle + time) * vortexStrength;
|
|
343
|
-
|
|
478
|
+
|
|
344
479
|
// Combine effects with premium smoothing
|
|
345
480
|
const totalX = ix + (organicX * 0.035 + fluidVelocityX + vortexX) * mask;
|
|
346
481
|
const totalY = iy + (organicY * 0.035 + fluidVelocityY + vortexY) * mask;
|
|
347
|
-
|
|
348
|
-
return createTexture(
|
|
349
|
-
clampValue(totalX + 0.5, 0, 1),
|
|
350
|
-
clampValue(totalY + 0.5, 0, 1)
|
|
351
|
-
);
|
|
482
|
+
|
|
483
|
+
return createTexture(clampValue(totalX + 0.5, 0, 1), clampValue(totalY + 0.5, 0, 1));
|
|
352
484
|
},
|
|
353
485
|
|
|
354
486
|
// High-end glass with advanced refraction and depth
|
|
@@ -356,20 +488,20 @@ export const fragmentShaders = {
|
|
|
356
488
|
if (!validateVec2(uv)) {
|
|
357
489
|
return { x: 0.5, y: 0.5 };
|
|
358
490
|
}
|
|
359
|
-
|
|
491
|
+
|
|
360
492
|
const ix = uv.x - 0.5;
|
|
361
493
|
const iy = uv.y - 0.5;
|
|
362
494
|
const time = Date.now() * TIME_SCALE * 0.4;
|
|
363
|
-
|
|
495
|
+
|
|
364
496
|
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
365
497
|
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
366
498
|
const mouseDistance = calculateLength(mouseX, mouseY);
|
|
367
|
-
|
|
499
|
+
|
|
368
500
|
// Advanced radial distortion with depth
|
|
369
501
|
const centerDistance = calculateLength(ix, iy);
|
|
370
|
-
const refractionStrength = Math.pow(centerDistance, 1.5) * 0.3;
|
|
502
|
+
const refractionStrength = Math.pow(Math.min(centerDistance, 1), 1.5) * 0.3; // Limit centerDistance
|
|
371
503
|
const refractionAngle = Math.atan2(iy, ix);
|
|
372
|
-
|
|
504
|
+
|
|
373
505
|
// Multi-layer depth effect
|
|
374
506
|
let depthX = 0;
|
|
375
507
|
let depthY = 0;
|
|
@@ -377,29 +509,26 @@ export const fragmentShaders = {
|
|
|
377
509
|
const layerScale = (layer + 1) * 5;
|
|
378
510
|
const layerTime = time * (1 + layer * 0.3);
|
|
379
511
|
const layerStrength = 0.01 / (layer + 1);
|
|
380
|
-
|
|
512
|
+
|
|
381
513
|
depthX += Math.sin(ix * layerScale + layerTime) * layerStrength;
|
|
382
514
|
depthY += Math.cos(iy * layerScale - layerTime) * layerStrength;
|
|
383
515
|
}
|
|
384
|
-
|
|
516
|
+
|
|
385
517
|
// Glass refraction with mouse influence
|
|
386
518
|
const refractionX = Math.cos(refractionAngle) * refractionStrength * (1 + mouseDistance * 0.5);
|
|
387
519
|
const refractionY = Math.sin(refractionAngle) * refractionStrength * (1 + mouseDistance * 0.5);
|
|
388
|
-
|
|
520
|
+
|
|
389
521
|
// Subtle organic movement
|
|
390
522
|
const organicNoise = fbm(ix * 8 + time, iy * 8 - time, 2) - 0.5;
|
|
391
|
-
|
|
523
|
+
|
|
392
524
|
// Edge-aware distortion
|
|
393
525
|
const distanceToEdge = roundedRectSDF(ix, iy, 0.43, 0.33, 0.36);
|
|
394
526
|
const edgeMask = smoothStep(0.9, -0.05, distanceToEdge);
|
|
395
|
-
|
|
527
|
+
|
|
396
528
|
const finalX = ix + (refractionX + depthX + organicNoise * 0.015) * edgeMask;
|
|
397
529
|
const finalY = iy + (refractionY + depthY + organicNoise * 0.015) * edgeMask;
|
|
398
|
-
|
|
399
|
-
return createTexture(
|
|
400
|
-
clampValue(finalX + 0.5, 0, 1),
|
|
401
|
-
clampValue(finalY + 0.5, 0, 1)
|
|
402
|
-
);
|
|
530
|
+
|
|
531
|
+
return createTexture(clampValue(finalX + 0.5, 0, 1), clampValue(finalY + 0.5, 0, 1));
|
|
403
532
|
},
|
|
404
533
|
|
|
405
534
|
// Metallic liquid effect with shimmer
|
|
@@ -407,36 +536,36 @@ export const fragmentShaders = {
|
|
|
407
536
|
if (!validateVec2(uv)) {
|
|
408
537
|
return { x: 0.5, y: 0.5 };
|
|
409
538
|
}
|
|
410
|
-
|
|
539
|
+
|
|
411
540
|
const ix = uv.x - 0.5;
|
|
412
541
|
const iy = uv.y - 0.5;
|
|
413
542
|
const time = Date.now() * TIME_SCALE * 1.2;
|
|
414
|
-
|
|
543
|
+
|
|
415
544
|
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
416
545
|
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
417
|
-
|
|
546
|
+
|
|
418
547
|
// Metallic wave patterns
|
|
419
548
|
const wave1 = Math.sin(ix * 20 + time * 4) * Math.cos(iy * 15 - time * 3) * 0.02;
|
|
420
549
|
const wave2 = Math.cos(ix * 15 - time * 2) * Math.sin(iy * 20 + time * 5) * 0.015;
|
|
421
|
-
|
|
550
|
+
|
|
422
551
|
// Shimmer effect
|
|
423
552
|
const shimmer = fbm(ix * 25 + time * 2, iy * 25 - time * 2, 4) * 0.025;
|
|
424
|
-
|
|
553
|
+
|
|
425
554
|
// Mouse interaction with metallic flow
|
|
426
555
|
const flowAngle = Math.atan2(iy - mouseY, ix - mouseX);
|
|
427
556
|
const flowDistance = calculateLength(ix - mouseX, iy - mouseY);
|
|
428
|
-
const flowEffect =
|
|
429
|
-
|
|
557
|
+
const flowEffect =
|
|
558
|
+
Math.sin(flowDistance * 15 - time * 6) *
|
|
559
|
+
0.02 *
|
|
560
|
+
easeOutQuart(1 - Math.min(flowDistance * 2, 1));
|
|
561
|
+
|
|
430
562
|
const distanceToEdge = roundedRectSDF(ix, iy, 0.41, 0.31, 0.37);
|
|
431
563
|
const mask = smoothStep(0.88, -0.08, distanceToEdge);
|
|
432
|
-
|
|
564
|
+
|
|
433
565
|
const totalX = ix + (wave1 + shimmer + Math.cos(flowAngle) * flowEffect) * mask;
|
|
434
566
|
const totalY = iy + (wave2 + shimmer * 0.8 + Math.sin(flowAngle) * flowEffect) * mask;
|
|
435
|
-
|
|
436
|
-
return createTexture(
|
|
437
|
-
clampValue(totalX + 0.5, 0, 1),
|
|
438
|
-
clampValue(totalY + 0.5, 0, 1)
|
|
439
|
-
);
|
|
567
|
+
|
|
568
|
+
return createTexture(clampValue(totalX + 0.5, 0, 1), clampValue(totalY + 0.5, 0, 1));
|
|
440
569
|
},
|
|
441
570
|
|
|
442
571
|
// basiBasi - Expert Premium Glass Shader
|
|
@@ -445,84 +574,87 @@ export const fragmentShaders = {
|
|
|
445
574
|
if (!validateVec2(uv)) {
|
|
446
575
|
return { x: 0.5, y: 0.5 };
|
|
447
576
|
}
|
|
448
|
-
|
|
577
|
+
|
|
449
578
|
const ix = uv.x - 0.5;
|
|
450
579
|
const iy = uv.y - 0.5;
|
|
451
580
|
const time = Date.now() * TIME_SCALE * 0.5;
|
|
452
|
-
|
|
581
|
+
|
|
453
582
|
// Mouse interaction setup
|
|
454
583
|
const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
|
|
455
584
|
const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
|
|
456
585
|
const mouseDistance = calculateLength(mouseX, mouseY);
|
|
457
586
|
const mouseFalloff = easeOutQuart(1 - Math.min(mouseDistance * 1.2, 1));
|
|
458
|
-
|
|
587
|
+
|
|
459
588
|
// === CAUSTIC LIGHT PATTERNS ===
|
|
460
589
|
// Simulate light refraction through glass creating caustic patterns
|
|
461
590
|
const causticIntensity = calculateCaustics(ix, iy, time, 0.8);
|
|
462
591
|
const causticDistortion = (causticIntensity - 0.5) * 0.02;
|
|
463
|
-
|
|
592
|
+
|
|
464
593
|
// === SPECTRAL DISPERSION ===
|
|
465
594
|
// Rainbow-like chromatic effects from light splitting
|
|
466
595
|
const refractionAngle = Math.atan2(iy, ix);
|
|
467
596
|
const spectralDispersion = calculateSpectralDispersion(ix, iy, refractionAngle, 0.025);
|
|
468
|
-
|
|
597
|
+
|
|
469
598
|
// Average the RGB channels for displacement (simulating prism effect)
|
|
470
|
-
const spectralX =
|
|
471
|
-
|
|
472
|
-
|
|
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
|
+
|
|
473
604
|
// === MULTI-LAYER PARALLAX DEPTH ===
|
|
474
605
|
// Create depth perception with 7 layers
|
|
475
606
|
let parallaxX = 0;
|
|
476
607
|
let parallaxY = 0;
|
|
477
608
|
const numParallaxLayers = 7;
|
|
478
|
-
|
|
609
|
+
|
|
479
610
|
for (let layer = 0; layer < numParallaxLayers; layer++) {
|
|
480
611
|
const depth = (layer + 1) / numParallaxLayers;
|
|
481
612
|
const parallaxOffset = calculateParallaxOffset(ix, iy, depth, mouseX, mouseY);
|
|
482
|
-
|
|
613
|
+
|
|
483
614
|
// Layer-specific distortion
|
|
484
|
-
const layerNoise =
|
|
485
|
-
(
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
+
|
|
490
622
|
const layerWeight = 1 / (layer + 1);
|
|
491
623
|
parallaxX += (parallaxOffset.x + layerNoise * 0.01) * layerWeight;
|
|
492
624
|
parallaxY += (parallaxOffset.y + layerNoise * 0.01) * layerWeight;
|
|
493
625
|
}
|
|
494
|
-
|
|
626
|
+
|
|
495
627
|
// Normalize parallax effect
|
|
496
628
|
parallaxX /= numParallaxLayers;
|
|
497
629
|
parallaxY /= numParallaxLayers;
|
|
498
|
-
|
|
630
|
+
|
|
499
631
|
// === VOLUMETRIC SCATTERING ===
|
|
500
632
|
// Simulate light scattering through glass volume
|
|
501
633
|
const volumetricDensity = calculateVolumetricDensity(ix, iy, 0.5, time);
|
|
502
634
|
const scatteringX = Math.cos(refractionAngle) * volumetricDensity * 0.015;
|
|
503
635
|
const scatteringY = Math.sin(refractionAngle) * volumetricDensity * 0.015;
|
|
504
|
-
|
|
636
|
+
|
|
505
637
|
// === ADVANCED TURBULENCE ===
|
|
506
638
|
// High-quality organic distortion
|
|
507
639
|
const turbulence = calculateTurbulence(ix * 6, iy * 6, time, 6);
|
|
508
640
|
const turbulenceX = Math.cos(turbulence * Math.PI * 2) * 0.012;
|
|
509
641
|
const turbulenceY = Math.sin(turbulence * Math.PI * 2) * 0.012;
|
|
510
|
-
|
|
642
|
+
|
|
511
643
|
// === MICRO-SURFACE DETAIL ===
|
|
512
644
|
// Fine glass texture details
|
|
513
645
|
const microSurface = calculateMicroSurface(ix, iy, time);
|
|
514
646
|
const microDetailX = (microSurface - 0.5) * 0.008;
|
|
515
647
|
const microDetailY = (microSurface - 0.5) * 0.008;
|
|
516
|
-
|
|
648
|
+
|
|
517
649
|
// === ADVANCED RADIAL REFRACTION ===
|
|
518
650
|
// Enhanced glass-like refraction with depth
|
|
519
651
|
const centerDistance = calculateLength(ix, iy);
|
|
520
|
-
const refractionStrength = Math.pow(centerDistance, 1.8) * 0.35;
|
|
652
|
+
const refractionStrength = Math.pow(Math.min(centerDistance, 1), 1.8) * 0.35; // Limit centerDistance
|
|
521
653
|
const dynamicRefraction = refractionStrength * (1 + mouseFalloff * mouseDistance * 0.8);
|
|
522
|
-
|
|
654
|
+
|
|
523
655
|
const refractionX = Math.cos(refractionAngle) * dynamicRefraction;
|
|
524
656
|
const refractionY = Math.sin(refractionAngle) * dynamicRefraction;
|
|
525
|
-
|
|
657
|
+
|
|
526
658
|
// === MOUSE-DRIVEN VORTEX ===
|
|
527
659
|
// Interactive swirl effect
|
|
528
660
|
const vortexAngle = Math.atan2(iy - mouseY, ix - mouseX);
|
|
@@ -530,63 +662,64 @@ export const fragmentShaders = {
|
|
|
530
662
|
const vortexStrength = mouseFalloff * Math.sin(vortexDistance * 10 - time * 3) * 0.025;
|
|
531
663
|
const vortexX = Math.cos(vortexAngle + time * 2) * vortexStrength;
|
|
532
664
|
const vortexY = Math.sin(vortexAngle + time * 2) * vortexStrength;
|
|
533
|
-
|
|
665
|
+
|
|
534
666
|
// === FLUID DYNAMICS ===
|
|
535
667
|
// Liquid-like flow patterns
|
|
536
|
-
const fluidX =
|
|
537
|
-
|
|
538
|
-
|
|
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
|
+
|
|
539
673
|
// === RIPPLE EFFECTS ===
|
|
540
674
|
// Multi-directional wave propagation
|
|
541
|
-
const ripple1 = Math.sin(centerDistance * 15 - time * 4) * 0.012;
|
|
542
|
-
const ripple2 = Math.cos(centerDistance * 20 + time * 3) * 0.008;
|
|
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
|
|
543
677
|
const rippleEffect = (ripple1 + ripple2) * mouseFalloff;
|
|
544
678
|
const rippleX = Math.cos(refractionAngle) * rippleEffect;
|
|
545
679
|
const rippleY = Math.sin(refractionAngle) * rippleEffect;
|
|
546
|
-
|
|
680
|
+
|
|
547
681
|
// === EDGE-AWARE MASKING ===
|
|
548
682
|
// Smooth rounded rectangle with premium edge handling
|
|
549
683
|
const distanceToEdge = roundedRectSDF(ix, iy, 0.44, 0.34, 0.39);
|
|
550
684
|
const edgeMask = smoothStep(0.92, -0.12, distanceToEdge);
|
|
551
685
|
const edgeSoftness = smoothStep(0.85, 0.1, distanceToEdge);
|
|
552
|
-
|
|
686
|
+
|
|
553
687
|
// === COMBINE ALL EFFECTS ===
|
|
554
688
|
// Layer all distortions with proper weighting
|
|
555
|
-
const totalDistortionX =
|
|
556
|
-
refractionX * 1.2 +
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
+
|
|
581
717
|
// === FINAL POSITION ===
|
|
582
718
|
// Apply all distortions with smooth falloff
|
|
583
719
|
const finalX = ix + totalDistortionX * 0.85; // Scale down for subtlety
|
|
584
720
|
const finalY = iy + totalDistortionY * 0.85;
|
|
585
|
-
|
|
586
|
-
return createTexture(
|
|
587
|
-
clampValue(finalX + 0.5, 0, 1),
|
|
588
|
-
clampValue(finalY + 0.5, 0, 1)
|
|
589
|
-
);
|
|
721
|
+
|
|
722
|
+
return createTexture(clampValue(finalX + 0.5, 0, 1), clampValue(finalY + 0.5, 0, 1));
|
|
590
723
|
},
|
|
591
724
|
};
|
|
592
725
|
|
|
@@ -601,10 +734,15 @@ export class ShaderDisplacementGenerator {
|
|
|
601
734
|
if (!this.validateOptions(options)) {
|
|
602
735
|
throw new Error('Invalid shader options provided');
|
|
603
736
|
}
|
|
604
|
-
|
|
737
|
+
|
|
605
738
|
this.canvas = document.createElement('canvas');
|
|
606
|
-
|
|
607
|
-
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)));
|
|
608
746
|
this.canvas.style.display = 'none';
|
|
609
747
|
|
|
610
748
|
const context = this.canvas.getContext('2d');
|
|
@@ -613,84 +751,114 @@ export class ShaderDisplacementGenerator {
|
|
|
613
751
|
}
|
|
614
752
|
this.context = context;
|
|
615
753
|
}
|
|
616
|
-
|
|
754
|
+
|
|
617
755
|
private validateOptions(options: ShaderOptions): boolean {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
|
+
}
|
|
622
771
|
}
|
|
623
772
|
|
|
624
773
|
updateShader(mousePosition?: Vec2): string {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
let maxScale = 0;
|
|
629
|
-
const rawValues: number[] = [];
|
|
630
|
-
|
|
631
|
-
// Calculate displacement values with enhanced smoothing
|
|
632
|
-
for (let y = 0; y < h; y++) {
|
|
633
|
-
for (let x = 0; x < w; x++) {
|
|
634
|
-
const uv: Vec2 = { x: x / w, y: y / h };
|
|
635
|
-
|
|
636
|
-
const pos = this.options.fragment(uv, mousePosition);
|
|
637
|
-
let dx = pos.x * w - x;
|
|
638
|
-
let dy = pos.y * h - y;
|
|
639
|
-
|
|
640
|
-
// Apply edge smoothing for Apple-like effect
|
|
641
|
-
const edgeX = Math.min(x / w, (w - x) / w) * 2;
|
|
642
|
-
const edgeY = Math.min(y / h, (h - y) / h) * 2;
|
|
643
|
-
const edgeFactor = Math.min(edgeX, edgeY);
|
|
644
|
-
|
|
645
|
-
dx *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
|
|
646
|
-
dy *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
|
|
647
|
-
|
|
648
|
-
maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy));
|
|
649
|
-
rawValues.push(dx, dy);
|
|
650
|
-
}
|
|
651
|
-
}
|
|
774
|
+
try {
|
|
775
|
+
const w = this.options.width * this.canvasDPI;
|
|
776
|
+
const h = this.options.height * this.canvasDPI;
|
|
652
777
|
|
|
653
|
-
|
|
654
|
-
|
|
778
|
+
let maxScale = 0;
|
|
779
|
+
const rawValues: number[] = [];
|
|
655
780
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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 };
|
|
659
785
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
for (let x = 0; x < w; x++) {
|
|
664
|
-
const dx = rawValues[rawIndex++] || 0;
|
|
665
|
-
const dy = rawValues[rawIndex++] || 0;
|
|
786
|
+
const pos = this.options.fragment(uv, mousePosition);
|
|
787
|
+
let dx = pos.x * w - x;
|
|
788
|
+
let dy = pos.y * h - y;
|
|
666
789
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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);
|
|
670
794
|
|
|
671
|
-
|
|
672
|
-
|
|
795
|
+
dx *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
|
|
796
|
+
dy *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
|
|
673
797
|
|
|
674
|
-
|
|
675
|
-
|
|
798
|
+
maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy));
|
|
799
|
+
rawValues.push(dx, dy);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
676
802
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
803
|
+
// Improved normalization to prevent artifacts while maintaining intensity
|
|
804
|
+
maxScale = Math.max(maxScale, MIN_SCALE);
|
|
805
|
+
|
|
806
|
+
// Create ImageData and fill it
|
|
807
|
+
const imageData = this.context.createImageData(w, h);
|
|
808
|
+
const data = imageData.data;
|
|
809
|
+
|
|
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;
|
|
816
|
+
|
|
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);
|
|
820
|
+
|
|
821
|
+
const smoothedDx = dx * edgeFactor;
|
|
822
|
+
const smoothedDy = dy * edgeFactor;
|
|
823
|
+
|
|
824
|
+
const r = smoothedDx / maxScale + 0.5;
|
|
825
|
+
const g = smoothedDy / maxScale + 0.5;
|
|
826
|
+
|
|
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
|
+
}
|
|
682
841
|
}
|
|
683
|
-
}
|
|
684
842
|
|
|
685
|
-
|
|
686
|
-
|
|
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
|
+
}
|
|
687
850
|
}
|
|
688
851
|
|
|
689
852
|
destroy(): void {
|
|
690
|
-
|
|
853
|
+
try {
|
|
854
|
+
this.canvas.remove();
|
|
855
|
+
} catch (e) {
|
|
856
|
+
// Silently handle cleanup errors
|
|
857
|
+
console.warn('ShaderDisplacementGenerator: Error during cleanup', e);
|
|
858
|
+
}
|
|
691
859
|
}
|
|
692
860
|
|
|
693
861
|
getScale(): number {
|
|
694
862
|
return this.canvasDPI;
|
|
695
863
|
}
|
|
696
|
-
}
|
|
864
|
+
}
|