@heyramzi/ui 2.11.0 → 2.11.2

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 (267) hide show
  1. package/dist/agents/helpers.js +38 -38
  2. package/dist/agents/page-state.js +49 -50
  3. package/dist/agents/vendor/color.js +146 -127
  4. package/dist/agents/vendor/draw.js +617 -508
  5. package/dist/agents/vendor/engine.js +698 -599
  6. package/dist/agents/vendor/mesh.js +653 -683
  7. package/dist/agents/vendor/plastic.js +3913 -3112
  8. package/dist/agents/vendor/presets.js +163 -164
  9. package/dist/agents/vendor/shapes.js +21 -20
  10. package/dist/agents/vendor/ticker.js +41 -42
  11. package/dist/agents/vendor/types.js +1 -0
  12. package/dist/agents/vendor/wear.js +1421 -1448
  13. package/dist/ai/confirmation/confirmation-context.svelte.js +6 -6
  14. package/dist/ai/confirmation/index.js +3 -1
  15. package/dist/ai/index.js +33 -0
  16. package/dist/ai/prompt-input/index.js +3 -1
  17. package/dist/ai/reasoning/index.js +3 -1
  18. package/dist/ai/reasoning/reasoning-context.svelte.js +6 -6
  19. package/dist/ai/suggestion/index.js +3 -1
  20. package/dist/ai/typing-dots/index.js +3 -1
  21. package/dist/animations/text-swap.js +24 -24
  22. package/dist/brand/index.js +54 -48
  23. package/dist/canvas/card/resize.js +5 -3
  24. package/dist/canvas/edges/edge-path.js +255 -242
  25. package/dist/canvas/editor/context-menu.js +39 -36
  26. package/dist/canvas/editor/dispatch.svelte.js +154 -151
  27. package/dist/canvas/editor/editor-context.js +7 -2
  28. package/dist/canvas/editor/editor-mode.js +1 -0
  29. package/dist/canvas/editor/frame.js +73 -76
  30. package/dist/canvas/editor/hand-context.js +5 -2
  31. package/dist/canvas/editor/history-core.js +163 -191
  32. package/dist/canvas/editor/keymap.js +95 -169
  33. package/dist/canvas/editor/layout-actions.js +143 -144
  34. package/dist/canvas/editor/lock.js +52 -51
  35. package/dist/canvas/editor/rebase.js +217 -240
  36. package/dist/canvas/editor/style.js +132 -137
  37. package/dist/canvas/editor/text-field-context.js +7 -2
  38. package/dist/canvas/editor/z-order.js +65 -70
  39. package/dist/canvas/headless/alignment.js +22 -16
  40. package/dist/canvas/headless/break-apart-find.js +286 -279
  41. package/dist/canvas/headless/break-apart-pieces.js +62 -58
  42. package/dist/canvas/headless/break-apart-plan.js +299 -325
  43. package/dist/canvas/headless/break-apart-style.js +75 -70
  44. package/dist/canvas/headless/break-apart.js +188 -181
  45. package/dist/canvas/headless/canvas-cleanup.js +78 -58
  46. package/dist/canvas/headless/edge-anchors.js +115 -116
  47. package/dist/canvas/headless/edge-memo.js +24 -22
  48. package/dist/canvas/headless/edge-serialization.js +17 -27
  49. package/dist/canvas/headless/figure-geometry.js +232 -264
  50. package/dist/canvas/headless/fit-viewport.js +84 -67
  51. package/dist/canvas/headless/grid.js +88 -86
  52. package/dist/canvas/headless/icon-node.js +44 -36
  53. package/dist/canvas/headless/index.js +1 -0
  54. package/dist/canvas/headless/linkify.js +17 -19
  55. package/dist/canvas/headless/pen-marks.js +101 -95
  56. package/dist/canvas/headless/sketch.js +231 -209
  57. package/dist/canvas/headless/snap-guides.js +99 -83
  58. package/dist/canvas/headless/svg-figure.js +133 -117
  59. package/dist/canvas/headless/table-layout.js +146 -107
  60. package/dist/canvas/index.js +2 -0
  61. package/dist/canvas/lod/lod-mode.svelte.js +20 -18
  62. package/dist/canvas/lod/render-all.svelte.js +36 -39
  63. package/dist/canvas/ops/arrange.js +78 -71
  64. package/dist/canvas/ops/ops.js +260 -255
  65. package/dist/canvas/pen/colors.js +37 -35
  66. package/dist/canvas/pen/eraser.js +28 -27
  67. package/dist/canvas/pen/geometry.js +120 -130
  68. package/dist/canvas/pen/index.js +1 -0
  69. package/dist/canvas/pen/ops.js +11 -11
  70. package/dist/canvas/pen/pen-session.js +150 -159
  71. package/dist/canvas/pen/plugin.js +107 -104
  72. package/dist/canvas/pen/shapes.js +73 -68
  73. package/dist/canvas/pen/stroke-path.js +32 -30
  74. package/dist/canvas/pen/types.js +21 -21
  75. package/dist/canvas/pen/ui.js +10 -8
  76. package/dist/canvas/plugin.js +46 -46
  77. package/dist/canvas/screen/glyphs.js +12 -11
  78. package/dist/canvas/screen/labels.js +22 -22
  79. package/dist/canvas/screen/parts.js +98 -111
  80. package/dist/canvas/screen/plugin.js +47 -56
  81. package/dist/canvas/screen/types.js +30 -35
  82. package/dist/canvas/screen/ui.js +7 -7
  83. package/dist/canvas/test-utils.js +14 -14
  84. package/dist/canvas/types.js +1 -0
  85. package/dist/canvas/ui/forward-wheel.js +24 -25
  86. package/dist/canvas/ui/icons.js +64 -131
  87. package/dist/canvas/ui/place-panel.js +58 -49
  88. package/dist/canvas/ui/shortcut-tip.js +14 -12
  89. package/dist/canvas/ui/tools.js +300 -385
  90. package/dist/charts/dashboard/chart.js +4 -0
  91. package/dist/charts/dashboard/data-table.js +11 -0
  92. package/dist/charts/dashboard/windows.js +8 -3
  93. package/dist/charts/edge-fade.js +10 -5
  94. package/dist/charts/hover-band.js +16 -11
  95. package/dist/charts/index.js +0 -1
  96. package/dist/charts/motion.js +11 -7
  97. package/dist/charts/primitives/chart-utils.js +12 -12
  98. package/dist/components/copy/clipboard.js +32 -32
  99. package/dist/components/copy/copied.svelte.js +31 -23
  100. package/dist/components/data-table/data-table.js +39 -39
  101. package/dist/components/docs/index.js +4 -0
  102. package/dist/components/docs/scroll-spy.svelte.js +73 -68
  103. package/dist/components/list-page/filter-section.js +4 -1
  104. package/dist/components/list-page/index.js +2 -0
  105. package/dist/components/list-page/list-table.js +7 -2
  106. package/dist/components/list-page/queue-rail.js +14 -0
  107. package/dist/components/list-page/section-host.js +4 -2
  108. package/dist/components/list-page/step.js +4 -3
  109. package/dist/components/list-page/types.js +1 -0
  110. package/dist/components/picker/trigger.js +48 -30
  111. package/dist/components/picker/types.js +1 -0
  112. package/dist/components/segmented/collapse.js +38 -27
  113. package/dist/components/segmented/index.js +8 -0
  114. package/dist/components/segmented/segmented.js +67 -42
  115. package/dist/components/star-rating-input/index.js +3 -1
  116. package/dist/components/table-view/types.js +1 -0
  117. package/dist/components/tags-input/tags.js +81 -69
  118. package/dist/components/theme/index.js +4 -0
  119. package/dist/components/toolbar/index.js +4 -0
  120. package/dist/components/toolbar/toolbar-trigger.js +14 -1
  121. package/dist/editor/anchors.js +17 -13
  122. package/dist/editor/block-id.js +52 -51
  123. package/dist/editor/block-ops.js +108 -80
  124. package/dist/editor/doc-blocks.js +155 -166
  125. package/dist/editor/extensions.js +139 -180
  126. package/dist/editor/index.js +4 -0
  127. package/dist/editor/markdown-test-helpers.js +4 -2
  128. package/dist/editor/markdown.js +420 -408
  129. package/dist/editor/serializer.js +204 -200
  130. package/dist/editor/slash-items.js +89 -101
  131. package/dist/editor/youtube-url.js +25 -20
  132. package/dist/effects/animated-beam/path-calculator.svelte.js +43 -38
  133. package/dist/effects/animated-beam/types.js +1 -0
  134. package/dist/effects/border-beam/css.js +92 -78
  135. package/dist/effects/border-beam/presets.js +1705 -1720
  136. package/dist/effects/border-beam/register-properties.js +43 -33
  137. package/dist/effects/border-beam/types.js +1 -0
  138. package/dist/effects/image-comparison/compare.js +3 -2
  139. package/dist/effects/image-reveal/types.js +1 -0
  140. package/dist/effects/magnetic/magnetic.js +58 -52
  141. package/dist/effects/reveal/observe.js +50 -40
  142. package/dist/effects/reveal/types.js +1 -0
  143. package/dist/effects/shader-gradient/color.js +36 -29
  144. package/dist/effects/shader-gradient/renderer.js +384 -295
  145. package/dist/effects/shader-gradient/shaders.js +31 -0
  146. package/dist/effects/shader-gradient/types.js +1 -0
  147. package/dist/icons/apple-symbol-exports.js +99 -124
  148. package/dist/icons/sf-symbols.js +357 -461
  149. package/dist/icons/trades.js +43 -28
  150. package/dist/inbox/index.js +1 -0
  151. package/dist/inbox/logic.js +126 -122
  152. package/dist/inbox/types.js +43 -43
  153. package/dist/lib/arrive.js +19 -12
  154. package/dist/lib/icon-types.js +1 -0
  155. package/dist/lib/initials.js +16 -10
  156. package/dist/lib/layout.js +5 -0
  157. package/dist/lib/panel.js +13 -0
  158. package/dist/lib/result.js +22 -22
  159. package/dist/lib/sliding-pill.js +59 -44
  160. package/dist/lib/theme-transition.js +43 -43
  161. package/dist/lib/tone.js +31 -24
  162. package/dist/lib/utils.js +3 -1
  163. package/dist/onboarding/define-tours.js +24 -23
  164. package/dist/onboarding/onboarding.svelte.js +16 -12
  165. package/dist/onboarding/pick.js +10 -6
  166. package/dist/onboarding/stores.js +40 -41
  167. package/dist/onboarding/wait.js +24 -23
  168. package/dist/panel/side-panel-types.js +1 -0
  169. package/dist/record/demo-data.js +459 -486
  170. package/dist/record/fields.js +129 -131
  171. package/dist/record/format.js +70 -82
  172. package/dist/record/gutter.js +1 -0
  173. package/dist/record/kinds.js +95 -105
  174. package/dist/record/logic.js +238 -219
  175. package/dist/record/types.js +49 -48
  176. package/dist/shadcn/accordion/index.js +3 -1
  177. package/dist/shadcn/alert/index.js +3 -1
  178. package/dist/shadcn/alert-dialog/index.js +3 -1
  179. package/dist/shadcn/attachment/index.js +3 -1
  180. package/dist/shadcn/avatar/index.js +3 -1
  181. package/dist/shadcn/breadcrumb/index.js +3 -1
  182. package/dist/shadcn/bubble/index.js +3 -1
  183. package/dist/shadcn/button/index.js +3 -1
  184. package/dist/shadcn/calendar/index.js +3 -1
  185. package/dist/shadcn/card/index.js +3 -1
  186. package/dist/shadcn/checkbox/index.js +3 -1
  187. package/dist/shadcn/code-block/index.js +3 -1
  188. package/dist/shadcn/collapsible/index.js +3 -1
  189. package/dist/shadcn/command/index.js +4 -1
  190. package/dist/shadcn/context-menu/index.js +3 -1
  191. package/dist/shadcn/dialog/index.js +3 -1
  192. package/dist/shadcn/dropdown-menu/menu-item.js +40 -20
  193. package/dist/shadcn/empty/index.js +3 -1
  194. package/dist/shadcn/field/index.js +3 -1
  195. package/dist/shadcn/hooks/highlighter.js +77 -75
  196. package/dist/shadcn/hooks/is-mobile.svelte.js +3 -3
  197. package/dist/shadcn/hooks/use-highlighted-code.svelte.js +62 -62
  198. package/dist/shadcn/input/index.js +3 -1
  199. package/dist/shadcn/kbd/index.js +3 -1
  200. package/dist/shadcn/label/index.js +3 -1
  201. package/dist/shadcn/marker/index.js +3 -1
  202. package/dist/shadcn/message/index.js +3 -1
  203. package/dist/shadcn/message-scroller/index.js +3 -1
  204. package/dist/shadcn/popover/index.js +3 -1
  205. package/dist/shadcn/progress/index.js +3 -1
  206. package/dist/shadcn/progress/tone.js +8 -7
  207. package/dist/shadcn/radio-group/index.js +3 -1
  208. package/dist/shadcn/scroll-area/index.js +3 -1
  209. package/dist/shadcn/select/index.js +3 -1
  210. package/dist/shadcn/separator/index.js +3 -1
  211. package/dist/shadcn/sheet/index.js +3 -1
  212. package/dist/shadcn/sidebar/context.svelte.js +42 -28
  213. package/dist/shadcn/sidebar/index.js +3 -1
  214. package/dist/shadcn/skeleton/index.js +3 -1
  215. package/dist/shadcn/slider/index.js +3 -1
  216. package/dist/shadcn/switch/index.js +3 -1
  217. package/dist/shadcn/table/index.js +3 -1
  218. package/dist/shadcn/tabs/index.js +3 -1
  219. package/dist/shadcn/textarea/index.js +3 -1
  220. package/dist/shadcn/toggle/index.js +3 -1
  221. package/dist/shadcn/toggle-group/index.js +3 -1
  222. package/dist/shadcn/tooltip/context.js +7 -2
  223. package/dist/shadcn/tooltip/index.js +3 -1
  224. package/dist/shadcn/trace/index.js +3 -1
  225. package/dist/slides/bridges.js +65 -59
  226. package/dist/slides/deck-controller.svelte.js +210 -207
  227. package/dist/slides/export/capture.js +66 -71
  228. package/dist/slides/export/ffmpeg.js +76 -72
  229. package/dist/slides/export/index.js +3 -0
  230. package/dist/slides/export/launch.js +9 -6
  231. package/dist/slides/export/pdf.js +60 -54
  232. package/dist/slides/export/types.js +1 -0
  233. package/dist/slides/labels.js +24 -24
  234. package/dist/slides/layout.js +39 -41
  235. package/dist/slides/model.js +9 -7
  236. package/dist/slides/navigation.js +78 -73
  237. package/dist/slides/types.js +2 -2
  238. package/dist/status/define-statuses.js +18 -14
  239. package/dist/status/status-types.js +1 -0
  240. package/dist/test-setup.js +24 -20
  241. package/dist/thread/logic.js +73 -77
  242. package/dist/thread/types.js +21 -21
  243. package/dist/work/board-order.js +94 -90
  244. package/dist/work/dates.js +16 -9
  245. package/dist/work/detail/detail-types.js +18 -18
  246. package/dist/work/docs/edited.js +32 -29
  247. package/dist/work/docs/index.js +4 -0
  248. package/dist/work/docs/labels.js +34 -34
  249. package/dist/work/docs/menu.js +113 -106
  250. package/dist/work/docs/outline.js +39 -29
  251. package/dist/work/gantt/logic.js +211 -235
  252. package/dist/work/inbox/inbox-logic.js +71 -75
  253. package/dist/work/inbox/inbox-statuses.js +75 -72
  254. package/dist/work/inbox/inbox-types.js +1 -0
  255. package/dist/work/kanban-drag.js +27 -22
  256. package/dist/work/kanban-types.js +1 -0
  257. package/dist/work/presets/sales.js +36 -32
  258. package/dist/work/reorder.js +9 -8
  259. package/dist/work/task-types.js +1 -0
  260. package/dist/work/time/duration.js +51 -46
  261. package/dist/work/time/time-types.js +23 -23
  262. package/dist/work/use-kanban-dnd.svelte.js +126 -125
  263. package/package.json +3 -1
  264. package/src/components/list-page/master-detail.svelte +3 -1
  265. package/src/components/list-page/rail-row.svelte +4 -4
  266. package/src/components/tags-input/tags-input.svelte +2 -0
  267. package/src/editor/markdown.ts +30 -35
@@ -1,320 +1,409 @@
1
1
  import { HALFTONE_FRAGMENT_SHADER, MESH_FRAGMENT_SHADER, MESH_VERTEX_SHADER, QUAD_VERTEX_SHADER } from "./shaders.js";
2
+ /** Upstream builds `planeGeometry(10, 10, 1, 192)`: one cell across, 192 down. */
2
3
  const PLANE_SIZE = 10;
3
4
  const PLANE_SEGMENTS = 192;
4
5
  const DEG_TO_RAD = Math.PI / 180;
6
+ /** Per-channel screen angles, in the order `PostProcessing.tsx` assigns them. */
5
7
  const HALFTONE_ROTATION = {
6
- r: Math.PI / 12,
7
- g: Math.PI / 12 * 3,
8
- b: Math.PI / 12 * 2
8
+ r: Math.PI / 12,
9
+ g: Math.PI / 12 * 3,
10
+ b: Math.PI / 12 * 2
9
11
  };
10
12
  const HALFTONE_SCATTER = 1;
13
+ /** Widest half-frame, in world units, the sheet is known to cover at every phase. */
11
14
  const MAX_VISIBLE_HALF_WIDTH = 2.4;
15
+ /**
16
+ * Widest angle, in degrees, between the view axis and a ray to the corner of the frame.
17
+ */
18
+ /*
19
+ * This is what keeps the sheet from hiding part of itself: see the framing note in `render`.
20
+ */
12
21
  const MAX_CORNER_RAY_DEGREES = 10;
22
+ /**
23
+ * A strip of quads one cell wide, exactly upstream's `planeGeometry(10, 10, 1, 192)`.
24
+ */
25
+ /*
26
+ * Only the y axis is subdivided, which is what gives the sheet its long unbroken folds: across a row the fold is one straight interpolation between the two edges,
27
+ */
28
+ /*
29
+ * with nothing in between to bend it.
30
+ */
13
31
  function createPlane() {
14
- const positions = new Float32Array((PLANE_SEGMENTS + 1) * 2 * 3);
15
- const indices = new Uint16Array(PLANE_SEGMENTS * 6);
16
- const half = PLANE_SIZE / 2;
17
- const segment = PLANE_SIZE / PLANE_SEGMENTS;
18
- for(let iy = 0; iy <= PLANE_SEGMENTS; iy++){
19
- const y = -(iy * segment - half);
20
- const offset = iy * 6;
21
- positions[offset] = -half;
22
- positions[offset + 1] = y;
23
- positions[offset + 2] = 0;
24
- positions[offset + 3] = half;
25
- positions[offset + 4] = y;
26
- positions[offset + 5] = 0;
27
- }
28
- for(let iy = 0; iy < PLANE_SEGMENTS; iy++){
29
- const a = iy * 2;
30
- const b = (iy + 1) * 2;
31
- const c = (iy + 1) * 2 + 1;
32
- const d = iy * 2 + 1;
33
- const offset = iy * 6;
34
- indices[offset] = a;
35
- indices[offset + 1] = b;
36
- indices[offset + 2] = d;
37
- indices[offset + 3] = b;
38
- indices[offset + 4] = c;
39
- indices[offset + 5] = d;
40
- }
41
- return {
42
- positions,
43
- indices
44
- };
32
+ const positions = new Float32Array((PLANE_SEGMENTS + 1) * 2 * 3);
33
+ const indices = new Uint16Array(PLANE_SEGMENTS * 6);
34
+ const half = PLANE_SIZE / 2;
35
+ const segment = PLANE_SIZE / PLANE_SEGMENTS;
36
+ for (let iy = 0; iy <= PLANE_SEGMENTS; iy++) {
37
+ const y = -(iy * segment - half);
38
+ const offset = iy * 6;
39
+ positions[offset] = -half;
40
+ positions[offset + 1] = y;
41
+ positions[offset + 2] = 0;
42
+ positions[offset + 3] = half;
43
+ positions[offset + 4] = y;
44
+ positions[offset + 5] = 0;
45
+ }
46
+ for (let iy = 0; iy < PLANE_SEGMENTS; iy++) {
47
+ const a = iy * 2;
48
+ const b = (iy + 1) * 2;
49
+ const c = (iy + 1) * 2 + 1;
50
+ const d = iy * 2 + 1;
51
+ const offset = iy * 6;
52
+ indices[offset] = a;
53
+ indices[offset + 1] = b;
54
+ indices[offset + 2] = d;
55
+ indices[offset + 3] = b;
56
+ indices[offset + 4] = c;
57
+ indices[offset + 5] = d;
58
+ }
59
+ return {
60
+ positions,
61
+ indices
62
+ };
45
63
  }
46
64
  function perspective(fovDegrees, aspect, near, far) {
47
- const f = 1 / Math.tan(fovDegrees * DEG_TO_RAD / 2);
48
- const range = 1 / (near - far);
49
- return new Float32Array([
50
- f / aspect,
51
- 0,
52
- 0,
53
- 0,
54
- 0,
55
- f,
56
- 0,
57
- 0,
58
- 0,
59
- 0,
60
- (far + near) * range,
61
- -1,
62
- 0,
63
- 0,
64
- 2 * far * near * range,
65
- 0
66
- ]);
65
+ const f = 1 / Math.tan(fovDegrees * DEG_TO_RAD / 2);
66
+ const range = 1 / (near - far);
67
+ // prettier-ignore
68
+ return new Float32Array([
69
+ f / aspect,
70
+ 0,
71
+ 0,
72
+ 0,
73
+ 0,
74
+ f,
75
+ 0,
76
+ 0,
77
+ 0,
78
+ 0,
79
+ (far + near) * range,
80
+ -1,
81
+ 0,
82
+ 0,
83
+ 2 * far * near * range,
84
+ 0
85
+ ]);
67
86
  }
87
+ /** View matrix for a camera sitting on a sphere around the origin, looking at it. */
68
88
  function orbitView(distance, polarDegrees, azimuthDegrees) {
69
- const polar = polarDegrees * DEG_TO_RAD;
70
- const azimuth = azimuthDegrees * DEG_TO_RAD;
71
- const eye = [
72
- distance * Math.sin(polar) * Math.sin(azimuth),
73
- distance * Math.cos(polar),
74
- distance * Math.sin(polar) * Math.cos(azimuth)
75
- ];
76
- let zx = eye[0];
77
- let zy = eye[1];
78
- let zz = eye[2];
79
- const zLength = Math.hypot(zx, zy, zz) || 1;
80
- zx /= zLength;
81
- zy /= zLength;
82
- zz /= zLength;
83
- let xx = zz;
84
- let xy = 0;
85
- let xz = -zx;
86
- const xLength = Math.hypot(xx, xy, xz) || 1;
87
- xx /= xLength;
88
- xy /= xLength;
89
- xz /= xLength;
90
- const yx = zy * xz - zz * xy;
91
- const yy = zz * xx - zx * xz;
92
- const yz = zx * xy - zy * xx;
93
- return new Float32Array([
94
- xx,
95
- yx,
96
- zx,
97
- 0,
98
- xy,
99
- yy,
100
- zy,
101
- 0,
102
- xz,
103
- yz,
104
- zz,
105
- 0,
106
- -(xx * eye[0] + xy * eye[1] + xz * eye[2]),
107
- -(yx * eye[0] + yy * eye[1] + yz * eye[2]),
108
- -(zx * eye[0] + zy * eye[1] + zz * eye[2]),
109
- 1
110
- ]);
89
+ const polar = polarDegrees * DEG_TO_RAD;
90
+ const azimuth = azimuthDegrees * DEG_TO_RAD;
91
+ const eye = [
92
+ distance * Math.sin(polar) * Math.sin(azimuth),
93
+ distance * Math.cos(polar),
94
+ distance * Math.sin(polar) * Math.cos(azimuth)
95
+ ];
96
+ // Camera basis, target fixed at the origin so `eye` doubles as the forward axis.
97
+ let zx = eye[0];
98
+ let zy = eye[1];
99
+ let zz = eye[2];
100
+ const zLength = Math.hypot(zx, zy, zz) || 1;
101
+ zx /= zLength;
102
+ zy /= zLength;
103
+ zz /= zLength;
104
+ // up is (0, 1, 0), so cross(up, z) reduces to this.
105
+ let xx = zz;
106
+ let xy = 0;
107
+ let xz = -zx;
108
+ const xLength = Math.hypot(xx, xy, xz) || 1;
109
+ xx /= xLength;
110
+ xy /= xLength;
111
+ xz /= xLength;
112
+ const yx = zy * xz - zz * xy;
113
+ const yy = zz * xx - zx * xz;
114
+ const yz = zx * xy - zy * xx;
115
+ // prettier-ignore
116
+ return new Float32Array([
117
+ xx,
118
+ yx,
119
+ zx,
120
+ 0,
121
+ xy,
122
+ yy,
123
+ zy,
124
+ 0,
125
+ xz,
126
+ yz,
127
+ zz,
128
+ 0,
129
+ -(xx * eye[0] + xy * eye[1] + xz * eye[2]),
130
+ -(yx * eye[0] + yy * eye[1] + yz * eye[2]),
131
+ -(zx * eye[0] + zy * eye[1] + zz * eye[2]),
132
+ 1
133
+ ]);
111
134
  }
135
+ /** Translation times an XYZ-ordered euler rotation, matching three's `Euler` default. */
112
136
  function modelMatrix(position, rotationDegrees) {
113
- const a = Math.cos(rotationDegrees[0] * DEG_TO_RAD);
114
- const b = Math.sin(rotationDegrees[0] * DEG_TO_RAD);
115
- const c = Math.cos(rotationDegrees[1] * DEG_TO_RAD);
116
- const d = Math.sin(rotationDegrees[1] * DEG_TO_RAD);
117
- const e = Math.cos(rotationDegrees[2] * DEG_TO_RAD);
118
- const f = Math.sin(rotationDegrees[2] * DEG_TO_RAD);
119
- const ae = a * e;
120
- const af = a * f;
121
- const be = b * e;
122
- const bf = b * f;
123
- return new Float32Array([
124
- c * e,
125
- af + be * d,
126
- bf - ae * d,
127
- 0,
128
- -c * f,
129
- ae - bf * d,
130
- be + af * d,
131
- 0,
132
- d,
133
- -b * c,
134
- a * c,
135
- 0,
136
- position[0],
137
- position[1],
138
- position[2],
139
- 1
140
- ]);
137
+ const a = Math.cos(rotationDegrees[0] * DEG_TO_RAD);
138
+ const b = Math.sin(rotationDegrees[0] * DEG_TO_RAD);
139
+ const c = Math.cos(rotationDegrees[1] * DEG_TO_RAD);
140
+ const d = Math.sin(rotationDegrees[1] * DEG_TO_RAD);
141
+ const e = Math.cos(rotationDegrees[2] * DEG_TO_RAD);
142
+ const f = Math.sin(rotationDegrees[2] * DEG_TO_RAD);
143
+ const ae = a * e;
144
+ const af = a * f;
145
+ const be = b * e;
146
+ const bf = b * f;
147
+ // prettier-ignore
148
+ return new Float32Array([
149
+ c * e,
150
+ af + be * d,
151
+ bf - ae * d,
152
+ 0,
153
+ -c * f,
154
+ ae - bf * d,
155
+ be + af * d,
156
+ 0,
157
+ d,
158
+ -b * c,
159
+ a * c,
160
+ 0,
161
+ position[0],
162
+ position[1],
163
+ position[2],
164
+ 1
165
+ ]);
141
166
  }
142
167
  function compile(gl, type, source) {
143
- const shader = gl.createShader(type);
144
- if (!shader) throw new Error("shader-gradient: could not create shader");
145
- gl.shaderSource(shader, source);
146
- gl.compileShader(shader);
147
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
148
- const log = gl.getShaderInfoLog(shader);
149
- gl.deleteShader(shader);
150
- throw new Error(`shader-gradient: shader failed to compile. ${log}`);
151
- }
152
- return shader;
168
+ const shader = gl.createShader(type);
169
+ if (!shader) throw new Error("shader-gradient: could not create shader");
170
+ gl.shaderSource(shader, source);
171
+ gl.compileShader(shader);
172
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
173
+ const log = gl.getShaderInfoLog(shader);
174
+ gl.deleteShader(shader);
175
+ throw new Error(`shader-gradient: shader failed to compile. ${log}`);
176
+ }
177
+ return shader;
153
178
  }
154
179
  function link(gl, vertexSource, fragmentSource) {
155
- const vertex = compile(gl, gl.VERTEX_SHADER, vertexSource);
156
- const fragment = compile(gl, gl.FRAGMENT_SHADER, fragmentSource);
157
- const program = gl.createProgram();
158
- if (!program) throw new Error("shader-gradient: could not create program");
159
- gl.attachShader(program, vertex);
160
- gl.attachShader(program, fragment);
161
- gl.linkProgram(program);
162
- gl.deleteShader(vertex);
163
- gl.deleteShader(fragment);
164
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
165
- const log = gl.getProgramInfoLog(program);
166
- gl.deleteProgram(program);
167
- throw new Error(`shader-gradient: program failed to link. ${log}`);
168
- }
169
- return program;
180
+ const vertex = compile(gl, gl.VERTEX_SHADER, vertexSource);
181
+ const fragment = compile(gl, gl.FRAGMENT_SHADER, fragmentSource);
182
+ const program = gl.createProgram();
183
+ if (!program) throw new Error("shader-gradient: could not create program");
184
+ gl.attachShader(program, vertex);
185
+ gl.attachShader(program, fragment);
186
+ gl.linkProgram(program);
187
+ gl.deleteShader(vertex);
188
+ gl.deleteShader(fragment);
189
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
190
+ const log = gl.getProgramInfoLog(program);
191
+ gl.deleteProgram(program);
192
+ throw new Error(`shader-gradient: program failed to link. ${log}`);
193
+ }
194
+ return program;
170
195
  }
171
196
  function uniformLocations(gl, program, names) {
172
- const locations = {};
173
- for (const name of names)locations[name] = gl.getUniformLocation(program, name);
174
- return locations;
197
+ const locations = {};
198
+ for (const name of names) locations[name] = gl.getUniformLocation(program, name);
199
+ return locations;
175
200
  }
201
+ /**
202
+ * Returns null when the browser has no WebGL, so the caller can fall back to a plain
203
+ * background instead of throwing during hydration.
204
+ */
176
205
  export function createShaderGradientRenderer(canvas) {
177
- const gl = canvas.getContext("webgl2", {
178
- antialias: true,
179
- alpha: false
180
- }) ?? canvas.getContext("webgl", {
181
- antialias: true,
182
- alpha: false
183
- });
184
- if (!gl) return null;
185
- const plane = createPlane();
186
- const meshProgram = link(gl, MESH_VERTEX_SHADER, MESH_FRAGMENT_SHADER);
187
- const meshUniforms = uniformLocations(gl, meshProgram, [
188
- "uProjectionMatrix",
189
- "uViewMatrix",
190
- "uModelMatrix",
191
- "uTime",
192
- "uSpeed",
193
- "uNoiseDensity",
194
- "uNoiseStrength",
195
- "uC1",
196
- "uC2",
197
- "uC3",
198
- "uBrightness",
199
- "uFoldRange"
200
- ]);
201
- const meshPositionAttribute = gl.getAttribLocation(meshProgram, "position");
202
- const halftoneProgram = link(gl, QUAD_VERTEX_SHADER, HALFTONE_FRAGMENT_SHADER);
203
- const halftoneUniforms = uniformLocations(gl, halftoneProgram, [
204
- "tDiffuse",
205
- "uRadius",
206
- "uRotateR",
207
- "uRotateG",
208
- "uRotateB",
209
- "uScatter",
210
- "uWidth",
211
- "uHeight"
212
- ]);
213
- const halftonePositionAttribute = gl.getAttribLocation(halftoneProgram, "position");
214
- const positionBuffer = gl.createBuffer();
215
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
216
- gl.bufferData(gl.ARRAY_BUFFER, plane.positions, gl.STATIC_DRAW);
217
- const indexBuffer = gl.createBuffer();
218
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
219
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, plane.indices, gl.STATIC_DRAW);
220
- const quadBuffer = gl.createBuffer();
221
- gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);
222
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
223
- -1,
224
- -1,
225
- 3,
226
- -1,
227
- -1,
228
- 3
229
- ]), gl.STATIC_DRAW);
230
- const targetTexture = gl.createTexture();
231
- gl.bindTexture(gl.TEXTURE_2D, targetTexture);
232
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
233
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
234
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
235
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
236
- const framebuffer = gl.createFramebuffer();
237
- let width = 1;
238
- let height = 1;
239
- const setSize = (nextWidth, nextHeight)=>{
240
- width = Math.max(1, Math.floor(nextWidth));
241
- height = Math.max(1, Math.floor(nextHeight));
242
- canvas.width = width;
243
- canvas.height = height;
244
- gl.bindTexture(gl.TEXTURE_2D, targetTexture);
245
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
246
- gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
247
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
248
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
249
- };
250
- const render = (uniforms, time)=>{
251
- const aspect = width / height;
252
- const halfHeight = Math.min(Math.tan(uniforms.fov * DEG_TO_RAD / 2) * uniforms.cDistance, MAX_VISIBLE_HALF_WIDTH / aspect);
253
- const cornerRadius = Math.hypot(halfHeight * aspect, halfHeight);
254
- const distance = Math.max(uniforms.cDistance, cornerRadius / Math.tan(MAX_CORNER_RAY_DEGREES * DEG_TO_RAD));
255
- const fov = 2 * Math.atan(halfHeight / distance) / DEG_TO_RAD;
256
- gl.bindFramebuffer(gl.FRAMEBUFFER, uniforms.grain ? framebuffer : null);
257
- gl.viewport(0, 0, width, height);
258
- gl.clearColor(uniforms.bgColor[0], uniforms.bgColor[1], uniforms.bgColor[2], 1);
259
- gl.clear(gl.COLOR_BUFFER_BIT);
260
- gl.useProgram(meshProgram);
261
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
262
- gl.enableVertexAttribArray(meshPositionAttribute);
263
- gl.vertexAttribPointer(meshPositionAttribute, 3, gl.FLOAT, false, 0, 0);
264
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
265
- gl.uniformMatrix4fv(meshUniforms.uProjectionMatrix, false, perspective(fov, aspect, 0.1, 1000));
266
- gl.uniformMatrix4fv(meshUniforms.uViewMatrix, false, orbitView(distance, uniforms.cPolarAngle, uniforms.cAzimuthAngle));
267
- gl.uniformMatrix4fv(meshUniforms.uModelMatrix, false, modelMatrix([
268
- uniforms.positionX,
269
- uniforms.positionY,
270
- uniforms.positionZ
271
- ], [
272
- uniforms.rotationX,
273
- uniforms.rotationY,
274
- uniforms.rotationZ
275
- ]));
276
- gl.uniform1f(meshUniforms.uTime, time);
277
- gl.uniform1f(meshUniforms.uSpeed, uniforms.uSpeed);
278
- gl.uniform1f(meshUniforms.uNoiseDensity, uniforms.uDensity);
279
- gl.uniform1f(meshUniforms.uNoiseStrength, uniforms.uStrength);
280
- gl.uniform3fv(meshUniforms.uC1, uniforms.color1);
281
- gl.uniform3fv(meshUniforms.uC2, uniforms.color2);
282
- gl.uniform3fv(meshUniforms.uC3, uniforms.color3);
283
- gl.uniform1f(meshUniforms.uBrightness, uniforms.brightness);
284
- gl.uniform1f(meshUniforms.uFoldRange, 0.75 * uniforms.uStrength);
285
- gl.drawElements(gl.TRIANGLES, plane.indices.length, gl.UNSIGNED_SHORT, 0);
286
- if (!uniforms.grain) return;
287
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
288
- gl.viewport(0, 0, width, height);
289
- gl.useProgram(halftoneProgram);
290
- gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);
291
- gl.enableVertexAttribArray(halftonePositionAttribute);
292
- gl.vertexAttribPointer(halftonePositionAttribute, 2, gl.FLOAT, false, 0, 0);
293
- gl.activeTexture(gl.TEXTURE0);
294
- gl.bindTexture(gl.TEXTURE_2D, targetTexture);
295
- gl.uniform1i(halftoneUniforms.tDiffuse, 0);
296
- gl.uniform1f(halftoneUniforms.uRadius, uniforms.grainRadius);
297
- gl.uniform1f(halftoneUniforms.uRotateR, HALFTONE_ROTATION.r);
298
- gl.uniform1f(halftoneUniforms.uRotateG, HALFTONE_ROTATION.g);
299
- gl.uniform1f(halftoneUniforms.uRotateB, HALFTONE_ROTATION.b);
300
- gl.uniform1f(halftoneUniforms.uScatter, HALFTONE_SCATTER);
301
- gl.uniform1f(halftoneUniforms.uWidth, width);
302
- gl.uniform1f(halftoneUniforms.uHeight, height);
303
- gl.drawArrays(gl.TRIANGLES, 0, 3);
304
- };
305
- const destroy = ()=>{
306
- gl.deleteBuffer(positionBuffer);
307
- gl.deleteBuffer(indexBuffer);
308
- gl.deleteBuffer(quadBuffer);
309
- gl.deleteTexture(targetTexture);
310
- gl.deleteFramebuffer(framebuffer);
311
- gl.deleteProgram(meshProgram);
312
- gl.deleteProgram(halftoneProgram);
313
- gl.getExtension("WEBGL_lose_context")?.loseContext();
314
- };
315
- return {
316
- setSize,
317
- render,
318
- destroy
319
- };
206
+ const gl = canvas.getContext("webgl2", {
207
+ antialias: true,
208
+ alpha: false
209
+ }) ?? canvas.getContext("webgl", {
210
+ antialias: true,
211
+ alpha: false
212
+ });
213
+ if (!gl) return null;
214
+ const plane = createPlane();
215
+ const meshProgram = link(gl, MESH_VERTEX_SHADER, MESH_FRAGMENT_SHADER);
216
+ const meshUniforms = uniformLocations(gl, meshProgram, [
217
+ "uProjectionMatrix",
218
+ "uViewMatrix",
219
+ "uModelMatrix",
220
+ "uTime",
221
+ "uSpeed",
222
+ "uNoiseDensity",
223
+ "uNoiseStrength",
224
+ "uC1",
225
+ "uC2",
226
+ "uC3",
227
+ "uBrightness",
228
+ "uFoldRange"
229
+ ]);
230
+ const meshPositionAttribute = gl.getAttribLocation(meshProgram, "position");
231
+ const halftoneProgram = link(gl, QUAD_VERTEX_SHADER, HALFTONE_FRAGMENT_SHADER);
232
+ const halftoneUniforms = uniformLocations(gl, halftoneProgram, [
233
+ "tDiffuse",
234
+ "uRadius",
235
+ "uRotateR",
236
+ "uRotateG",
237
+ "uRotateB",
238
+ "uScatter",
239
+ "uWidth",
240
+ "uHeight"
241
+ ]);
242
+ const halftonePositionAttribute = gl.getAttribLocation(halftoneProgram, "position");
243
+ const positionBuffer = gl.createBuffer();
244
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
245
+ gl.bufferData(gl.ARRAY_BUFFER, plane.positions, gl.STATIC_DRAW);
246
+ const indexBuffer = gl.createBuffer();
247
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
248
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, plane.indices, gl.STATIC_DRAW);
249
+ const quadBuffer = gl.createBuffer();
250
+ gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);
251
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
252
+ -1,
253
+ -1,
254
+ 3,
255
+ -1,
256
+ -1,
257
+ 3
258
+ ]), gl.STATIC_DRAW);
259
+ // Offscreen target for the halftone pass. Skipped entirely when grain is off.
260
+ const targetTexture = gl.createTexture();
261
+ gl.bindTexture(gl.TEXTURE_2D, targetTexture);
262
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
263
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
264
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
265
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
266
+ const framebuffer = gl.createFramebuffer();
267
+ let width = 1;
268
+ let height = 1;
269
+ const setSize = (nextWidth, nextHeight) => {
270
+ width = Math.max(1, Math.floor(nextWidth));
271
+ height = Math.max(1, Math.floor(nextHeight));
272
+ canvas.width = width;
273
+ canvas.height = height;
274
+ gl.bindTexture(gl.TEXTURE_2D, targetTexture);
275
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
276
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
277
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, targetTexture, 0);
278
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
279
+ };
280
+ const render = (uniforms, time) => {
281
+ const aspect = width / height;
282
+ /*
283
+ * Framing, in two parts: how much of the sheet to show, and how long a lens to show it with. The caller's `fov` and `cDistance` set the first.
284
+ */
285
+ /*
286
+ * The second is not the caller's to set, and this is why.
287
+ */
288
+ /*
289
+ * The sheet is a height field: one segment across, 192 down, displaced along its own normal only.
290
+ */
291
+ /*
292
+ * Its folds are steep, past 70 degrees at the preset strength, and a surface that steep hides part of itself as soon as the view rays open far enough to
293
+ */
294
+ /*
295
+ * look along a fold. What comes back is a hard straight edge with a different colour on each side, because the sheet is ruled across its width,
296
+ */
297
+ /*
298
+ * so the line where it turns out of sight is one of those straight generators, and because the fragment colour rides the fold depth,
299
+ */
300
+ /*
301
+ * so the two sides of it sit at different depths and read as two different colours meeting.
302
+ */
303
+ /*
304
+ * It arrives in a corner, where the rays open widest, and it comes and goes with the drift. Nothing about it looks like a fold; it looks like a tear.
305
+ */
306
+ /*
307
+ * So the lens length is set by the frame, not by the caller: push the camera back until no ray to a corner opens more than MAX_CORNER_RAY_DEGREES,
308
+ */
309
+ /*
310
+ * then pick the field of view that puts the same half-frame back.
311
+ */
312
+ /*
313
+ * The visible size is unchanged, at every aspect and for every caller, and the only thing that goes is the perspective inside it,
314
+ */
315
+ /*
316
+ * which at these settings is a fold that pumps toward the camera rather than anything the composition needs.
317
+ */
318
+ /*
319
+ * Measured over 8 frame shapes from 0.55 to 3.2, both presets, 50 phases of the drift: 84 of those 800 frames carried an edge over 1.5 levels per pixel with
320
+ */
321
+ /*
322
+ * the rays uncapped, the worst 8.3.
323
+ */
324
+ /*
325
+ * At 14 degrees none of them did, and at 10 the worst frame sits at 0.75, which is the eight-bit floor of a smooth field.
326
+ */
327
+ /*
328
+ * Below 5 degrees nothing improves, so this is not a number to keep lowering.
329
+ */
330
+ /*
331
+ * `MAX_VISIBLE_HALF_WIDTH` still caps how much of the sheet a wide frame reaches for, because the sheet does run out: it is ten units across and the noise pushes parts of
332
+ */
333
+ /*
334
+ * it away, so past roughly a 1.8 frame an uncapped view would outrun it and show the clear colour behind its edge.
335
+ */
336
+ /*
337
+ * Widening the sheet instead would need a seam, which draws a hard line of its own.
338
+ */
339
+ const halfHeight = Math.min(Math.tan(uniforms.fov * DEG_TO_RAD / 2) * uniforms.cDistance, MAX_VISIBLE_HALF_WIDTH / aspect);
340
+ const cornerRadius = Math.hypot(halfHeight * aspect, halfHeight);
341
+ const distance = Math.max(uniforms.cDistance, cornerRadius / Math.tan(MAX_CORNER_RAY_DEGREES * DEG_TO_RAD));
342
+ const fov = 2 * Math.atan(halfHeight / distance) / DEG_TO_RAD;
343
+ gl.bindFramebuffer(gl.FRAMEBUFFER, uniforms.grain ? framebuffer : null);
344
+ gl.viewport(0, 0, width, height);
345
+ gl.clearColor(uniforms.bgColor[0], uniforms.bgColor[1], uniforms.bgColor[2], 1);
346
+ gl.clear(gl.COLOR_BUFFER_BIT);
347
+ gl.useProgram(meshProgram);
348
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
349
+ gl.enableVertexAttribArray(meshPositionAttribute);
350
+ gl.vertexAttribPointer(meshPositionAttribute, 3, gl.FLOAT, false, 0, 0);
351
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
352
+ gl.uniformMatrix4fv(meshUniforms.uProjectionMatrix, false, perspective(fov, aspect, .1, 1e3));
353
+ gl.uniformMatrix4fv(meshUniforms.uViewMatrix, false, orbitView(distance, uniforms.cPolarAngle, uniforms.cAzimuthAngle));
354
+ gl.uniformMatrix4fv(meshUniforms.uModelMatrix, false, modelMatrix([
355
+ uniforms.positionX,
356
+ uniforms.positionY,
357
+ uniforms.positionZ
358
+ ], [
359
+ uniforms.rotationX,
360
+ uniforms.rotationY,
361
+ uniforms.rotationZ
362
+ ]));
363
+ gl.uniform1f(meshUniforms.uTime, time);
364
+ gl.uniform1f(meshUniforms.uSpeed, uniforms.uSpeed);
365
+ gl.uniform1f(meshUniforms.uNoiseDensity, uniforms.uDensity);
366
+ gl.uniform1f(meshUniforms.uNoiseStrength, uniforms.uStrength);
367
+ gl.uniform3fv(meshUniforms.uC1, uniforms.color1);
368
+ gl.uniform3fv(meshUniforms.uC2, uniforms.color2);
369
+ gl.uniform3fv(meshUniforms.uC3, uniforms.color3);
370
+ gl.uniform1f(meshUniforms.uBrightness, uniforms.brightness);
371
+ // The fold reaches 0.75 x cnoise x strength, and classic Perlin tops out near 1.1.
372
+ // Slightly under that keeps the crests and troughs at the stops rather than short of them.
373
+ gl.uniform1f(meshUniforms.uFoldRange, .75 * uniforms.uStrength);
374
+ gl.drawElements(gl.TRIANGLES, plane.indices.length, gl.UNSIGNED_SHORT, 0);
375
+ if (!uniforms.grain) return;
376
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
377
+ gl.viewport(0, 0, width, height);
378
+ gl.useProgram(halftoneProgram);
379
+ gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer);
380
+ gl.enableVertexAttribArray(halftonePositionAttribute);
381
+ gl.vertexAttribPointer(halftonePositionAttribute, 2, gl.FLOAT, false, 0, 0);
382
+ gl.activeTexture(gl.TEXTURE0);
383
+ gl.bindTexture(gl.TEXTURE_2D, targetTexture);
384
+ gl.uniform1i(halftoneUniforms.tDiffuse, 0);
385
+ gl.uniform1f(halftoneUniforms.uRadius, uniforms.grainRadius);
386
+ gl.uniform1f(halftoneUniforms.uRotateR, HALFTONE_ROTATION.r);
387
+ gl.uniform1f(halftoneUniforms.uRotateG, HALFTONE_ROTATION.g);
388
+ gl.uniform1f(halftoneUniforms.uRotateB, HALFTONE_ROTATION.b);
389
+ gl.uniform1f(halftoneUniforms.uScatter, HALFTONE_SCATTER);
390
+ gl.uniform1f(halftoneUniforms.uWidth, width);
391
+ gl.uniform1f(halftoneUniforms.uHeight, height);
392
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
393
+ };
394
+ const destroy = () => {
395
+ gl.deleteBuffer(positionBuffer);
396
+ gl.deleteBuffer(indexBuffer);
397
+ gl.deleteBuffer(quadBuffer);
398
+ gl.deleteTexture(targetTexture);
399
+ gl.deleteFramebuffer(framebuffer);
400
+ gl.deleteProgram(meshProgram);
401
+ gl.deleteProgram(halftoneProgram);
402
+ gl.getExtension("WEBGL_lose_context")?.loseContext();
403
+ };
404
+ return {
405
+ setSize,
406
+ render,
407
+ destroy
408
+ };
320
409
  }