@perspective-dev/viewer-charts 4.3.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 (258) hide show
  1. package/LICENSE.md +193 -0
  2. package/dist/cdn/perspective-viewer-charts.js +3 -0
  3. package/dist/cdn/perspective-viewer-charts.js.map +7 -0
  4. package/dist/esm/axis/axis-primitives.d.ts +24 -0
  5. package/dist/esm/axis/bar-axis.d.ts +51 -0
  6. package/dist/esm/axis/canvas.d.ts +24 -0
  7. package/dist/esm/axis/categorical-axis-core.d.ts +42 -0
  8. package/dist/esm/axis/categorical-axis.d.ts +27 -0
  9. package/dist/esm/axis/facet-chrome.d.ts +13 -0
  10. package/dist/esm/axis/label-geometry.d.ts +41 -0
  11. package/dist/esm/axis/legend.d.ts +44 -0
  12. package/dist/esm/axis/numeric-axis.d.ts +20 -0
  13. package/dist/esm/charts/candlestick/candlestick-build.d.ts +129 -0
  14. package/dist/esm/charts/candlestick/candlestick-interact.d.ts +10 -0
  15. package/dist/esm/charts/candlestick/candlestick-render.d.ts +24 -0
  16. package/dist/esm/charts/candlestick/candlestick.d.ts +144 -0
  17. package/dist/esm/charts/candlestick/glyphs/draw-candlesticks.d.ts +36 -0
  18. package/dist/esm/charts/candlestick/glyphs/draw-ohlc.d.ts +33 -0
  19. package/dist/esm/charts/canvas-types.d.ts +15 -0
  20. package/dist/esm/charts/cartesian/cartesian-build.d.ts +14 -0
  21. package/dist/esm/charts/cartesian/cartesian-interact.d.ts +20 -0
  22. package/dist/esm/charts/cartesian/cartesian-render.d.ts +26 -0
  23. package/dist/esm/charts/cartesian/cartesian.d.ts +239 -0
  24. package/dist/esm/charts/cartesian/glyph.d.ts +53 -0
  25. package/dist/esm/charts/cartesian/glyphs/density.d.ts +142 -0
  26. package/dist/esm/charts/cartesian/glyphs/lines.d.ts +23 -0
  27. package/dist/esm/charts/cartesian/glyphs/points.d.ts +24 -0
  28. package/dist/esm/charts/cartesian/label-interner.d.ts +21 -0
  29. package/dist/esm/charts/cartesian/tooltip-lines.d.ts +11 -0
  30. package/dist/esm/charts/chart-base.d.ts +402 -0
  31. package/dist/esm/charts/chart.d.ts +338 -0
  32. package/dist/esm/charts/common/band-layout.d.ts +32 -0
  33. package/dist/esm/charts/common/categorical-y-chart.d.ts +53 -0
  34. package/dist/esm/charts/common/category-axis-resolver.d.ts +90 -0
  35. package/dist/esm/charts/common/chrome-cache.d.ts +18 -0
  36. package/dist/esm/charts/common/draw-tooltip-box.d.ts +9 -0
  37. package/dist/esm/charts/common/leaf-color.d.ts +33 -0
  38. package/dist/esm/charts/common/node-store.d.ts +81 -0
  39. package/dist/esm/charts/common/tree-chart.d.ts +48 -0
  40. package/dist/esm/charts/common/tree-chrome.d.ts +31 -0
  41. package/dist/esm/charts/common/tree-data.d.ts +54 -0
  42. package/dist/esm/charts/common/visible-extent.d.ts +51 -0
  43. package/dist/esm/charts/heatmap/heatmap-build.d.ts +86 -0
  44. package/dist/esm/charts/heatmap/heatmap-interact.d.ts +19 -0
  45. package/dist/esm/charts/heatmap/heatmap-render.d.ts +19 -0
  46. package/dist/esm/charts/heatmap/heatmap-y-axis.d.ts +46 -0
  47. package/dist/esm/charts/heatmap/heatmap.d.ts +117 -0
  48. package/dist/esm/charts/map/map.d.ts +67 -0
  49. package/dist/esm/charts/registry.d.ts +14 -0
  50. package/dist/esm/charts/series/glyphs/draw-areas.d.ts +30 -0
  51. package/dist/esm/charts/series/glyphs/draw-bars.d.ts +15 -0
  52. package/dist/esm/charts/series/glyphs/draw-lines.d.ts +34 -0
  53. package/dist/esm/charts/series/glyphs/draw-scatter.d.ts +33 -0
  54. package/dist/esm/charts/series/series-build.d.ts +228 -0
  55. package/dist/esm/charts/series/series-interact.d.ts +35 -0
  56. package/dist/esm/charts/series/series-render.d.ts +41 -0
  57. package/dist/esm/charts/series/series-type.d.ts +49 -0
  58. package/dist/esm/charts/series/series.d.ts +317 -0
  59. package/dist/esm/charts/sunburst/sunburst-interact.d.ts +7 -0
  60. package/dist/esm/charts/sunburst/sunburst-layout.d.ts +33 -0
  61. package/dist/esm/charts/sunburst/sunburst-render.d.ts +22 -0
  62. package/dist/esm/charts/sunburst/sunburst.d.ts +85 -0
  63. package/dist/esm/charts/treemap/treemap-interact.d.ts +12 -0
  64. package/dist/esm/charts/treemap/treemap-layout.d.ts +28 -0
  65. package/dist/esm/charts/treemap/treemap-render.d.ts +18 -0
  66. package/dist/esm/charts/treemap/treemap.d.ts +74 -0
  67. package/dist/esm/config.d.ts +27 -0
  68. package/dist/esm/data/lazy-row.d.ts +32 -0
  69. package/dist/esm/data/split-groups.d.ts +20 -0
  70. package/dist/esm/data/view-reader.d.ts +35 -0
  71. package/dist/esm/event-detail.d.ts +28 -0
  72. package/dist/esm/index.d.ts +3 -0
  73. package/dist/esm/interaction/hit-test.d.ts +30 -0
  74. package/dist/esm/interaction/host-sink-dom.d.ts +19 -0
  75. package/dist/esm/interaction/host-sink-message.d.ts +46 -0
  76. package/dist/esm/interaction/lazy-tooltip.d.ts +61 -0
  77. package/dist/esm/interaction/raw-event-forwarder.d.ts +27 -0
  78. package/dist/esm/interaction/spatial-grid.d.ts +15 -0
  79. package/dist/esm/interaction/tooltip-controller.d.ts +193 -0
  80. package/dist/esm/interaction/zoom-controller.d.ts +106 -0
  81. package/dist/esm/interaction/zoom-router.d.ts +48 -0
  82. package/dist/esm/layout/facet-grid.d.ts +126 -0
  83. package/dist/esm/layout/plot-layout.d.ts +104 -0
  84. package/dist/esm/layout/ticks.d.ts +17 -0
  85. package/dist/esm/map/mercator.d.ts +102 -0
  86. package/dist/esm/map/tile-cache.d.ts +38 -0
  87. package/dist/esm/map/tile-layer.d.ts +66 -0
  88. package/dist/esm/map/tile-loader.d.ts +52 -0
  89. package/dist/esm/map/tile-source.d.ts +66 -0
  90. package/dist/esm/perspective-viewer-charts.js +3 -0
  91. package/dist/esm/perspective-viewer-charts.js.map +7 -0
  92. package/dist/esm/plugin/charts.d.ts +40 -0
  93. package/dist/esm/plugin/plugin.d.ts +95 -0
  94. package/dist/esm/render/scheduler.d.ts +41 -0
  95. package/dist/esm/theme/gradient.d.ts +48 -0
  96. package/dist/esm/theme/palette.d.ts +13 -0
  97. package/dist/esm/theme/theme-snapshot.d.ts +7 -0
  98. package/dist/esm/theme/theme.d.ts +53 -0
  99. package/dist/esm/transport/protocol.d.ts +430 -0
  100. package/dist/esm/transport/renderer-transport.d.ts +201 -0
  101. package/dist/esm/utils/css.d.ts +1 -0
  102. package/dist/esm/utils/font-snapshot.d.ts +50 -0
  103. package/dist/esm/webgl/buffer-pool.d.ts +62 -0
  104. package/dist/esm/webgl/context-manager.d.ts +184 -0
  105. package/dist/esm/webgl/gradient-texture.d.ts +17 -0
  106. package/dist/esm/webgl/instanced-attrs.d.ts +44 -0
  107. package/dist/esm/webgl/plot-frame.d.ts +39 -0
  108. package/dist/esm/webgl/program-cache.d.ts +13 -0
  109. package/dist/esm/webgl/shader-manifest.d.ts +53 -0
  110. package/dist/esm/webgl/shader-registry.d.ts +22 -0
  111. package/dist/esm/worker/boot.d.ts +0 -0
  112. package/dist/esm/worker/dispatch.d.ts +9 -0
  113. package/dist/esm/worker/font-loader.d.ts +2 -0
  114. package/dist/esm/worker/renderer.worker.d.ts +115 -0
  115. package/dist/esm/worker/session-host.d.ts +26 -0
  116. package/package.json +47 -0
  117. package/src/css/perspective-viewer-charts.css +95 -0
  118. package/src/ts/axis/axis-primitives.ts +125 -0
  119. package/src/ts/axis/bar-axis.ts +345 -0
  120. package/src/ts/axis/canvas.ts +64 -0
  121. package/src/ts/axis/categorical-axis-core.ts +125 -0
  122. package/src/ts/axis/categorical-axis.ts +716 -0
  123. package/src/ts/axis/facet-chrome.ts +42 -0
  124. package/src/ts/axis/label-geometry.ts +188 -0
  125. package/src/ts/axis/legend.ts +218 -0
  126. package/src/ts/axis/numeric-axis.ts +353 -0
  127. package/src/ts/charts/candlestick/candlestick-build.ts +516 -0
  128. package/src/ts/charts/candlestick/candlestick-interact.ts +256 -0
  129. package/src/ts/charts/candlestick/candlestick-render.ts +387 -0
  130. package/src/ts/charts/candlestick/candlestick.ts +367 -0
  131. package/src/ts/charts/candlestick/glyphs/draw-candlesticks.ts +432 -0
  132. package/src/ts/charts/candlestick/glyphs/draw-ohlc.ts +317 -0
  133. package/src/ts/charts/canvas-types.ts +30 -0
  134. package/src/ts/charts/cartesian/cartesian-build.ts +616 -0
  135. package/src/ts/charts/cartesian/cartesian-interact.ts +355 -0
  136. package/src/ts/charts/cartesian/cartesian-render.ts +948 -0
  137. package/src/ts/charts/cartesian/cartesian.ts +469 -0
  138. package/src/ts/charts/cartesian/glyph.ts +81 -0
  139. package/src/ts/charts/cartesian/glyphs/density.ts +1263 -0
  140. package/src/ts/charts/cartesian/glyphs/lines.ts +320 -0
  141. package/src/ts/charts/cartesian/glyphs/points.ts +239 -0
  142. package/src/ts/charts/cartesian/label-interner.ts +56 -0
  143. package/src/ts/charts/cartesian/tooltip-lines.ts +80 -0
  144. package/src/ts/charts/chart-base.ts +840 -0
  145. package/src/ts/charts/chart.ts +427 -0
  146. package/src/ts/charts/common/band-layout.ts +63 -0
  147. package/src/ts/charts/common/categorical-y-chart.ts +81 -0
  148. package/src/ts/charts/common/category-axis-resolver.ts +314 -0
  149. package/src/ts/charts/common/chrome-cache.ts +79 -0
  150. package/src/ts/charts/common/draw-tooltip-box.ts +84 -0
  151. package/src/ts/charts/common/leaf-color.ts +92 -0
  152. package/src/ts/charts/common/node-store.ts +235 -0
  153. package/src/ts/charts/common/tree-chart.ts +76 -0
  154. package/src/ts/charts/common/tree-chrome.ts +123 -0
  155. package/src/ts/charts/common/tree-data.ts +623 -0
  156. package/src/ts/charts/common/visible-extent.ts +112 -0
  157. package/src/ts/charts/heatmap/heatmap-build.ts +426 -0
  158. package/src/ts/charts/heatmap/heatmap-interact.ts +274 -0
  159. package/src/ts/charts/heatmap/heatmap-render.ts +815 -0
  160. package/src/ts/charts/heatmap/heatmap-y-axis.ts +351 -0
  161. package/src/ts/charts/heatmap/heatmap.ts +368 -0
  162. package/src/ts/charts/map/map.ts +201 -0
  163. package/src/ts/charts/registry.ts +65 -0
  164. package/src/ts/charts/series/glyphs/draw-areas.ts +331 -0
  165. package/src/ts/charts/series/glyphs/draw-bars.ts +113 -0
  166. package/src/ts/charts/series/glyphs/draw-lines.ts +320 -0
  167. package/src/ts/charts/series/glyphs/draw-scatter.ts +328 -0
  168. package/src/ts/charts/series/series-build.ts +848 -0
  169. package/src/ts/charts/series/series-interact.ts +604 -0
  170. package/src/ts/charts/series/series-render.ts +1109 -0
  171. package/src/ts/charts/series/series-type.ts +99 -0
  172. package/src/ts/charts/series/series.ts +794 -0
  173. package/src/ts/charts/sunburst/sunburst-interact.ts +460 -0
  174. package/src/ts/charts/sunburst/sunburst-layout.ts +238 -0
  175. package/src/ts/charts/sunburst/sunburst-render.ts +887 -0
  176. package/src/ts/charts/sunburst/sunburst.ts +248 -0
  177. package/src/ts/charts/treemap/treemap-interact.ts +445 -0
  178. package/src/ts/charts/treemap/treemap-layout.ts +328 -0
  179. package/src/ts/charts/treemap/treemap-render.ts +886 -0
  180. package/src/ts/charts/treemap/treemap.ts +247 -0
  181. package/src/ts/config.ts +41 -0
  182. package/src/ts/data/lazy-row.ts +140 -0
  183. package/src/ts/data/split-groups.ts +97 -0
  184. package/src/ts/data/view-reader.ts +107 -0
  185. package/src/ts/event-detail.ts +44 -0
  186. package/src/ts/index.ts +53 -0
  187. package/src/ts/interaction/hit-test.ts +106 -0
  188. package/src/ts/interaction/host-sink-dom.ts +85 -0
  189. package/src/ts/interaction/host-sink-message.ts +75 -0
  190. package/src/ts/interaction/lazy-tooltip.ts +102 -0
  191. package/src/ts/interaction/raw-event-forwarder.ts +175 -0
  192. package/src/ts/interaction/spatial-grid.ts +100 -0
  193. package/src/ts/interaction/tooltip-controller.ts +407 -0
  194. package/src/ts/interaction/zoom-controller.ts +468 -0
  195. package/src/ts/interaction/zoom-router.ts +230 -0
  196. package/src/ts/layout/facet-grid.ts +346 -0
  197. package/src/ts/layout/plot-layout.ts +277 -0
  198. package/src/ts/layout/ticks.ts +168 -0
  199. package/src/ts/map/mercator.ts +204 -0
  200. package/src/ts/map/tile-cache.ts +96 -0
  201. package/src/ts/map/tile-layer.ts +382 -0
  202. package/src/ts/map/tile-loader.ts +143 -0
  203. package/src/ts/map/tile-source.ts +156 -0
  204. package/src/ts/plugin/charts.ts +286 -0
  205. package/src/ts/plugin/plugin.ts +668 -0
  206. package/src/ts/render/scheduler.ts +339 -0
  207. package/src/ts/shaders/area.frag.glsl +20 -0
  208. package/src/ts/shaders/area.vert.glsl +19 -0
  209. package/src/ts/shaders/bar.frag.glsl +25 -0
  210. package/src/ts/shaders/bar.vert.glsl +60 -0
  211. package/src/ts/shaders/candlestick-body.frag.glsl +19 -0
  212. package/src/ts/shaders/candlestick-body.vert.glsl +34 -0
  213. package/src/ts/shaders/density-extreme.frag.glsl +30 -0
  214. package/src/ts/shaders/density-mrt.frag.glsl +44 -0
  215. package/src/ts/shaders/density-mrt.vert.glsl +48 -0
  216. package/src/ts/shaders/density-resolve.frag.glsl +89 -0
  217. package/src/ts/shaders/density-resolve.vert.glsl +23 -0
  218. package/src/ts/shaders/density-splat.frag.glsl +34 -0
  219. package/src/ts/shaders/density-splat.vert.glsl +52 -0
  220. package/src/ts/shaders/gridline.frag.glsl +18 -0
  221. package/src/ts/shaders/gridline.vert.glsl +18 -0
  222. package/src/ts/shaders/heatmap.frag.glsl +23 -0
  223. package/src/ts/shaders/heatmap.vert.glsl +42 -0
  224. package/src/ts/shaders/line-uniform.frag.glsl +26 -0
  225. package/src/ts/shaders/line-uniform.vert.glsl +54 -0
  226. package/src/ts/shaders/line.frag.glsl +28 -0
  227. package/src/ts/shaders/line.vert.glsl +87 -0
  228. package/src/ts/shaders/scatter.frag.glsl +39 -0
  229. package/src/ts/shaders/scatter.vert.glsl +67 -0
  230. package/src/ts/shaders/sunburst-arc.frag.glsl +19 -0
  231. package/src/ts/shaders/sunburst-arc.vert.glsl +79 -0
  232. package/src/ts/shaders/tile.frag.glsl +27 -0
  233. package/src/ts/shaders/tile.vert.glsl +35 -0
  234. package/src/ts/shaders/treemap.frag.glsl +19 -0
  235. package/src/ts/shaders/treemap.vert.glsl +25 -0
  236. package/src/ts/shaders/y-scatter.frag.glsl +30 -0
  237. package/src/ts/shaders/y-scatter.vert.glsl +31 -0
  238. package/src/ts/theme/gradient.ts +312 -0
  239. package/src/ts/theme/palette.ts +64 -0
  240. package/src/ts/theme/theme-snapshot.ts +66 -0
  241. package/src/ts/theme/theme.ts +166 -0
  242. package/src/ts/transport/protocol.ts +497 -0
  243. package/src/ts/transport/renderer-transport.ts +788 -0
  244. package/src/ts/utils/css.ts +36 -0
  245. package/src/ts/utils/font-snapshot.ts +159 -0
  246. package/src/ts/webgl/buffer-pool.ts +163 -0
  247. package/src/ts/webgl/context-manager.ts +414 -0
  248. package/src/ts/webgl/gradient-texture.ts +84 -0
  249. package/src/ts/webgl/instanced-attrs.ts +139 -0
  250. package/src/ts/webgl/plot-frame.ts +91 -0
  251. package/src/ts/webgl/program-cache.ts +46 -0
  252. package/src/ts/webgl/shader-manifest.ts +148 -0
  253. package/src/ts/webgl/shader-registry.ts +97 -0
  254. package/src/ts/worker/boot.ts +22 -0
  255. package/src/ts/worker/dispatch.ts +99 -0
  256. package/src/ts/worker/font-loader.ts +89 -0
  257. package/src/ts/worker/renderer.worker.ts +734 -0
  258. package/src/ts/worker/session-host.ts +118 -0
@@ -0,0 +1,339 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ import type { WebGLContextManager } from "../webgl/context-manager";
14
+
15
+ /**
16
+ * Module-level render scheduler. The single entry point for driving a
17
+ * chart frame. Every render-triggering caller — upload chunks, zoom /
18
+ * pan, resize, theme invalidation, host-driven redraws — calls
19
+ * `requestRender(glManager, fullRender)` and awaits the returned
20
+ * promise.
21
+ *
22
+ * ## Guarantees
23
+ *
24
+ * 1. **At most one RAF queued globally.** The first request kicks
25
+ * off `requestAnimationFrame(drain)`; subsequent requests during
26
+ * that window enqueue without scheduling another callback.
27
+ *
28
+ * 2. **Coalesced per `glManager`.** The pending map is keyed by
29
+ * `WebGLContextManager`, so concurrent requests for the same
30
+ * chart share an entry — there is exactly one `_fullRender` call
31
+ * per glManager per RAF, regardless of how many requests landed.
32
+ *
33
+ * 3. **Promise resolves after the entry's own present.** Each
34
+ * waiter resolves when its entry's `_fullRender` +
35
+ * `awaitGpuFence` + `endFrame` chain completes. Independent
36
+ * glManagers run their fence waits in parallel (Phase 2 below),
37
+ * so a fast chart's waiters do not block on a slow chart in the
38
+ * same frame.
39
+ *
40
+ * 4. **At most one `_fullRender` per glManager per RAF.** GL
41
+ * contexts are not re-entered. `transferToImageBitmap` is called
42
+ * exactly once per glManager per frame, so the host blitter
43
+ * receives one bitmap per frame per chart and never an empty
44
+ * one.
45
+ *
46
+ * ## Drain ordering
47
+ *
48
+ * - **Phase 1 (synchronous):** iterate the pending snapshot and call
49
+ * each entry's `fullRender()` in one un-yielded loop. This pushes
50
+ * all GL command buffers to all contexts before any fence wait
51
+ * begins, letting per-context GPU work overlap.
52
+ *
53
+ * - **Phase 2 (parallel):** `Promise.all(snapshot.map(present))`
54
+ * where `present` does `await awaitGpuFence(); endFrame();
55
+ * resolve waiters`. Each entry's waiters resolve as soon as its
56
+ * own present completes — independent of other entries.
57
+ *
58
+ * ## Failure modes
59
+ *
60
+ * - A throw from `fullRender()` rejects that entry's waiters and
61
+ * drops the entry; other entries continue to drain normally.
62
+ *
63
+ * - A rejection from `awaitGpuFence` calls `endFrame` anyway (so
64
+ * canvas state stays consistent — `transferToImageBitmap` clears
65
+ * the offscreen even on the error path) and rejects that entry's
66
+ * waiters.
67
+ *
68
+ * ## Snapshot bypass
69
+ *
70
+ * The scheduler always pairs `_fullRender` with `endFrame()`, which
71
+ * calls `transferToImageBitmap` and clears the offscreen. PNG
72
+ * export needs `gl.readPixels` against an intact backbuffer, so
73
+ * `snapshotPng` deliberately calls `_fullRender` directly and skips
74
+ * `endFrame`. That is the only sanctioned bypass; everything else
75
+ * goes through `requestRender`.
76
+ */
77
+
78
+ interface Entry {
79
+ glManager: WebGLContextManager;
80
+ fullRender: () => void;
81
+ waiters: PromiseWithResolvers<void>[];
82
+ }
83
+
84
+ const pending = new Map<WebGLContextManager, Entry>();
85
+ let rafId = 0;
86
+
87
+ /**
88
+ * Set of `glManager`s currently in `present()` (Phase 2 — between
89
+ * Phase 1 paint and `endFrame`). Mutations to these canvases must be
90
+ * deferred until Phase 2 completes, otherwise they corrupt the bitmap
91
+ * that `transferToImageBitmap` ships:
92
+ *
93
+ * - `glManager.resize` sets `canvas.width = N`, which the spec
94
+ * mandates clears the drawing buffer immediately (out-of-band
95
+ * from the GL command queue).
96
+ * - `glManager.clear` queues `gl.clear` after Phase 1's draw
97
+ * commands but before the fence; if it executes before
98
+ * `transferToImageBitmap`, the canvas is wiped.
99
+ *
100
+ * Either path produces a blank frame on the host. `deferIfDraining`
101
+ * is the gate; sibling message handlers (resize, clear) wrap their
102
+ * canvas-mutating bodies in it.
103
+ */
104
+ const inFlight = new Set<WebGLContextManager>();
105
+ const deferred = new Map<WebGLContextManager, (() => void)[]>();
106
+
107
+ /**
108
+ * Request a coalesced render of `glManager` whose body is
109
+ * `fullRender`. Returns a promise that resolves when this entry's
110
+ * Phase 2 (`awaitGpuFence` + `endFrame`) completes.
111
+ *
112
+ * If a request is already pending for the same glManager, the new
113
+ * call's `fullRender` closure replaces the prior one (latest call
114
+ * wins; closures read chart state lazily so this is functionally a
115
+ * no-op, but keeps the closure fresh) and the returned promise
116
+ * resolves alongside the existing waiters.
117
+ */
118
+ export function requestRender(
119
+ glManager: WebGLContextManager,
120
+ fullRender: () => void,
121
+ ): Promise<void> {
122
+ let entry = pending.get(glManager);
123
+ if (entry) {
124
+ entry.fullRender = fullRender;
125
+ } else {
126
+ entry = { glManager, fullRender, waiters: [] };
127
+ pending.set(glManager, entry);
128
+ }
129
+
130
+ const waiter = Promise.withResolvers<void>();
131
+ entry.waiters.push(waiter);
132
+
133
+ if (!rafId) {
134
+ rafId = scheduleFrame(drain);
135
+ }
136
+
137
+ return waiter.promise;
138
+ }
139
+
140
+ /**
141
+ * Run `op` synchronously if no drain `present()` is currently active
142
+ * for `glManager`. Otherwise queue `op` to run as soon as that
143
+ * glManager's in-flight `present()` completes (after `endFrame`,
144
+ * after the resolved/rejected waiters).
145
+ *
146
+ * Used by canvas-mutating callers — `WorkerRenderer.resize`,
147
+ * `WorkerRenderer.clear` — to avoid wiping the offscreen between
148
+ * Phase 1 paint and Phase 2 `endFrame`. `glManager.resize` setting
149
+ * `canvas.width = N` clears the drawing buffer immediately
150
+ * (per the WebGL spec, out-of-band from the GL command queue), and
151
+ * a clear that lands in Phase 2's fence-wait yield window corrupts
152
+ * the bitmap that `transferToImageBitmap` ships, producing a blank
153
+ * frame on the host.
154
+ *
155
+ * Deferred ops execute in `present()`'s `finally` clause, so they
156
+ * land *after* the in-flight drain's bitmap has been shipped and
157
+ * before the next drain starts. If a deferred op itself triggers a
158
+ * `requestRender`, the resulting entry queues into `pending` and
159
+ * the drain's tail check (`pending.size > 0 → scheduleFrame(drain)`)
160
+ * picks it up for the next RAF.
161
+ */
162
+ export function deferIfDraining(
163
+ glManager: WebGLContextManager,
164
+ op: () => void,
165
+ ): void {
166
+ if (!inFlight.has(glManager)) {
167
+ op();
168
+ return;
169
+ }
170
+
171
+ let ops = deferred.get(glManager);
172
+ if (!ops) {
173
+ ops = [];
174
+ deferred.set(glManager, ops);
175
+ }
176
+
177
+ ops.push(op);
178
+ }
179
+
180
+ /**
181
+ * Test-only: clear pending state. Production callers must not use
182
+ * this — outstanding waiters are silently dropped.
183
+ */
184
+ export function _resetForTest(): void {
185
+ if (rafId) {
186
+ cancelFrame(rafId);
187
+ rafId = 0;
188
+ }
189
+
190
+ pending.clear();
191
+ inFlight.clear();
192
+ deferred.clear();
193
+ }
194
+
195
+ // async function drain(): Promise<void> {
196
+ // rafId = 0;
197
+
198
+ // // Snapshot the pending set up front so requests that arrive during
199
+ // // the drain (in microtasks between Phase 2 awaits, or in tasks
200
+ // // unblocked by `awaitGpuFence`'s yields) queue into the next RAF
201
+ // // rather than mutating this drain's working set.
202
+ // const snapshot = Array.from(pending.values());
203
+ // pending.clear();
204
+
205
+ // // Phase 1: synchronously queue GL commands for every entry. One
206
+ // // un-yielded loop so all contexts have their commands submitted
207
+ // // before any fence wait begins; otherwise fence waits serialize
208
+ // // behind each subsequent `_fullRender`'s draw submissions.
209
+ // const ready: Entry[] = [];
210
+ // for (const entry of snapshot) {
211
+ // try {
212
+ // entry.fullRender();
213
+ // ready.push(entry);
214
+ // } catch (err) {
215
+ // console.error("scheduler: fullRender threw", err);
216
+ // for (const w of entry.waiters) {
217
+ // w.reject(err);
218
+ // }
219
+ // }
220
+ // }
221
+
222
+ // // Phase 2: run each entry's fence + endFrame + waiter-resolve as
223
+ // // its own async task. `Promise.all` joins for the drain wall
224
+ // // time, but per-entry waiters resolve as soon as their entry's
225
+ // // present completes — a fast chart in this frame is not held up
226
+ // // by a slow chart.
227
+ // await Promise.all(ready.map(present));
228
+ // }
229
+
230
+ async function drain(): Promise<void> {
231
+ const snapshot = Array.from(pending.values());
232
+ pending.clear();
233
+ const ready: Entry[] = [];
234
+ for (const entry of snapshot) {
235
+ try {
236
+ // Apply any dimension change recorded by
237
+ // `glManager.requestResize` *before* the paint, in the
238
+ // same un-yielded synchronous Phase 1 loop. This pairs
239
+ // the canvas-clearing `canvas.width = N` assignment
240
+ // with the immediately-following `_fullRender`, so the
241
+ // browser's compositor only ever observes the canvas
242
+ // post-paint. In direct/in-process modes the visible
243
+ // canvas IS the GL canvas, and a clear-without-matching-
244
+ // paint in the previous task would otherwise present an
245
+ // empty frame to the user.
246
+ entry.glManager.applyPendingResize();
247
+ entry.fullRender();
248
+ ready.push(entry);
249
+ } catch (err) {
250
+ console.error("scheduler: fullRender threw", err);
251
+ for (const w of entry.waiters) {
252
+ w.reject(err);
253
+ }
254
+ }
255
+ }
256
+
257
+ await Promise.all(ready.map(present));
258
+
259
+ // Now (and only now) clear rafId. If new requests landed during
260
+ // this drain, schedule the next RAF.
261
+ rafId = 0;
262
+ if (pending.size > 0) {
263
+ rafId = scheduleFrame(drain);
264
+ }
265
+ }
266
+
267
+ async function present(entry: Entry): Promise<void> {
268
+ // Mark this glManager as in-flight *synchronously*, before the
269
+ // first await. `Promise.all(ready.map(present))` calls each
270
+ // `present` synchronously to collect its returned promise, so
271
+ // every entry's glManager is registered in `inFlight` before
272
+ // any fence-wait yields and before any sibling message handler
273
+ // can run. Mutations posted by sibling handlers (resize, clear)
274
+ // route through `deferIfDraining` and queue into `deferred`
275
+ // until the `finally` block flushes them.
276
+ inFlight.add(entry.glManager);
277
+ try {
278
+ await entry.glManager.awaitGpuFence();
279
+ entry.glManager.endFrame();
280
+ for (const w of entry.waiters) {
281
+ w.resolve();
282
+ }
283
+ } catch (err) {
284
+ console.error("scheduler: present failed", err);
285
+ // Still call `endFrame` so the canvas state is consistent —
286
+ // `transferToImageBitmap` clears the offscreen, and skipping
287
+ // it would leave a stale image bound to a context the host
288
+ // already considers presented.
289
+ try {
290
+ entry.glManager.endFrame();
291
+ } catch {
292
+ // Swallow: already in a failure path.
293
+ }
294
+
295
+ for (const w of entry.waiters) {
296
+ w.reject(err);
297
+ }
298
+ } finally {
299
+ // Bitmap shipped (or error reported). Re-open the canvas to
300
+ // mutations and flush any deferred ops in arrival order.
301
+ // Deferred ops may call `requestRender`; the resulting
302
+ // entry queues into `pending` and the drain's tail check
303
+ // picks it up for the next RAF.
304
+ inFlight.delete(entry.glManager);
305
+ const ops = deferred.get(entry.glManager);
306
+ if (ops) {
307
+ deferred.delete(entry.glManager);
308
+ for (const op of ops) {
309
+ try {
310
+ op();
311
+ } catch (err) {
312
+ console.error("scheduler: deferred op threw", err);
313
+ }
314
+ }
315
+ }
316
+ }
317
+ }
318
+
319
+ /**
320
+ * RAF in worker scope is exposed by `DedicatedWorkerGlobalScope` for
321
+ * `OffscreenCanvas` painting and is the same primitive as on the
322
+ * main thread. Fall back to `setTimeout(16)` for environments
323
+ * without RAF (jsdom, headless tests without a polyfill).
324
+ */
325
+ function scheduleFrame(cb: () => void): number {
326
+ if (typeof requestAnimationFrame === "function") {
327
+ return requestAnimationFrame(cb);
328
+ }
329
+
330
+ return setTimeout(cb, 16) as unknown as number;
331
+ }
332
+
333
+ function cancelFrame(id: number): void {
334
+ if (typeof cancelAnimationFrame === "function") {
335
+ cancelAnimationFrame(id);
336
+ } else {
337
+ clearTimeout(id);
338
+ }
339
+ }
@@ -0,0 +1,20 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ precision highp float;
14
+
15
+ uniform vec3 u_color;
16
+ uniform float u_opacity;
17
+
18
+ void main() {
19
+ gl_FragColor = vec4(u_color, u_opacity);
20
+ }
@@ -0,0 +1,19 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ attribute vec2 a_position;
14
+
15
+ uniform mat4 u_projection;
16
+
17
+ void main() {
18
+ gl_Position = u_projection * vec4(a_position, 0.0, 1.0);
19
+ }
@@ -0,0 +1,25 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ precision highp float;
14
+
15
+ varying vec3 v_color;
16
+ varying float v_hover;
17
+ varying vec2 v_local;
18
+
19
+ void main() {
20
+ vec3 color = v_color;
21
+ if (v_hover > 0.5) {
22
+ color = mix(color, vec3(1.0), 0.25);
23
+ }
24
+ gl_FragColor = vec4(color, 1.0);
25
+ }
@@ -0,0 +1,60 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ // Per-vertex (divisor=0): unit quad corner in [0,1]^2 order
14
+ // 0=(0,0) bottom-left, 1=(1,0) bottom-right, 2=(0,1) top-left, 3=(1,1) top-right
15
+ attribute vec2 a_corner;
16
+
17
+ // Per-instance (divisor=1) bar attributes
18
+ attribute float a_x_center;
19
+ attribute float a_half_width;
20
+ attribute float a_y0;
21
+ attribute float a_y1;
22
+ attribute vec3 a_color;
23
+ attribute float a_series_id;
24
+ attribute float a_axis;
25
+
26
+ uniform mat4 u_proj_left;
27
+ uniform mat4 u_proj_right;
28
+ uniform float u_hover_series;
29
+ // 0 = vertical bars (categorical X, numeric Y).
30
+ // 1 = horizontal bars (numeric X, categorical Y). Instance attributes
31
+ // stay in "logical" form — x_center + halfWidth on the categorical axis,
32
+ // y0/y1 on the value axis — and we transpose at projection time.
33
+ uniform float u_horizontal;
34
+
35
+ varying vec3 v_color;
36
+ varying float v_hover;
37
+ varying vec2 v_local;
38
+
39
+ void main() {
40
+ vec2 pos;
41
+ if (u_horizontal > 0.5) {
42
+ pos = vec2(
43
+ mix(a_y0, a_y1, a_corner.x),
44
+ a_x_center + (a_corner.y - 0.5) * 2.0 * a_half_width
45
+ );
46
+ } else {
47
+ pos = vec2(
48
+ a_x_center + (a_corner.x - 0.5) * 2.0 * a_half_width,
49
+ mix(a_y0, a_y1, a_corner.y)
50
+ );
51
+ }
52
+
53
+ // Branch on per-instance axis flag. Both matrices are set each frame.
54
+ mat4 proj = a_axis < 0.5 ? u_proj_left : u_proj_right;
55
+ gl_Position = proj * vec4(pos, 0.0, 1.0);
56
+
57
+ v_color = a_color;
58
+ v_hover = abs(a_series_id - u_hover_series) < 0.5 ? 1.0 : 0.0;
59
+ v_local = a_corner;
60
+ }
@@ -0,0 +1,19 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ precision highp float;
14
+
15
+ varying vec3 v_color;
16
+
17
+ void main() {
18
+ gl_FragColor = vec4(v_color, 1.0);
19
+ }
@@ -0,0 +1,34 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ // Filled-rectangle shader for candlestick bodies. A trimmed copy of
14
+ // the bar shader without the hover-highlight / dual-axis plumbing that
15
+ // candlestick doesn't need.
16
+
17
+ attribute vec2 a_corner; // per-vertex, divisor=0: (0|1, 0|1)
18
+
19
+ attribute float a_x_center; // per-instance
20
+ attribute float a_half_width;
21
+ attribute float a_y0;
22
+ attribute float a_y1;
23
+ attribute vec3 a_color;
24
+
25
+ uniform mat4 u_projection;
26
+
27
+ varying vec3 v_color;
28
+
29
+ void main() {
30
+ float x = a_x_center + (a_corner.x - 0.5) * 2.0 * a_half_width;
31
+ float y = mix(a_y0, a_y1, a_corner.y);
32
+ gl_Position = u_projection * vec4(x, y, 0.0, 1.0);
33
+ v_color = a_color;
34
+ }
@@ -0,0 +1,30 @@
1
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
3
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
4
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
5
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
6
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
8
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
10
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
+
13
+ precision highp float;
14
+
15
+ varying vec2 v_uv;
16
+ varying float v_color_t;
17
+
18
+ void main() {
19
+ float r = length(v_uv);
20
+ if(r >= 1.0) {
21
+ discard;
22
+ }
23
+
24
+ // Signed deviation from the gradient midpoint, in [-1, +1]. Split
25
+ // into two non-negative channels so the FBO format stays in [0, 1]
26
+ // and so the `MAX` blend at the call site keeps the strongest
27
+ // contributor of each sign separately.
28
+ float dev = (v_color_t - 0.5) * 2.0;
29
+ gl_FragColor = vec4(max(0.0, dev), max(0.0, -dev), 0.0, 0.0);
30
+ }
@@ -0,0 +1,44 @@
1
+ #version 300 es
2
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
3
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
4
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
5
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
6
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
7
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
8
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
9
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
10
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
11
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
12
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
13
+
14
+ // MRT variant of the splat fragment used by `extreme` mode when the
15
+ // running context advertises `OES_draw_buffers_indexed`. One pass
16
+ // writes to both targets:
17
+ //
18
+ // - location 0 (heat FBO, ADD blend): same payload as
19
+ // density-splat.frag.glsl — `(w, w·t, 0, 0)`.
20
+ // - location 1 (extreme FBO, MAX blend): same payload as
21
+ // density-extreme.frag.glsl — split signed deviation.
22
+
23
+ precision highp float;
24
+
25
+ uniform float u_intensity;
26
+
27
+ in vec2 v_uv;
28
+ in float v_color_t;
29
+
30
+ layout(location = 0) out vec4 outHeat;
31
+ layout(location = 1) out vec4 outExtreme;
32
+
33
+ void main() {
34
+ float r = length(v_uv);
35
+ float w = max(0.0f, 1.0f - r);
36
+ w = w * w * u_intensity;
37
+ if(w <= 0.0f) {
38
+ discard;
39
+ }
40
+
41
+ outHeat = vec4(w, w * v_color_t, 0.0f, 0.0f);
42
+ float dev = (v_color_t - 0.5f) * 2.0f;
43
+ outExtreme = vec4(max(0.0f, dev), max(0.0f, -dev), 0.0f, 0.0f);
44
+ }
@@ -0,0 +1,48 @@
1
+ #version 300 es
2
+ // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
3
+ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
4
+ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
5
+ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
6
+ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
7
+ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
8
+ // ┃ Copyright (c) 2017, the Perspective Authors. ┃
9
+ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
10
+ // ┃ This file is part of the Perspective library, distributed under the terms ┃
11
+ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
12
+ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
13
+
14
+ // GLSL ES 3.00 variant of `density-splat.vert.glsl`, mirroring its math
15
+ // 1:1 in the modern dialect (`in`/`out` instead of `attribute`/
16
+ // `varying`). Paired with `density-mrt.frag.glsl` for the MRT fast
17
+ // path used by `extreme` mode on WebGL2 — the program's vertex and
18
+ // fragment shaders must share a GLSL version, so the WebGL1-style
19
+ // splat vert can't be linked against a 300 ES MRT frag.
20
+
21
+ in vec2 a_corner;
22
+ in vec2 a_position;
23
+ in float a_color_value;
24
+
25
+ uniform mat4 u_projection;
26
+ uniform vec2 u_radius_ndc;
27
+ uniform vec2 u_color_range;
28
+
29
+ out vec2 v_uv;
30
+ out float v_color_t;
31
+
32
+ void main() {
33
+ vec4 center = u_projection * vec4(a_position, 0.0, 1.0);
34
+ gl_Position = center + vec4(a_corner * u_radius_ndc * center.w, 0.0, 0.0);
35
+
36
+ v_uv = a_corner;
37
+
38
+ float cmin = u_color_range.x;
39
+ float cmax = u_color_range.y;
40
+ if(cmax <= cmin) {
41
+ v_color_t = 0.5;
42
+ } else if(cmin < 0.0 && cmax > 0.0) {
43
+ float denom = max(-cmin, cmax);
44
+ v_color_t = clamp(0.5 + 0.5 * (a_color_value / denom), 0.0, 1.0);
45
+ } else {
46
+ v_color_t = clamp((a_color_value - cmin) / (cmax - cmin), 0.0, 1.0);
47
+ }
48
+ }