@vysmo/transitions 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +618 -0
  3. package/dist/index.d.ts +6 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/inputs/normalize.d.ts +2 -0
  8. package/dist/inputs/normalize.d.ts.map +1 -0
  9. package/dist/inputs/normalize.js +2 -0
  10. package/dist/inputs/normalize.js.map +1 -0
  11. package/dist/runner/gl.d.ts +7 -0
  12. package/dist/runner/gl.d.ts.map +1 -0
  13. package/dist/runner/gl.js +59 -0
  14. package/dist/runner/gl.js.map +1 -0
  15. package/dist/runner/mesh.d.ts +28 -0
  16. package/dist/runner/mesh.d.ts.map +1 -0
  17. package/dist/runner/mesh.js +96 -0
  18. package/dist/runner/mesh.js.map +1 -0
  19. package/dist/runner/runner.d.ts +107 -0
  20. package/dist/runner/runner.d.ts.map +1 -0
  21. package/dist/runner/runner.js +410 -0
  22. package/dist/runner/runner.js.map +1 -0
  23. package/dist/transitions/all.d.ts +9 -0
  24. package/dist/transitions/all.d.ts.map +1 -0
  25. package/dist/transitions/all.js +129 -0
  26. package/dist/transitions/all.js.map +1 -0
  27. package/dist/transitions/bloom-reveal.d.ts +27 -0
  28. package/dist/transitions/bloom-reveal.d.ts.map +1 -0
  29. package/dist/transitions/bloom-reveal.js +106 -0
  30. package/dist/transitions/bloom-reveal.js.map +1 -0
  31. package/dist/transitions/chromatic-pulse.d.ts +4 -0
  32. package/dist/transitions/chromatic-pulse.d.ts.map +1 -0
  33. package/dist/transitions/chromatic-pulse.js +40 -0
  34. package/dist/transitions/chromatic-pulse.js.map +1 -0
  35. package/dist/transitions/clock-wipe.d.ts +6 -0
  36. package/dist/transitions/clock-wipe.d.ts.map +1 -0
  37. package/dist/transitions/clock-wipe.js +33 -0
  38. package/dist/transitions/clock-wipe.js.map +1 -0
  39. package/dist/transitions/color-phase.d.ts +10 -0
  40. package/dist/transitions/color-phase.d.ts.map +1 -0
  41. package/dist/transitions/color-phase.js +24 -0
  42. package/dist/transitions/color-phase.js.map +1 -0
  43. package/dist/transitions/colour-distance.d.ts.map +1 -0
  44. package/dist/transitions/cross-warp.d.ts.map +1 -0
  45. package/dist/transitions/cross-zoom.d.ts +5 -0
  46. package/dist/transitions/cross-zoom.d.ts.map +1 -0
  47. package/dist/transitions/cross-zoom.js +59 -0
  48. package/dist/transitions/cross-zoom.js.map +1 -0
  49. package/dist/transitions/crosshatch.d.ts +12 -0
  50. package/dist/transitions/crosshatch.d.ts.map +1 -0
  51. package/dist/transitions/crosshatch.js +49 -0
  52. package/dist/transitions/crosshatch.js.map +1 -0
  53. package/dist/transitions/define.d.ts +35 -0
  54. package/dist/transitions/define.d.ts.map +1 -0
  55. package/dist/transitions/define.js +37 -0
  56. package/dist/transitions/define.js.map +1 -0
  57. package/dist/transitions/directional-burn.d.ts +14 -0
  58. package/dist/transitions/directional-burn.d.ts.map +1 -0
  59. package/dist/transitions/directional-burn.js +67 -0
  60. package/dist/transitions/directional-burn.js.map +1 -0
  61. package/dist/transitions/directional-warp.d.ts +12 -0
  62. package/dist/transitions/directional-warp.d.ts.map +1 -0
  63. package/dist/transitions/directional-warp.js +46 -0
  64. package/dist/transitions/directional-warp.js.map +1 -0
  65. package/dist/transitions/dissolve.d.ts +2 -0
  66. package/dist/transitions/dissolve.d.ts.map +1 -0
  67. package/dist/transitions/dissolve.js +16 -0
  68. package/dist/transitions/dissolve.js.map +1 -0
  69. package/dist/transitions/dreamy-zoom.d.ts +25 -0
  70. package/dist/transitions/dreamy-zoom.d.ts.map +1 -0
  71. package/dist/transitions/dreamy-zoom.js +66 -0
  72. package/dist/transitions/dreamy-zoom.js.map +1 -0
  73. package/dist/transitions/dreamy.d.ts +8 -0
  74. package/dist/transitions/dreamy.d.ts.map +1 -0
  75. package/dist/transitions/dreamy.js +26 -0
  76. package/dist/transitions/dreamy.js.map +1 -0
  77. package/dist/transitions/drip-wipe.d.ts +22 -0
  78. package/dist/transitions/drip-wipe.d.ts.map +1 -0
  79. package/dist/transitions/drip-wipe.js +109 -0
  80. package/dist/transitions/drip-wipe.js.map +1 -0
  81. package/dist/transitions/ember-scatter.d.ts +22 -0
  82. package/dist/transitions/ember-scatter.d.ts.map +1 -0
  83. package/dist/transitions/ember-scatter.js +94 -0
  84. package/dist/transitions/ember-scatter.js.map +1 -0
  85. package/dist/transitions/film-burn.d.ts +17 -0
  86. package/dist/transitions/film-burn.d.ts.map +1 -0
  87. package/dist/transitions/film-burn.js +101 -0
  88. package/dist/transitions/film-burn.js.map +1 -0
  89. package/dist/transitions/film-grain.d.ts +24 -0
  90. package/dist/transitions/film-grain.d.ts.map +1 -0
  91. package/dist/transitions/film-grain.js +78 -0
  92. package/dist/transitions/film-grain.js.map +1 -0
  93. package/dist/transitions/flow-warp.d.ts +20 -0
  94. package/dist/transitions/flow-warp.d.ts.map +1 -0
  95. package/dist/transitions/flow-warp.js +51 -0
  96. package/dist/transitions/flow-warp.js.map +1 -0
  97. package/dist/transitions/fluid-flow.d.ts +26 -0
  98. package/dist/transitions/fluid-flow.d.ts.map +1 -0
  99. package/dist/transitions/fluid-flow.js +94 -0
  100. package/dist/transitions/fluid-flow.js.map +1 -0
  101. package/dist/transitions/fly-eye.d.ts.map +1 -0
  102. package/dist/transitions/fly-eye.js +58 -0
  103. package/dist/transitions/fly-eye.js.map +1 -0
  104. package/dist/transitions/glass-shatter.d.ts +25 -0
  105. package/dist/transitions/glass-shatter.d.ts.map +1 -0
  106. package/dist/transitions/glass-shatter.js +86 -0
  107. package/dist/transitions/glass-shatter.js.map +1 -0
  108. package/dist/transitions/glitch.d.ts +6 -0
  109. package/dist/transitions/glitch.d.ts.map +1 -0
  110. package/dist/transitions/glitch.js +59 -0
  111. package/dist/transitions/glitch.js.map +1 -0
  112. package/dist/transitions/god-rays-reveal.d.ts +32 -0
  113. package/dist/transitions/god-rays-reveal.d.ts.map +1 -0
  114. package/dist/transitions/god-rays-reveal.js +110 -0
  115. package/dist/transitions/god-rays-reveal.js.map +1 -0
  116. package/dist/transitions/gravity-pull.d.ts +16 -0
  117. package/dist/transitions/gravity-pull.d.ts.map +1 -0
  118. package/dist/transitions/gravity-pull.js +65 -0
  119. package/dist/transitions/gravity-pull.js.map +1 -0
  120. package/dist/transitions/grid-reveal.d.ts +12 -0
  121. package/dist/transitions/grid-reveal.d.ts.map +1 -0
  122. package/dist/transitions/grid-reveal.js +53 -0
  123. package/dist/transitions/grid-reveal.js.map +1 -0
  124. package/dist/transitions/heat-haze.d.ts +13 -0
  125. package/dist/transitions/heat-haze.d.ts.map +1 -0
  126. package/dist/transitions/heat-haze.js +51 -0
  127. package/dist/transitions/heat-haze.js.map +1 -0
  128. package/dist/transitions/index.d.ts +64 -0
  129. package/dist/transitions/index.d.ts.map +1 -0
  130. package/dist/transitions/index.js +63 -0
  131. package/dist/transitions/index.js.map +1 -0
  132. package/dist/transitions/ink-bloom.d.ts +15 -0
  133. package/dist/transitions/ink-bloom.d.ts.map +1 -0
  134. package/dist/transitions/ink-bloom.js +76 -0
  135. package/dist/transitions/ink-bloom.js.map +1 -0
  136. package/dist/transitions/ink-diffuse.d.ts +26 -0
  137. package/dist/transitions/ink-diffuse.d.ts.map +1 -0
  138. package/dist/transitions/ink-diffuse.js +90 -0
  139. package/dist/transitions/ink-diffuse.js.map +1 -0
  140. package/dist/transitions/iris-zoom.d.ts +13 -0
  141. package/dist/transitions/iris-zoom.d.ts.map +1 -0
  142. package/dist/transitions/iris-zoom.js +67 -0
  143. package/dist/transitions/iris-zoom.js.map +1 -0
  144. package/dist/transitions/kinetic-bands.d.ts +7 -0
  145. package/dist/transitions/kinetic-bands.d.ts.map +1 -0
  146. package/dist/transitions/kinetic-bands.js +58 -0
  147. package/dist/transitions/kinetic-bands.js.map +1 -0
  148. package/dist/transitions/lenticular-flip.d.ts +22 -0
  149. package/dist/transitions/lenticular-flip.d.ts.map +1 -0
  150. package/dist/transitions/lenticular-flip.js +73 -0
  151. package/dist/transitions/lenticular-flip.js.map +1 -0
  152. package/dist/transitions/light-leak.d.ts +7 -0
  153. package/dist/transitions/light-leak.d.ts.map +1 -0
  154. package/dist/transitions/light-leak.js +43 -0
  155. package/dist/transitions/light-leak.js.map +1 -0
  156. package/dist/transitions/lightning-strike.d.ts +22 -0
  157. package/dist/transitions/lightning-strike.d.ts.map +1 -0
  158. package/dist/transitions/lightning-strike.js +90 -0
  159. package/dist/transitions/lightning-strike.js.map +1 -0
  160. package/dist/transitions/linear-blur.d.ts +11 -0
  161. package/dist/transitions/linear-blur.d.ts.map +1 -0
  162. package/dist/transitions/linear-blur.js +40 -0
  163. package/dist/transitions/linear-blur.js.map +1 -0
  164. package/dist/transitions/liquid-chrome.d.ts +22 -0
  165. package/dist/transitions/liquid-chrome.d.ts.map +1 -0
  166. package/dist/transitions/liquid-chrome.js +114 -0
  167. package/dist/transitions/liquid-chrome.js.map +1 -0
  168. package/dist/transitions/liquid-morph.d.ts +6 -0
  169. package/dist/transitions/liquid-morph.d.ts.map +1 -0
  170. package/dist/transitions/liquid-morph.js +50 -0
  171. package/dist/transitions/liquid-morph.js.map +1 -0
  172. package/dist/transitions/lumina-melt.d.ts +11 -0
  173. package/dist/transitions/lumina-melt.d.ts.map +1 -0
  174. package/dist/transitions/lumina-melt.js +36 -0
  175. package/dist/transitions/lumina-melt.js.map +1 -0
  176. package/dist/transitions/mosaic.d.ts +13 -0
  177. package/dist/transitions/mosaic.d.ts.map +1 -0
  178. package/dist/transitions/mosaic.js +58 -0
  179. package/dist/transitions/mosaic.js.map +1 -0
  180. package/dist/transitions/noise-dissolve.d.ts +5 -0
  181. package/dist/transitions/noise-dissolve.d.ts.map +1 -0
  182. package/dist/transitions/noise-dissolve.js +41 -0
  183. package/dist/transitions/noise-dissolve.js.map +1 -0
  184. package/dist/transitions/page-curl.d.ts +32 -0
  185. package/dist/transitions/page-curl.d.ts.map +1 -0
  186. package/dist/transitions/page-curl.js +165 -0
  187. package/dist/transitions/page-curl.js.map +1 -0
  188. package/dist/transitions/paint-bleed.d.ts +12 -0
  189. package/dist/transitions/paint-bleed.d.ts.map +1 -0
  190. package/dist/transitions/paint-bleed.js +59 -0
  191. package/dist/transitions/paint-bleed.js.map +1 -0
  192. package/dist/transitions/particle-assemble.d.ts +23 -0
  193. package/dist/transitions/particle-assemble.d.ts.map +1 -0
  194. package/dist/transitions/particle-assemble.js +65 -0
  195. package/dist/transitions/particle-assemble.js.map +1 -0
  196. package/dist/transitions/pinwheel.d.ts +11 -0
  197. package/dist/transitions/pinwheel.d.ts.map +1 -0
  198. package/dist/transitions/pinwheel.js +40 -0
  199. package/dist/transitions/pinwheel.js.map +1 -0
  200. package/dist/transitions/pixelate.d.ts +4 -0
  201. package/dist/transitions/pixelate.d.ts.map +1 -0
  202. package/dist/transitions/pixelate.js +26 -0
  203. package/dist/transitions/pixelate.js.map +1 -0
  204. package/dist/transitions/plasma-pulse.d.ts.map +1 -0
  205. package/dist/transitions/plasma-pulse.js +92 -0
  206. package/dist/transitions/plasma-pulse.js.map +1 -0
  207. package/dist/transitions/polka-dots-curtain.d.ts +13 -0
  208. package/dist/transitions/polka-dots-curtain.d.ts.map +1 -0
  209. package/dist/transitions/polka-dots-curtain.js +46 -0
  210. package/dist/transitions/polka-dots-curtain.js.map +1 -0
  211. package/dist/transitions/polygon-flip.d.ts +27 -0
  212. package/dist/transitions/polygon-flip.d.ts.map +1 -0
  213. package/dist/transitions/polygon-flip.js +97 -0
  214. package/dist/transitions/polygon-flip.js.map +1 -0
  215. package/dist/transitions/portal-dive.d.ts +18 -0
  216. package/dist/transitions/portal-dive.d.ts.map +1 -0
  217. package/dist/transitions/portal-dive.js +83 -0
  218. package/dist/transitions/portal-dive.js.map +1 -0
  219. package/dist/transitions/prism-split.d.ts +12 -0
  220. package/dist/transitions/prism-split.d.ts.map +1 -0
  221. package/dist/transitions/prism-split.js +52 -0
  222. package/dist/transitions/prism-split.js.map +1 -0
  223. package/dist/transitions/push.d.ts +4 -0
  224. package/dist/transitions/push.d.ts.map +1 -0
  225. package/dist/transitions/push.js +34 -0
  226. package/dist/transitions/push.js.map +1 -0
  227. package/dist/transitions/radial-reveal.d.ts +5 -0
  228. package/dist/transitions/radial-reveal.d.ts.map +1 -0
  229. package/dist/transitions/radial-reveal.js +31 -0
  230. package/dist/transitions/radial-reveal.js.map +1 -0
  231. package/dist/transitions/ripple-wave.d.ts +24 -0
  232. package/dist/transitions/ripple-wave.d.ts.map +1 -0
  233. package/dist/transitions/ripple-wave.js +135 -0
  234. package/dist/transitions/ripple-wave.js.map +1 -0
  235. package/dist/transitions/ripple.d.ts +7 -0
  236. package/dist/transitions/ripple.d.ts.map +1 -0
  237. package/dist/transitions/ripple.js +43 -0
  238. package/dist/transitions/ripple.js.map +1 -0
  239. package/dist/transitions/shape-reveal.d.ts +12 -0
  240. package/dist/transitions/shape-reveal.d.ts.map +1 -0
  241. package/dist/transitions/shape-reveal.js +53 -0
  242. package/dist/transitions/shape-reveal.js.map +1 -0
  243. package/dist/transitions/shockwave.d.ts +10 -0
  244. package/dist/transitions/shockwave.d.ts.map +1 -0
  245. package/dist/transitions/shockwave.js +56 -0
  246. package/dist/transitions/shockwave.js.map +1 -0
  247. package/dist/transitions/singularity.d.ts +22 -0
  248. package/dist/transitions/singularity.d.ts.map +1 -0
  249. package/dist/transitions/singularity.js +59 -0
  250. package/dist/transitions/singularity.js.map +1 -0
  251. package/dist/transitions/slide.d.ts +10 -0
  252. package/dist/transitions/slide.d.ts.map +1 -0
  253. package/dist/transitions/slide.js +76 -0
  254. package/dist/transitions/slide.js.map +1 -0
  255. package/dist/transitions/smoldering-edge.d.ts +15 -0
  256. package/dist/transitions/smoldering-edge.d.ts.map +1 -0
  257. package/dist/transitions/smoldering-edge.js +74 -0
  258. package/dist/transitions/smoldering-edge.js.map +1 -0
  259. package/dist/transitions/split.d.ts +14 -0
  260. package/dist/transitions/split.d.ts.map +1 -0
  261. package/dist/transitions/split.js +53 -0
  262. package/dist/transitions/split.js.map +1 -0
  263. package/dist/transitions/swirl.d.ts +12 -0
  264. package/dist/transitions/swirl.d.ts.map +1 -0
  265. package/dist/transitions/swirl.js +42 -0
  266. package/dist/transitions/swirl.js.map +1 -0
  267. package/dist/transitions/tangent-motion-blur.d.ts +13 -0
  268. package/dist/transitions/tangent-motion-blur.d.ts.map +1 -0
  269. package/dist/transitions/tangent-motion-blur.js +58 -0
  270. package/dist/transitions/tangent-motion-blur.js.map +1 -0
  271. package/dist/transitions/tile-scatter.d.ts +22 -0
  272. package/dist/transitions/tile-scatter.d.ts.map +1 -0
  273. package/dist/transitions/tile-scatter.js +122 -0
  274. package/dist/transitions/tile-scatter.js.map +1 -0
  275. package/dist/transitions/tilt-sweep.d.ts +11 -0
  276. package/dist/transitions/tilt-sweep.d.ts.map +1 -0
  277. package/dist/transitions/tilt-sweep.js +31 -0
  278. package/dist/transitions/tilt-sweep.js.map +1 -0
  279. package/dist/transitions/warp-zoom.d.ts +7 -0
  280. package/dist/transitions/warp-zoom.d.ts.map +1 -0
  281. package/dist/transitions/warp-zoom.js +70 -0
  282. package/dist/transitions/warp-zoom.js.map +1 -0
  283. package/dist/transitions/wave-stripes.d.ts +16 -0
  284. package/dist/transitions/wave-stripes.d.ts.map +1 -0
  285. package/dist/transitions/wave-stripes.js +55 -0
  286. package/dist/transitions/wave-stripes.js.map +1 -0
  287. package/dist/transitions/wave-wipe.d.ts +23 -0
  288. package/dist/transitions/wave-wipe.d.ts.map +1 -0
  289. package/dist/transitions/wave-wipe.js +84 -0
  290. package/dist/transitions/wave-wipe.js.map +1 -0
  291. package/dist/transitions/wind.d.ts +9 -0
  292. package/dist/transitions/wind.d.ts.map +1 -0
  293. package/dist/transitions/wind.js +48 -0
  294. package/dist/transitions/wind.js.map +1 -0
  295. package/dist/transitions/window-slice.d.ts +11 -0
  296. package/dist/transitions/window-slice.d.ts.map +1 -0
  297. package/dist/transitions/window-slice.js +34 -0
  298. package/dist/transitions/window-slice.js.map +1 -0
  299. package/dist/transitions/wipe-directional.d.ts +5 -0
  300. package/dist/transitions/wipe-directional.d.ts.map +1 -0
  301. package/dist/transitions/wipe-directional.js +26 -0
  302. package/dist/transitions/wipe-directional.js.map +1 -0
  303. package/dist/types.d.ts +78 -0
  304. package/dist/types.d.ts.map +1 -0
  305. package/dist/types.js +2 -0
  306. package/dist/types.js.map +1 -0
  307. package/package.json +56 -0
  308. package/src/__tests__/__screenshots__/endpoint-correctness.test.ts/endpoint-correctness---every-transition-must-be-pixel-pure-from-to-at-progress-0-1-rippleWave-at-progress-0-is-pure--from--1.png +0 -0
  309. package/src/__tests__/__screenshots__/endpoint-correctness.test.ts/endpoint-correctness---every-transition-must-be-pixel-pure-from-to-at-progress-0-1-rippleWave-at-progress-1-is-pure--to--1.png +0 -0
  310. package/src/__tests__/endpoint-correctness.test.ts +161 -0
  311. package/src/__tests__/readme.test.ts +39 -0
  312. package/src/__tests__/ssr.test.ts +45 -0
  313. package/src/index.ts +75 -0
  314. package/src/inputs/normalize.ts +1 -0
  315. package/src/runner/__tests__/__screenshots__/mesh.test.ts/buildSubdividedPlane-centroid-is-shared-across-a-face-and-equals-mean-of-its-3-positions-1.png +0 -0
  316. package/src/runner/__tests__/__screenshots__/mesh.test.ts/buildSubdividedPlane-uv-matches-aPosition---0-5---0-5-1.png +0 -0
  317. package/src/runner/__tests__/mesh.test.ts +128 -0
  318. package/src/runner/gl.ts +69 -0
  319. package/src/runner/mesh.ts +135 -0
  320. package/src/runner/runner.ts +528 -0
  321. package/src/transitions/all.ts +131 -0
  322. package/src/transitions/bloom-reveal.ts +106 -0
  323. package/src/transitions/chromatic-pulse.ts +40 -0
  324. package/src/transitions/clock-wipe.ts +33 -0
  325. package/src/transitions/color-phase.ts +24 -0
  326. package/src/transitions/cross-zoom.ts +59 -0
  327. package/src/transitions/crosshatch.ts +49 -0
  328. package/src/transitions/define.ts +56 -0
  329. package/src/transitions/directional-burn.ts +67 -0
  330. package/src/transitions/directional-warp.ts +46 -0
  331. package/src/transitions/dissolve.ts +16 -0
  332. package/src/transitions/dreamy-zoom.ts +66 -0
  333. package/src/transitions/dreamy.ts +26 -0
  334. package/src/transitions/drip-wipe.ts +109 -0
  335. package/src/transitions/ember-scatter.ts +94 -0
  336. package/src/transitions/film-burn.ts +101 -0
  337. package/src/transitions/film-grain.ts +78 -0
  338. package/src/transitions/flow-warp.ts +51 -0
  339. package/src/transitions/fluid-flow.ts +94 -0
  340. package/src/transitions/glass-shatter.ts +86 -0
  341. package/src/transitions/glitch.ts +59 -0
  342. package/src/transitions/god-rays-reveal.ts +110 -0
  343. package/src/transitions/gravity-pull.ts +65 -0
  344. package/src/transitions/grid-reveal.ts +53 -0
  345. package/src/transitions/heat-haze.ts +51 -0
  346. package/src/transitions/index.ts +63 -0
  347. package/src/transitions/ink-bloom.ts +76 -0
  348. package/src/transitions/ink-diffuse.ts +90 -0
  349. package/src/transitions/iris-zoom.ts +67 -0
  350. package/src/transitions/kinetic-bands.ts +58 -0
  351. package/src/transitions/lenticular-flip.ts +73 -0
  352. package/src/transitions/light-leak.ts +43 -0
  353. package/src/transitions/linear-blur.ts +40 -0
  354. package/src/transitions/liquid-chrome.ts +114 -0
  355. package/src/transitions/liquid-morph.ts +50 -0
  356. package/src/transitions/lumina-melt.ts +36 -0
  357. package/src/transitions/mosaic.ts +58 -0
  358. package/src/transitions/noise-dissolve.ts +41 -0
  359. package/src/transitions/page-curl.ts +165 -0
  360. package/src/transitions/paint-bleed.ts +59 -0
  361. package/src/transitions/pinwheel.ts +40 -0
  362. package/src/transitions/pixelate.ts +26 -0
  363. package/src/transitions/polka-dots-curtain.ts +46 -0
  364. package/src/transitions/polygon-flip.ts +97 -0
  365. package/src/transitions/portal-dive.ts +83 -0
  366. package/src/transitions/prism-split.ts +52 -0
  367. package/src/transitions/push.ts +34 -0
  368. package/src/transitions/radial-reveal.ts +31 -0
  369. package/src/transitions/ripple-wave.ts +135 -0
  370. package/src/transitions/ripple.ts +43 -0
  371. package/src/transitions/shape-reveal.ts +53 -0
  372. package/src/transitions/shockwave.ts +56 -0
  373. package/src/transitions/singularity.ts +59 -0
  374. package/src/transitions/slide.ts +76 -0
  375. package/src/transitions/smoldering-edge.ts +74 -0
  376. package/src/transitions/split.ts +53 -0
  377. package/src/transitions/swirl.ts +42 -0
  378. package/src/transitions/tangent-motion-blur.ts +58 -0
  379. package/src/transitions/tile-scatter.ts +122 -0
  380. package/src/transitions/warp-zoom.ts +70 -0
  381. package/src/transitions/wave-stripes.ts +55 -0
  382. package/src/transitions/wind.ts +48 -0
  383. package/src/transitions/wipe-directional.ts +26 -0
  384. package/src/types.ts +87 -0
@@ -0,0 +1,161 @@
1
+ import { beforeAll, afterAll, describe, expect, it } from "vitest";
2
+ import {
3
+ ALL_TRANSITIONS,
4
+ Runner,
5
+ directionalBurn,
6
+ directionalWarp,
7
+ flowWarp,
8
+ kineticBands,
9
+ lightLeak,
10
+ paintBleed,
11
+ slide,
12
+ smolderingEdge,
13
+ tangentMotionBlur,
14
+ wind,
15
+ wipeDirectional,
16
+ type Transition,
17
+ type UniformParams,
18
+ } from "../index.js";
19
+
20
+ const SIZE = 32;
21
+ const TOLERANCE = 2;
22
+
23
+ function makeSolid(r: number, g: number, b: number): HTMLCanvasElement {
24
+ const canvas = document.createElement("canvas");
25
+ canvas.width = SIZE;
26
+ canvas.height = SIZE;
27
+ const ctx = canvas.getContext("2d");
28
+ if (!ctx) throw new Error("2D context unavailable");
29
+ ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
30
+ ctx.fillRect(0, 0, SIZE, SIZE);
31
+ return canvas;
32
+ }
33
+
34
+ function readPixels(runner: Runner): Uint8Array {
35
+ const pixels = new Uint8Array(SIZE * SIZE * 4);
36
+ runner.gl.readPixels(
37
+ 0,
38
+ 0,
39
+ SIZE,
40
+ SIZE,
41
+ runner.gl.RGBA,
42
+ runner.gl.UNSIGNED_BYTE,
43
+ pixels,
44
+ );
45
+ return pixels;
46
+ }
47
+
48
+ function assertAllPixelsMatch(
49
+ pixels: Uint8Array,
50
+ expected: [number, number, number],
51
+ tolerance = TOLERANCE,
52
+ ): void {
53
+ let worstDiff = 0;
54
+ let worstIndex = -1;
55
+ for (let i = 0; i < pixels.length; i += 4) {
56
+ for (let c = 0; c < 3; c++) {
57
+ const diff = Math.abs((pixels[i + c] ?? 0) - (expected[c] ?? 0));
58
+ if (diff > worstDiff) {
59
+ worstDiff = diff;
60
+ worstIndex = i;
61
+ }
62
+ }
63
+ }
64
+ if (worstDiff > tolerance) {
65
+ const actual = [
66
+ pixels[worstIndex] ?? 0,
67
+ pixels[worstIndex + 1] ?? 0,
68
+ pixels[worstIndex + 2] ?? 0,
69
+ ];
70
+ expect.fail(
71
+ `Pixel at index ${worstIndex} = [${actual.join(", ")}], expected ≈ [${expected.join(", ")}] (max diff ${worstDiff})`,
72
+ );
73
+ }
74
+ }
75
+
76
+ // Source of truth for the catalog: ALL_TRANSITIONS from the package.
77
+ // Adding a new transition to the package's index automatically picks it
78
+ // up here.
79
+ const TRANSITIONS = ALL_TRANSITIONS;
80
+
81
+ describe("endpoint correctness — every transition must be pixel-pure from/to at progress 0/1", () => {
82
+ let runner: Runner;
83
+ let from: HTMLCanvasElement;
84
+ let to: HTMLCanvasElement;
85
+
86
+ beforeAll(() => {
87
+ const canvas = document.createElement("canvas");
88
+ canvas.width = SIZE;
89
+ canvas.height = SIZE;
90
+ runner = new Runner({
91
+ canvas,
92
+ contextAttributes: { preserveDrawingBuffer: true },
93
+ });
94
+ from = makeSolid(255, 0, 0);
95
+ to = makeSolid(0, 0, 255);
96
+ });
97
+
98
+ afterAll(() => {
99
+ runner.dispose();
100
+ });
101
+
102
+ for (const transition of TRANSITIONS) {
103
+ it(`${transition.name} at progress=0 is pure "from"`, () => {
104
+ runner.render(transition, { from, to, progress: 0 });
105
+ assertAllPixelsMatch(readPixels(runner), [255, 0, 0]);
106
+ });
107
+
108
+ it(`${transition.name} at progress=1 is pure "to"`, () => {
109
+ runner.render(transition, { from, to, progress: 1 });
110
+ assertAllPixelsMatch(readPixels(runner), [0, 0, 255]);
111
+ });
112
+ }
113
+
114
+ // Regression: TextureCache.resolve() calls gl.bindTexture() against the
115
+ // active unit; if the runner interleaves resolves with activeTexture()
116
+ // calls, a supplied displacement source can clobber unit 1 (uTo). Prior
117
+ // tests passed because they never supplied a displacement and hit the
118
+ // default 1×1 path that skips resolve().
119
+ it("endpoints stay correct when a displacement source is supplied", () => {
120
+ const displacement = makeSolid(200, 80, 40);
121
+ runner.render(flowWarp, { from, to, progress: 0, displacement });
122
+ assertAllPixelsMatch(readPixels(runner), [255, 0, 0]);
123
+ runner.render(flowWarp, { from, to, progress: 1, displacement });
124
+ assertAllPixelsMatch(readPixels(runner), [0, 0, 255]);
125
+ });
126
+
127
+ // Regression: shaders that compute `0.5 + dot(uv - 0.5, dir)` without
128
+ // dividing by maxProj = (|dx| + |dy|)/2 produce a sliver of the previous
129
+ // image at the corners when direction is non-axis-aligned. These tests
130
+ // exercise the diagonal case so the fix can't silently regress.
131
+ const DIAGONAL_DIRECTIONAL: ReadonlyArray<[string, Transition<UniformParams>]> = [
132
+ ["slide", slide as Transition<UniformParams>],
133
+ ["paintBleed", paintBleed as Transition<UniformParams>],
134
+ ["kineticBands", kineticBands as Transition<UniformParams>],
135
+ ["lightLeak", lightLeak as Transition<UniformParams>],
136
+ ["tangentMotionBlur", tangentMotionBlur as Transition<UniformParams>],
137
+ ["wind", wind as Transition<UniformParams>],
138
+ ["directionalBurn", directionalBurn as Transition<UniformParams>],
139
+ ["smolderingEdge", smolderingEdge as Transition<UniformParams>],
140
+ ["directionalWarp", directionalWarp as Transition<UniformParams>],
141
+ ];
142
+ for (const [name, transition] of DIAGONAL_DIRECTIONAL) {
143
+ it(`${name} endpoints correct with diagonal direction [1, 1]`, () => {
144
+ const params = { direction: [1, 1] as const };
145
+ runner.render(transition, { from, to, progress: 0, params });
146
+ assertAllPixelsMatch(readPixels(runner), [255, 0, 0]);
147
+ runner.render(transition, { from, to, progress: 1, params });
148
+ assertAllPixelsMatch(readPixels(runner), [0, 0, 255]);
149
+ });
150
+ }
151
+
152
+ // wipe-directional uses an angle (radians), not a vec2 — but the same
153
+ // gradient pattern. Verify diagonal angle (π/4 = 45°) is endpoint-correct.
154
+ it("wipeDirectional endpoints correct at 45° (diagonal angle)", () => {
155
+ const params = { angle: Math.PI / 4 };
156
+ runner.render(wipeDirectional, { from, to, progress: 0, params });
157
+ assertAllPixelsMatch(readPixels(runner), [255, 0, 0]);
158
+ runner.render(wipeDirectional, { from, to, progress: 1, params });
159
+ assertAllPixelsMatch(readPixels(runner), [0, 0, 255]);
160
+ });
161
+ });
@@ -0,0 +1,39 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ import { dirname, join } from "node:path";
4
+ import { describe, expect, it } from "vitest";
5
+ import { ALL_TRANSITIONS } from "../index.js";
6
+
7
+ /**
8
+ * Guard against the README's hardcoded transition count drifting from
9
+ * the actual catalog. If the count changes, this test fails on every
10
+ * phrase that still embeds the stale number, so the README copy is
11
+ * forced into sync with `ALL_TRANSITIONS`.
12
+ *
13
+ * Add a phrase here when you write a new "N transitions" sentence in
14
+ * the README. The list is the contract.
15
+ */
16
+
17
+ const README_PATH = join(
18
+ dirname(fileURLToPath(import.meta.url)),
19
+ "..",
20
+ "..",
21
+ "README.md",
22
+ );
23
+
24
+ const COUNT_PHRASES = (n: number) => [
25
+ `${n} WebGL2 transition shaders`,
26
+ `Runner + all ${n} transitions`,
27
+ `Runner + all ${n})`,
28
+ ];
29
+
30
+ describe("README catalog count stays in sync with ALL_TRANSITIONS", () => {
31
+ const readme = readFileSync(README_PATH, "utf-8");
32
+ const expected = ALL_TRANSITIONS.length;
33
+
34
+ for (const phrase of COUNT_PHRASES(expected)) {
35
+ it(`README contains "${phrase}"`, () => {
36
+ expect(readme).toContain(phrase);
37
+ });
38
+ }
39
+ });
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ /**
4
+ * SSR safety: the module graph must load in Node without DOM globals,
5
+ * because @vysmo/transitions is loaded by SSR frameworks (Astro, Next,
6
+ * etc.) at build time.
7
+ *
8
+ * Constructing a `Runner` requires a live WebGL2 context — that's
9
+ * exercised in the browser tests. This file only asserts that
10
+ * *importing* the module + reading the pure-data exports works without
11
+ * `window` / `WebGLTexture` / `HTMLCanvasElement` defined.
12
+ */
13
+ describe("SSR safety", () => {
14
+ it("window is undefined in this runtime", () => {
15
+ expect(typeof window).toBe("undefined");
16
+ });
17
+
18
+ it("module loads without DOM globals", async () => {
19
+ const mod = await import("../index.js");
20
+ expect(mod).toBeDefined();
21
+ expect(typeof mod.Runner).toBe("function");
22
+ expect(typeof mod.defineTransition).toBe("function");
23
+ });
24
+
25
+ it("transitions are plain-data and safe to read in Node", async () => {
26
+ const { dissolve, crossZoom, pageCurl } = await import("../index.js");
27
+ expect(dissolve.name).toBe("dissolve");
28
+ expect(typeof dissolve.shader.glsl).toBe("string");
29
+ expect(dissolve.defaults).toBeDefined();
30
+ expect(crossZoom.name).toBe("cross-zoom");
31
+ expect(pageCurl.mesh).toBeDefined();
32
+ });
33
+
34
+ it("defineTransition is pure and runs in Node", async () => {
35
+ const { defineTransition } = await import("../index.js");
36
+ const t = defineTransition({
37
+ name: "test/passthrough",
38
+ glsl: "vec4 transition(vec2 uv) { return mix(getFromColor(uv), getToColor(uv), uProgress); }",
39
+ defaults: { strength: 1 },
40
+ });
41
+ expect(t.name).toBe("test/passthrough");
42
+ expect(t.defaults.strength).toBe(1);
43
+ expect(t.shader.glsl).toContain("transition");
44
+ });
45
+ });
package/src/index.ts ADDED
@@ -0,0 +1,75 @@
1
+ export { Runner } from "./runner/runner.js";
2
+ export type { RunnerOptions } from "./runner/runner.js";
3
+ export {
4
+ dissolve,
5
+ wipeDirectional,
6
+ slide,
7
+ radialReveal,
8
+ crossZoom,
9
+ glitch,
10
+ noiseDissolve,
11
+ clockWipe,
12
+ ripple,
13
+ pixelate,
14
+ kineticBands,
15
+ lightLeak,
16
+ liquidMorph,
17
+ gridReveal,
18
+ warpZoom,
19
+ chromaticPulse,
20
+ push,
21
+ shapeReveal,
22
+ paintBleed,
23
+ shockwave,
24
+ swirl,
25
+ split,
26
+ directionalWarp,
27
+ wind,
28
+ linearBlur,
29
+ luminaMelt,
30
+ pinwheel,
31
+ dreamy,
32
+ tangentMotionBlur,
33
+ colorPhase,
34
+ filmBurn,
35
+ mosaic,
36
+ emberScatter,
37
+ directionalBurn,
38
+ inkBloom,
39
+ smolderingEdge,
40
+ polkaDotsCurtain,
41
+ crosshatch,
42
+ dreamyZoom,
43
+ heatHaze,
44
+ prismSplit,
45
+ irisZoom,
46
+ gravityPull,
47
+ waveStripes,
48
+ flowWarp,
49
+ dripWipe,
50
+ portalDive,
51
+ liquidChrome,
52
+ glassShatter,
53
+ inkDiffuse,
54
+ bloomReveal,
55
+ godRaysReveal,
56
+ fluidFlow,
57
+ lenticularFlip,
58
+ filmGrain,
59
+ singularity,
60
+ polygonFlip,
61
+ pageCurl,
62
+ rippleWave,
63
+ tileScatter,
64
+ defineTransition,
65
+ ALL_TRANSITIONS,
66
+ } from "./transitions/index.js";
67
+ export type { DefineTransitionSpec } from "./transitions/index.js";
68
+ export type {
69
+ RenderArgs,
70
+ TextureSource,
71
+ Transition,
72
+ TransitionShader,
73
+ UniformParams,
74
+ UniformValue,
75
+ } from "./types.js";
@@ -0,0 +1 @@
1
+ export { TextureCache } from "@vysmo/gl-core";
@@ -0,0 +1,128 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildSubdividedPlane } from "../mesh.js";
3
+
4
+ describe("buildSubdividedPlane", () => {
5
+ it("produces 2·nx·ny triangles and 3 vertices per face", () => {
6
+ const mesh = buildSubdividedPlane(3, 4);
7
+ expect(mesh.vertexCount).toBe(3 * 4 * 2 * 3);
8
+ expect(mesh.position.length).toBe(mesh.vertexCount * 2);
9
+ expect(mesh.uv.length).toBe(mesh.vertexCount * 2);
10
+ expect(mesh.offset.length).toBe(mesh.vertexCount);
11
+ expect(mesh.centroid.length).toBe(mesh.vertexCount * 2);
12
+ expect(mesh.bary.length).toBe(mesh.vertexCount * 3);
13
+ expect(mesh.random.length).toBe(mesh.vertexCount);
14
+ });
15
+
16
+ it("positions span [-1, 1] in clip space on both axes", () => {
17
+ const mesh = buildSubdividedPlane(5, 5);
18
+ let minX = Infinity;
19
+ let minY = Infinity;
20
+ let maxX = -Infinity;
21
+ let maxY = -Infinity;
22
+ for (let i = 0; i < mesh.position.length; i += 2) {
23
+ const x = mesh.position[i] as number;
24
+ const y = mesh.position[i + 1] as number;
25
+ if (x < minX) minX = x;
26
+ if (x > maxX) maxX = x;
27
+ if (y < minY) minY = y;
28
+ if (y > maxY) maxY = y;
29
+ }
30
+ expect(minX).toBeCloseTo(-1, 5);
31
+ expect(maxX).toBeCloseTo(1, 5);
32
+ expect(minY).toBeCloseTo(-1, 5);
33
+ expect(maxY).toBeCloseTo(1, 5);
34
+ });
35
+
36
+ it("uv matches aPosition · 0.5 + 0.5", () => {
37
+ const mesh = buildSubdividedPlane(3, 3);
38
+ for (let i = 0; i < mesh.vertexCount; i++) {
39
+ const px = mesh.position[i * 2] as number;
40
+ const py = mesh.position[i * 2 + 1] as number;
41
+ const u = mesh.uv[i * 2] as number;
42
+ const v = mesh.uv[i * 2 + 1] as number;
43
+ expect(u).toBeCloseTo(px * 0.5 + 0.5, 5);
44
+ expect(v).toBeCloseTo(py * 0.5 + 0.5, 5);
45
+ }
46
+ });
47
+
48
+ it("bary sums to 1 on every vertex and covers all three corners per face", () => {
49
+ const mesh = buildSubdividedPlane(2, 2);
50
+ for (let f = 0; f < mesh.vertexCount / 3; f++) {
51
+ const seen = new Set<string>();
52
+ for (let k = 0; k < 3; k++) {
53
+ const base = (f * 3 + k) * 3;
54
+ const b0 = mesh.bary[base] as number;
55
+ const b1 = mesh.bary[base + 1] as number;
56
+ const b2 = mesh.bary[base + 2] as number;
57
+ expect(b0 + b1 + b2).toBeCloseTo(1, 5);
58
+ seen.add(`${b0},${b1},${b2}`);
59
+ }
60
+ expect(seen.size).toBe(3);
61
+ }
62
+ });
63
+
64
+ it("offset is shared across all 6 vertices of a quad (per-quad)", () => {
65
+ const mesh = buildSubdividedPlane(4, 3);
66
+ const vertsPerQuad = 6;
67
+ const quadCount = mesh.vertexCount / vertsPerQuad;
68
+ for (let q = 0; q < quadCount; q++) {
69
+ const v0 = mesh.offset[q * vertsPerQuad] as number;
70
+ for (let k = 1; k < vertsPerQuad; k++) {
71
+ expect(mesh.offset[q * vertsPerQuad + k]).toBe(v0);
72
+ }
73
+ }
74
+ });
75
+
76
+ it("offset is a deterministic permutation over quads covering [0, 1]", () => {
77
+ const mesh = buildSubdividedPlane(3, 3);
78
+ const vertsPerQuad = 6;
79
+ const quadCount = mesh.vertexCount / vertsPerQuad;
80
+ const quadOffsets: number[] = [];
81
+ for (let q = 0; q < quadCount; q++) {
82
+ quadOffsets.push(mesh.offset[q * vertsPerQuad] as number);
83
+ }
84
+ const sorted = [...quadOffsets].sort((a, b) => a - b);
85
+ const n = sorted.length;
86
+ expect(sorted[0]).toBe(0);
87
+ expect(sorted[n - 1]).toBe(1);
88
+ const unique = new Set(sorted);
89
+ expect(unique.size).toBe(n);
90
+
91
+ const again = buildSubdividedPlane(3, 3);
92
+ for (let i = 0; i < mesh.offset.length; i++) {
93
+ expect(again.offset[i]).toBe(mesh.offset[i]);
94
+ }
95
+ });
96
+
97
+ it("centroid is shared across a quad and equals the quad centre", () => {
98
+ const mesh = buildSubdividedPlane(2, 2);
99
+ const vertsPerQuad = 6;
100
+ const quadCount = mesh.vertexCount / vertsPerQuad;
101
+ for (let q = 0; q < quadCount; q++) {
102
+ const cx0 = mesh.centroid[q * vertsPerQuad * 2] as number;
103
+ const cy0 = mesh.centroid[q * vertsPerQuad * 2 + 1] as number;
104
+ // Every vertex of the quad has identical centroid.
105
+ for (let k = 1; k < vertsPerQuad; k++) {
106
+ expect(mesh.centroid[(q * vertsPerQuad + k) * 2]).toBeCloseTo(cx0, 5);
107
+ expect(mesh.centroid[(q * vertsPerQuad + k) * 2 + 1]).toBeCloseTo(cy0, 5);
108
+ }
109
+ // And the centroid equals the mean of the quad's 4 distinct corner positions.
110
+ const positions = new Set<string>();
111
+ for (let k = 0; k < vertsPerQuad; k++) {
112
+ const px = mesh.position[(q * vertsPerQuad + k) * 2] as number;
113
+ const py = mesh.position[(q * vertsPerQuad + k) * 2 + 1] as number;
114
+ positions.add(`${px},${py}`);
115
+ }
116
+ expect(positions.size).toBe(4);
117
+ let sx = 0;
118
+ let sy = 0;
119
+ for (const key of positions) {
120
+ const [xs, ys] = key.split(",");
121
+ sx += parseFloat(xs as string);
122
+ sy += parseFloat(ys as string);
123
+ }
124
+ expect(cx0).toBeCloseTo(sx / 4, 5);
125
+ expect(cy0).toBeCloseTo(sy / 4, 5);
126
+ }
127
+ });
128
+ });
@@ -0,0 +1,69 @@
1
+ export {
2
+ FULLSCREEN_VERTEX_SHADER as VERTEX_SHADER,
3
+ compileShader,
4
+ linkProgram,
5
+ buildProgram,
6
+ paramKeyToUniformName,
7
+ } from "@vysmo/gl-core";
8
+
9
+ export const MESH_VERTEX_SHELL_HEAD = `#version 300 es
10
+ precision highp float;
11
+ uniform float uProgress;
12
+ uniform vec2 uResolution;
13
+ uniform int uPass;
14
+ uniform int uPassCount;
15
+ uniform int uInstances;
16
+ in vec2 aPosition;
17
+ in vec2 aUv;
18
+ in float aOffset;
19
+ in vec2 aCentroid;
20
+ in vec3 aBary;
21
+ in float aRandom;
22
+ out vec2 vUv;
23
+
24
+ `;
25
+
26
+ export const FRAGMENT_SHELL_HEAD = `#version 300 es
27
+ precision highp float;
28
+ uniform sampler2D uFrom;
29
+ uniform sampler2D uTo;
30
+ uniform sampler2D uDisplacement;
31
+ uniform sampler2D uEnvironment;
32
+ uniform sampler2D uPrevious;
33
+ uniform float uProgress;
34
+ uniform vec2 uResolution;
35
+ uniform int uPass;
36
+ uniform int uPassCount;
37
+ uniform int uInstances;
38
+ in vec2 vUv;
39
+ out vec4 fragColor;
40
+
41
+ vec4 getFromColor(vec2 uv) { return texture(uFrom, uv); }
42
+ vec4 getToColor(vec2 uv) { return texture(uTo, uv); }
43
+ vec4 getDisplacement(vec2 uv) { return texture(uDisplacement, uv); }
44
+ vec4 getEnvironment(vec2 uv) { return texture(uEnvironment, uv); }
45
+ vec4 getPrevious(vec2 uv) { return texture(uPrevious, uv); }
46
+
47
+ // Reflect out-of-range UVs back into [0,1] instead of clamping to edge.
48
+ // Use this when a shader displaces UVs (warp, flow, etc.) and you want
49
+ // the displaced regions to sample real interior content rather than
50
+ // streaked edge texels.
51
+ vec2 mirrorUv(vec2 uv) {
52
+ return abs(mod(uv + 1.0, 2.0) - 1.0);
53
+ }
54
+
55
+ `;
56
+
57
+ export const FRAGMENT_SHELL_TAIL = `
58
+ void main() {
59
+ fragColor = transition(vUv);
60
+ }
61
+ `;
62
+
63
+ export function wrapFragmentShader(userBody: string): string {
64
+ return FRAGMENT_SHELL_HEAD + userBody + FRAGMENT_SHELL_TAIL;
65
+ }
66
+
67
+ export function wrapMeshVertexShader(userBody: string): string {
68
+ return MESH_VERTEX_SHELL_HEAD + userBody;
69
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Subdivided-plane geometry for mesh-based transitions.
3
+ *
4
+ * Output is unindexed (6 vertices per quad, 3 per triangle). Attributes
5
+ * are organised so the two triangles that form a quad share per-face
6
+ * data — a quad is the natural "face" unit for flip/scatter/fly effects
7
+ * because a square quad is self-symmetric under 180° rotation, whereas
8
+ * a right-isoceles triangle is not.
9
+ *
10
+ * Layout per vertex:
11
+ * aPosition — per-vertex, clip space [-1, 1]
12
+ * aUv — per-vertex, texture space [0, 1]
13
+ * aBary — per-vertex, barycentric within its triangle
14
+ * aCentroid — per-QUAD centre, shared by all 6 verts of a quad
15
+ * aOffset — per-QUAD deterministic shuffle in [0, 1]
16
+ * aRandom — per-QUAD independent hash in [0, 1]
17
+ */
18
+ export interface MeshBuffers {
19
+ vertexCount: number;
20
+ position: Float32Array;
21
+ uv: Float32Array;
22
+ offset: Float32Array;
23
+ centroid: Float32Array;
24
+ bary: Float32Array;
25
+ random: Float32Array;
26
+ }
27
+
28
+ const BARY_A: readonly [number, number, number] = [1, 0, 0];
29
+ const BARY_B: readonly [number, number, number] = [0, 1, 0];
30
+ const BARY_C: readonly [number, number, number] = [0, 0, 1];
31
+
32
+ function seededShuffledOffsets(n: number): Float32Array {
33
+ const out = new Float32Array(n);
34
+ if (n === 0) return out;
35
+ if (n === 1) {
36
+ out[0] = 0;
37
+ return out;
38
+ }
39
+ for (let i = 0; i < n; i++) out[i] = i / (n - 1);
40
+ // Park-Miller LCG, seeded deterministically.
41
+ let seed = 1337;
42
+ for (let i = n - 1; i > 0; i--) {
43
+ seed = (seed * 48271) % 0x7fffffff;
44
+ const j = seed % (i + 1);
45
+ const tmp = out[i] as number;
46
+ out[i] = out[j] as number;
47
+ out[j] = tmp;
48
+ }
49
+ return out;
50
+ }
51
+
52
+ function hash01(k: number): number {
53
+ // Shadertoy-style hash, deterministic per integer input.
54
+ const x = Math.sin(k * 12.9898 + 78.233) * 43758.5453;
55
+ return x - Math.floor(x);
56
+ }
57
+
58
+ export function buildSubdividedPlane(nx: number, ny: number): MeshBuffers {
59
+ const gridX = Math.max(1, Math.floor(nx));
60
+ const gridY = Math.max(1, Math.floor(ny));
61
+ const quadCount = gridX * gridY;
62
+ const faceCount = quadCount * 2;
63
+ const vertexCount = faceCount * 3;
64
+
65
+ const position = new Float32Array(vertexCount * 2);
66
+ const uv = new Float32Array(vertexCount * 2);
67
+ const offset = new Float32Array(vertexCount);
68
+ const centroid = new Float32Array(vertexCount * 2);
69
+ const bary = new Float32Array(vertexCount * 3);
70
+ const random = new Float32Array(vertexCount);
71
+
72
+ const quadOffsets = seededShuffledOffsets(quadCount);
73
+
74
+ const dx = 2 / gridX;
75
+ const dy = 2 / gridY;
76
+
77
+ let v = 0;
78
+
79
+ for (let iy = 0; iy < gridY; iy++) {
80
+ for (let ix = 0; ix < gridX; ix++) {
81
+ const quadIdx = iy * gridX + ix;
82
+ const x0 = -1 + ix * dx;
83
+ const x1 = x0 + dx;
84
+ const y0 = -1 + iy * dy;
85
+ const y1 = y0 + dy;
86
+
87
+ const cx = (x0 + x1) * 0.5;
88
+ const cy = (y0 + y1) * 0.5;
89
+ const quadOffset = quadOffsets[quadIdx] as number;
90
+ const quadRandom = hash01(quadIdx + 1);
91
+
92
+ // Two triangles per quad: (x0,y0)-(x1,y0)-(x0,y1) and (x1,y0)-(x1,y1)-(x0,y1).
93
+ const tris: Array<[[number, number], [number, number], [number, number]]> = [
94
+ [
95
+ [x0, y0],
96
+ [x1, y0],
97
+ [x0, y1],
98
+ ],
99
+ [
100
+ [x1, y0],
101
+ [x1, y1],
102
+ [x0, y1],
103
+ ],
104
+ ];
105
+
106
+ for (const tri of tris) {
107
+ const verts = [tri[0], tri[1], tri[2]];
108
+ const barys = [BARY_A, BARY_B, BARY_C];
109
+
110
+ for (let k = 0; k < 3; k++) {
111
+ const vert = verts[k] as [number, number];
112
+ const b = barys[k] as readonly [number, number, number];
113
+ const p0 = vert[0];
114
+ const p1 = vert[1];
115
+
116
+ position[v * 2] = p0;
117
+ position[v * 2 + 1] = p1;
118
+ uv[v * 2] = p0 * 0.5 + 0.5;
119
+ uv[v * 2 + 1] = p1 * 0.5 + 0.5;
120
+ offset[v] = quadOffset;
121
+ centroid[v * 2] = cx;
122
+ centroid[v * 2 + 1] = cy;
123
+ bary[v * 3] = b[0];
124
+ bary[v * 3 + 1] = b[1];
125
+ bary[v * 3 + 2] = b[2];
126
+ random[v] = quadRandom;
127
+
128
+ v++;
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ return { vertexCount, position, uv, offset, centroid, bary, random };
135
+ }