@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.
Files changed (225) hide show
  1. package/README.md +19 -0
  2. package/dist/atomix.css +1703 -1544
  3. package/dist/atomix.min.css +4 -4
  4. package/dist/index.d.ts +1465 -963
  5. package/dist/index.esm.js +16289 -25908
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +15650 -21780
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.js +1 -1
  10. package/dist/index.min.js.map +1 -1
  11. package/dist/themes/applemix.css +15008 -0
  12. package/dist/themes/applemix.min.css +72 -0
  13. package/dist/themes/boomdevs.css +1608 -1450
  14. package/dist/themes/boomdevs.min.css +5 -5
  15. package/dist/themes/esrar.css +1702 -1543
  16. package/dist/themes/esrar.min.css +4 -4
  17. package/dist/themes/flashtrade.css +15159 -0
  18. package/dist/themes/flashtrade.min.css +86 -0
  19. package/dist/themes/mashroom.css +1699 -1540
  20. package/dist/themes/mashroom.min.css +7 -7
  21. package/dist/themes/shaj-default.css +1693 -1534
  22. package/dist/themes/shaj-default.min.css +4 -4
  23. package/package.json +6 -17
  24. package/src/components/Accordion/Accordion.stories.tsx +662 -21
  25. package/src/components/Accordion/Accordion.tsx +21 -14
  26. package/src/components/AtomixGlass/AtomixGlass.test.tsx +106 -72
  27. package/src/components/AtomixGlass/AtomixGlass.tsx +529 -1195
  28. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +400 -0
  29. package/src/components/AtomixGlass/GlassFilter.tsx +156 -0
  30. package/src/components/AtomixGlass/README.md +124 -2
  31. package/src/components/AtomixGlass/atomixGLass.old.tsx +1266 -0
  32. package/src/components/AtomixGlass/glass-utils.ts +263 -0
  33. package/src/components/AtomixGlass/shader-utils.ts +792 -68
  34. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1250 -0
  35. package/src/components/AtomixGlass/stories/Examples.stories.tsx +5768 -0
  36. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1065 -0
  37. package/src/components/AtomixGlass/stories/Playground.stories.tsx +1129 -0
  38. package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +395 -0
  39. package/src/components/AtomixGlass/stories/shared-components.tsx +301 -0
  40. package/src/components/AtomixGlass/utils.ts +3 -3
  41. package/src/components/Avatar/Avatar.tsx +3 -0
  42. package/src/components/Avatar/AvatarGroup.tsx +2 -1
  43. package/src/components/Badge/Badge.stories.tsx +76 -55
  44. package/src/components/Badge/Badge.tsx +12 -14
  45. package/src/components/Breadcrumb/Breadcrumb.tsx +23 -4
  46. package/src/components/Button/Button.stories.tsx +501 -20
  47. package/src/components/Button/Button.tsx +5 -8
  48. package/src/components/Callout/Callout.stories.tsx +86 -35
  49. package/src/components/Callout/Callout.tsx +31 -9
  50. package/src/components/Card/Card.stories.tsx +565 -2
  51. package/src/components/Card/Card.tsx +15 -4
  52. package/src/components/Card/ElevationCard.tsx +2 -0
  53. package/src/components/Chart/AnimatedChart.tsx +179 -156
  54. package/src/components/Chart/AreaChart.tsx +123 -12
  55. package/src/components/Chart/BarChart.tsx +91 -100
  56. package/src/components/Chart/BaseChart.tsx +80 -0
  57. package/src/components/Chart/BubbleChart.tsx +114 -290
  58. package/src/components/Chart/CandlestickChart.tsx +282 -622
  59. package/src/components/Chart/Chart.stories.tsx +576 -179
  60. package/src/components/Chart/Chart.tsx +374 -75
  61. package/src/components/Chart/ChartRenderer.tsx +371 -220
  62. package/src/components/Chart/ChartToolbar.tsx +372 -61
  63. package/src/components/Chart/ChartTooltip.tsx +33 -18
  64. package/src/components/Chart/DonutChart.tsx +172 -254
  65. package/src/components/Chart/FunnelChart.tsx +169 -240
  66. package/src/components/Chart/GaugeChart.tsx +224 -392
  67. package/src/components/Chart/HeatmapChart.tsx +302 -440
  68. package/src/components/Chart/LineChart.tsx +148 -103
  69. package/src/components/Chart/MultiAxisChart.tsx +267 -395
  70. package/src/components/Chart/PieChart.tsx +114 -64
  71. package/src/components/Chart/RadarChart.tsx +202 -218
  72. package/src/components/Chart/ScatterChart.tsx +111 -97
  73. package/src/components/Chart/TreemapChart.tsx +147 -222
  74. package/src/components/Chart/WaterfallChart.tsx +253 -291
  75. package/src/components/Chart/index.ts +11 -9
  76. package/src/components/Chart/types.ts +85 -9
  77. package/src/components/Chart/utils.ts +66 -0
  78. package/src/components/ColorModeToggle/ColorModeToggle.tsx +6 -3
  79. package/src/components/Countdown/Countdown.tsx +4 -0
  80. package/src/components/DataTable/DataTable.tsx +2 -1
  81. package/src/components/DatePicker/DatePicker.stories.tsx +689 -12
  82. package/src/components/DatePicker/DatePicker.tsx +3 -9
  83. package/src/components/DatePicker/types.ts +5 -0
  84. package/src/components/Dropdown/Dropdown.stories.tsx +32 -25
  85. package/src/components/Dropdown/Dropdown.tsx +26 -28
  86. package/src/components/EdgePanel/EdgePanel.stories.tsx +473 -2
  87. package/src/components/EdgePanel/EdgePanel.tsx +101 -13
  88. package/src/components/Footer/Footer.stories.tsx +187 -60
  89. package/src/components/Footer/Footer.test.tsx +134 -0
  90. package/src/components/Footer/Footer.tsx +133 -34
  91. package/src/components/Footer/FooterLink.tsx +1 -1
  92. package/src/components/Footer/FooterSection.tsx +53 -36
  93. package/src/components/Footer/FooterSocialLink.tsx +32 -29
  94. package/src/components/Footer/README.md +82 -3
  95. package/src/components/Footer/index.ts +1 -1
  96. package/src/components/Form/Checkbox.stories.tsx +13 -5
  97. package/src/components/Form/Checkbox.tsx +3 -6
  98. package/src/components/Form/Form.stories.tsx +10 -3
  99. package/src/components/Form/Form.tsx +2 -0
  100. package/src/components/Form/FormGroup.tsx +2 -1
  101. package/src/components/Form/Input.stories.tsx +12 -11
  102. package/src/components/Form/Input.tsx +97 -95
  103. package/src/components/Form/Radio.stories.tsx +22 -7
  104. package/src/components/Form/Radio.tsx +3 -6
  105. package/src/components/Form/Select.stories.tsx +21 -6
  106. package/src/components/Form/Select.tsx +3 -5
  107. package/src/components/Form/Textarea.stories.tsx +13 -11
  108. package/src/components/Form/Textarea.tsx +88 -86
  109. package/src/components/Hero/Hero.stories.tsx +2 -3
  110. package/src/components/Hero/Hero.tsx +5 -6
  111. package/src/components/Icon/Icon.tsx +12 -1
  112. package/src/components/List/List.tsx +2 -1
  113. package/src/components/List/ListGroup.tsx +2 -1
  114. package/src/components/Messages/Messages.stories.tsx +113 -0
  115. package/src/components/Messages/Messages.tsx +52 -9
  116. package/src/components/Modal/Modal.stories.tsx +48 -32
  117. package/src/components/Modal/Modal.tsx +19 -24
  118. package/src/components/Navigation/Menu/MegaMenu.tsx +2 -2
  119. package/src/components/Navigation/Menu/Menu.tsx +2 -2
  120. package/src/components/Navigation/Nav/Nav.stories.tsx +469 -0
  121. package/src/components/Navigation/Nav/Nav.tsx +22 -4
  122. package/src/components/Navigation/Nav/NavDropdown.tsx +10 -1
  123. package/src/components/Navigation/Navbar/Navbar.stories.tsx +413 -0
  124. package/src/components/Navigation/Navbar/Navbar.tsx +70 -29
  125. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +340 -0
  126. package/src/components/Navigation/SideMenu/SideMenu.tsx +29 -2
  127. package/src/components/Pagination/Pagination.stories.tsx +13 -6
  128. package/src/components/Pagination/Pagination.tsx +7 -6
  129. package/src/components/PhotoViewer/PhotoViewer.tsx +2 -1
  130. package/src/components/Popover/Popover.stories.tsx +32 -24
  131. package/src/components/Popover/Popover.tsx +4 -1
  132. package/src/components/ProductReview/ProductReview.tsx +8 -2
  133. package/src/components/Progress/Progress.tsx +19 -3
  134. package/src/components/Rating/Rating.stories.tsx +11 -6
  135. package/src/components/Rating/Rating.tsx +3 -5
  136. package/src/components/River/River.tsx +5 -5
  137. package/src/components/SectionIntro/SectionIntro.tsx +8 -2
  138. package/src/components/Slider/Slider.stories.tsx +4 -4
  139. package/src/components/Slider/Slider.tsx +4 -3
  140. package/src/components/Spinner/Spinner.tsx +19 -3
  141. package/src/components/Steps/Steps.stories.tsx +5 -4
  142. package/src/components/Steps/Steps.tsx +8 -5
  143. package/src/components/Tab/Tab.stories.tsx +4 -3
  144. package/src/components/Tab/Tab.tsx +8 -6
  145. package/src/components/Testimonial/Testimonial.tsx +8 -2
  146. package/src/components/Todo/Todo.tsx +2 -1
  147. package/src/components/Toggle/Toggle.stories.tsx +5 -4
  148. package/src/components/Toggle/Toggle.tsx +8 -5
  149. package/src/components/Tooltip/Tooltip.stories.tsx +40 -30
  150. package/src/components/Tooltip/Tooltip.tsx +9 -2
  151. package/src/components/Upload/Upload.stories.tsx +252 -0
  152. package/src/components/Upload/Upload.tsx +92 -53
  153. package/src/components/VideoPlayer/VideoPlayer.tsx +3 -1
  154. package/src/components/index.ts +0 -4
  155. package/src/layouts/Grid/Grid.stories.tsx +10 -23
  156. package/src/layouts/Grid/Grid.tsx +20 -1
  157. package/src/layouts/Grid/GridCol.tsx +76 -48
  158. package/src/lib/composables/useAtomixGlass.ts +861 -44
  159. package/src/lib/composables/useBarChart.ts +21 -4
  160. package/src/lib/composables/useChart.ts +227 -370
  161. package/src/lib/composables/useChartExport.ts +19 -78
  162. package/src/lib/composables/useChartToolbar.ts +11 -21
  163. package/src/lib/composables/useEdgePanel.ts +125 -71
  164. package/src/lib/composables/useFooter.ts +3 -3
  165. package/src/lib/composables/useGlassContainer.ts +16 -7
  166. package/src/lib/composables/useLineChart.ts +11 -2
  167. package/src/lib/composables/usePieChart.ts +4 -14
  168. package/src/lib/composables/useRiver.ts +5 -0
  169. package/src/lib/composables/useSlider.ts +62 -24
  170. package/src/lib/composables/useVideoPlayer.ts +60 -63
  171. package/src/lib/constants/components.ts +147 -32
  172. package/src/lib/types/components.ts +355 -25
  173. package/src/lib/utils/displacement-generator.ts +55 -49
  174. package/src/lib/utils/icons.ts +1 -1
  175. package/src/lib/utils/index.ts +16 -10
  176. package/src/styles/01-settings/_settings.accordion.scss +19 -19
  177. package/src/styles/01-settings/_settings.animations.scss +5 -5
  178. package/src/styles/01-settings/_settings.avatar-group.scss +1 -1
  179. package/src/styles/01-settings/_settings.avatar.scss +17 -17
  180. package/src/styles/01-settings/_settings.background.scss +0 -3
  181. package/src/styles/01-settings/_settings.badge.scss +1 -1
  182. package/src/styles/01-settings/_settings.breadcrumb.scss +1 -1
  183. package/src/styles/01-settings/_settings.card.scss +1 -1
  184. package/src/styles/01-settings/_settings.chart.scss +65 -2
  185. package/src/styles/01-settings/_settings.dropdown.scss +1 -1
  186. package/src/styles/01-settings/_settings.edge-panel.scss +1 -1
  187. package/src/styles/01-settings/_settings.footer.scss +35 -42
  188. package/src/styles/01-settings/_settings.input.scss +1 -1
  189. package/src/styles/01-settings/_settings.list.scss +1 -1
  190. package/src/styles/01-settings/_settings.rating.scss +1 -1
  191. package/src/styles/01-settings/_settings.tabs.scss +1 -1
  192. package/src/styles/01-settings/_settings.upload.scss +6 -5
  193. package/src/styles/02-tools/_tools.animations.scss +4 -5
  194. package/src/styles/02-tools/_tools.background.scss +1 -13
  195. package/src/styles/02-tools/_tools.glass.scss +0 -1
  196. package/src/styles/02-tools/_tools.utility-api.scss +91 -48
  197. package/src/styles/03-generic/_generic.root.scss +1 -4
  198. package/src/styles/04-elements/_elements.body.scss +0 -1
  199. package/src/styles/06-components/_components.atomix-glass.scss +249 -0
  200. package/src/styles/06-components/_components.badge.scss +8 -23
  201. package/src/styles/06-components/_components.button.scss +8 -3
  202. package/src/styles/06-components/_components.callout.scss +10 -5
  203. package/src/styles/06-components/_components.card.scss +2 -14
  204. package/src/styles/06-components/_components.chart.scss +969 -1449
  205. package/src/styles/06-components/_components.dropdown.scss +19 -7
  206. package/src/styles/06-components/_components.edge-panel.scss +103 -0
  207. package/src/styles/06-components/_components.footer.scss +166 -85
  208. package/src/styles/06-components/_components.input.scss +8 -9
  209. package/src/styles/06-components/_components.list.scss +1 -0
  210. package/src/styles/06-components/_components.messages.scss +176 -0
  211. package/src/styles/06-components/_components.modal.scss +16 -4
  212. package/src/styles/06-components/_components.navbar.scss +12 -1
  213. package/src/styles/06-components/_components.side-menu.scss +5 -0
  214. package/src/styles/06-components/_components.skeleton.scss +8 -6
  215. package/src/styles/06-components/_components.upload.scss +219 -4
  216. package/src/styles/06-components/old.chart.styles.scss +1 -30
  217. package/src/styles/99-utilities/_index.scss +1 -0
  218. package/src/styles/99-utilities/_utilities.glass-fixes.scss +1 -0
  219. package/src/styles/99-utilities/_utilities.scss +1 -1
  220. package/src/components/AtomixGlass/AtomixGlass.stories.tsx +0 -3011
  221. package/src/components/AtomixGlass/AtomixGlassComprehensivePreview.stories.tsx +0 -1369
  222. package/src/components/Chart/AdvancedChart.tsx +0 -624
  223. package/src/components/Chart/LineChartNew.tsx +0 -167
  224. package/src/components/Chart/RealTimeChart.tsx +0 -436
  225. 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, mouse?: Vec2) => Vec2;
11
+ fragment: (uv: Vec2, mousePosition?: Vec2) => Vec2;
12
12
  mousePosition?: Vec2;
13
13
  }
14
14
 
15
- function smoothStep(a: number, b: number, t: number): number {
16
- t = Math.max(0, Math.min(1, (t - a) / (b - a)));
17
- return t * t * (3 - 2 * t);
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
- function length(x: number, y: number): number {
21
- return Math.sqrt(x * x + y * y);
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
- function roundedRectSDF(
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) + length(Math.max(qx, 0), Math.max(qy, 0)) - radius;
34
- }
78
+ return Math.min(Math.max(qx, qy), 0) + calculateLength(Math.max(qx, 0), Math.max(qy, 0)) - radius;
79
+ };
35
80
 
36
- function texture(x: number, y: number): Vec2 {
37
- return { x, y };
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 distanceToEdge = roundedRectSDF(ix, iy, 0.3, 0.2, 0.6);
46
- const displacement = smoothStep(0.8, 0, distanceToEdge - 0.15);
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
- return texture(ix * scaled + 0.5, iy * scaled + 0.5);
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
- this.canvas.width = options.width * this.canvasDPI;
62
- this.canvas.height = options.height * this.canvasDPI;
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
- const w = this.options.width * this.canvasDPI;
74
- const h = this.options.height * this.canvasDPI;
774
+ try {
775
+ const w = this.options.width * this.canvasDPI;
776
+ const h = this.options.height * this.canvasDPI;
75
777
 
76
- let maxScale = 0;
77
- const rawValues: number[] = [];
778
+ let maxScale = 0;
779
+ const rawValues: number[] = [];
78
780
 
79
- // Calculate displacement values
80
- for (let y = 0; y < h; y++) {
81
- for (let x = 0; x < w; x++) {
82
- const uv: Vec2 = { x: x / w, y: y / h };
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
- const pos = this.options.fragment(uv, mousePosition);
85
- const dx = pos.x * w - x;
86
- const dy = pos.y * h - y;
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
- maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy));
89
- rawValues.push(dx, dy);
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
- // Improved normalization to prevent artifacts while maintaining intensity
94
- if (maxScale > 0) {
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
- // Create ImageData and fill it
101
- const imageData = this.context.createImageData(w, h);
102
- const data = imageData.data;
806
+ // Create ImageData and fill it
807
+ const imageData = this.context.createImageData(w, h);
808
+ const data = imageData.data;
103
809
 
104
- // Convert to image data with smoother normalization
105
- let rawIndex = 0;
106
- for (let y = 0; y < h; y++) {
107
- for (let x = 0; x < w; x++) {
108
- const dx = rawValues[rawIndex++] || 0;
109
- const dy = rawValues[rawIndex++] || 0;
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
- // Smooth the displacement values at edges to prevent hard transitions
112
- const edgeDistance = Math.min(x, y, w - x - 1, h - y - 1);
113
- const edgeFactor = Math.min(1, edgeDistance / 2); // Smooth within 2 pixels of edge
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
- const smoothedDx = dx * edgeFactor;
116
- const smoothedDy = dy * edgeFactor;
821
+ const smoothedDx = dx * edgeFactor;
822
+ const smoothedDy = dy * edgeFactor;
117
823
 
118
- const r = smoothedDx / maxScale + 0.5;
119
- const g = smoothedDy / maxScale + 0.5;
824
+ const r = smoothedDx / maxScale + 0.5;
825
+ const g = smoothedDy / maxScale + 0.5;
120
826
 
121
- const pixelIndex = (y * w + x) * 4;
122
- data[pixelIndex] = Math.max(0, Math.min(255, r * 255)); // Red channel (X displacement)
123
- data[pixelIndex + 1] = Math.max(0, Math.min(255, g * 255)); // Green channel (Y displacement)
124
- data[pixelIndex + 2] = Math.max(0, Math.min(255, g * 255)); // Blue channel (Y displacement for SVG filter compatibility)
125
- data[pixelIndex + 3] = 255; // Alpha channel
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
- this.context.putImageData(imageData, 0, 0);
130
- return this.canvas.toDataURL();
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
- this.canvas.remove();
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
+ }