@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,320 @@
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
+ import type { SeriesChart } from "../series";
15
+ import {
16
+ createLineCornerBuffer,
17
+ getInstancing,
18
+ } from "../../../webgl/instanced-attrs";
19
+ import { compileProgram } from "../../../webgl/program-cache";
20
+ import lineVert from "../../../shaders/line-uniform.vert.glsl";
21
+ import lineFrag from "../../../shaders/line-uniform.frag.glsl";
22
+
23
+ type GL = WebGL2RenderingContext | WebGLRenderingContext;
24
+
25
+ interface LineProgramCache {
26
+ program: WebGLProgram;
27
+ cornerBuffer: WebGLBuffer;
28
+ u_projection: WebGLUniformLocation | null;
29
+ u_color: WebGLUniformLocation | null;
30
+ u_resolution: WebGLUniformLocation | null;
31
+ u_line_width: WebGLUniformLocation | null;
32
+ a_start: number;
33
+ a_end: number;
34
+ a_corner: number;
35
+ }
36
+
37
+ interface LineRun {
38
+ /**
39
+ * Byte offset into the per-series GPU buffer at the start of this run.
40
+ */
41
+ offsetBytes: number;
42
+
43
+ /**
44
+ * Number of points in this run; the run draws `count - 1` segments.
45
+ */
46
+ count: number;
47
+ }
48
+
49
+ interface LineSeriesEntry {
50
+ seriesId: number;
51
+ axis: 0 | 1;
52
+ color: [number, number, number];
53
+
54
+ /**
55
+ * GPU buffer holding `[x0,y0,x1,y1,...]` for every run in the series.
56
+ */
57
+ gpuBuffer: WebGLBuffer;
58
+
59
+ /**
60
+ * Run offsets into `gpuBuffer`. Empty when the series has no segments.
61
+ */
62
+ runs: LineRun[];
63
+ }
64
+
65
+ /**
66
+ * Persistent line glyph state. Built in `rebuildBuffers` (called from
67
+ * `uploadAndRender`) and reused across pan/zoom frames. Legacy code
68
+ * rebuilt the JS-side polylines + GPU buffers on every frame, which
69
+ * dominated the per-frame budget at high N.
70
+ */
71
+ interface LineBuffers {
72
+ /**
73
+ * One entry per line series (hidden series included; draw skips them).
74
+ */
75
+ series: LineSeriesEntry[];
76
+ }
77
+
78
+ /**
79
+ * Reusable Float32 scratch for assembling polyline points before GPU
80
+ * upload. Sized lazily and grown on demand. Replaces the legacy
81
+ * `scratch: number[]` (boxed) → `Float32Array.from(scratch)` (copy)
82
+ * pattern.
83
+ */
84
+ let _lineScratch: Float32Array = new Float32Array(0);
85
+
86
+ function ensureLineScratch(n: number): Float32Array {
87
+ if (_lineScratch.length >= n) {
88
+ return _lineScratch;
89
+ }
90
+
91
+ _lineScratch = new Float32Array(Math.max(n, _lineScratch.length * 2));
92
+ return _lineScratch;
93
+ }
94
+
95
+ /**
96
+ * Line glyph for {@link SeriesChart}. Owns its program + per-series
97
+ * GPU buffers privately; chart routes lifecycle through
98
+ * `_glyphs.lines`.
99
+ */
100
+ export class LineGlyph {
101
+ private _program: LineProgramCache | null = null;
102
+ private _buffers: LineBuffers | null = null;
103
+
104
+ private ensureProgram(glManager: WebGLContextManager): LineProgramCache {
105
+ if (this._program) {
106
+ return this._program;
107
+ }
108
+
109
+ const cornerBuffer = createLineCornerBuffer(glManager.gl);
110
+ const partial = compileProgram<Omit<LineProgramCache, "cornerBuffer">>(
111
+ glManager,
112
+ "bar-line",
113
+ lineVert,
114
+ lineFrag,
115
+ ["u_projection", "u_color", "u_resolution", "u_line_width"],
116
+ ["a_start", "a_end", "a_corner"],
117
+ );
118
+ this._program = { ...partial, cornerBuffer };
119
+ return this._program;
120
+ }
121
+
122
+ /**
123
+ * Drop persistent line buffers. Subsequent draws will no-op until
124
+ * the next `rebuildBuffers` call.
125
+ */
126
+ invalidateBuffers(chart: SeriesChart): void {
127
+ const buf = this._buffers;
128
+ if (!buf || !chart._glManager) {
129
+ this._buffers = null;
130
+ return;
131
+ }
132
+
133
+ const gl = chart._glManager.gl;
134
+ for (const s of buf.series) {
135
+ gl.deleteBuffer(s.gpuBuffer);
136
+ }
137
+
138
+ this._buffers = null;
139
+ }
140
+
141
+ /**
142
+ * Rebuild the per-series GPU buffers for line glyphs. Called once
143
+ * per data load (and once after `restyle()` because palette colors
144
+ * are captured on the {@link LineSeriesEntry}). The buffer contents
145
+ * encode `[x,y]` points in run-major order; one `bufferData` per
146
+ * series. After this, every `draw` call rebinds + dispatches with
147
+ * no further uploads until the next data load.
148
+ */
149
+ rebuildBuffers(chart: SeriesChart, glManager: WebGLContextManager): void {
150
+ const lineSeries = chart._lineSeries;
151
+ if (lineSeries.length === 0) {
152
+ this._buffers = null;
153
+ return;
154
+ }
155
+
156
+ const N = chart._numCategories;
157
+ if (N === 0) {
158
+ this._buffers = null;
159
+ return;
160
+ }
161
+
162
+ this.ensureProgram(glManager);
163
+ const gl = glManager.gl;
164
+ const samples = chart._samples;
165
+ const valid = chart._sampleValid;
166
+ const xOrigin = chart._categoryOrigin;
167
+ const positions = chart._categoryPositions;
168
+ const S = chart._series.length;
169
+
170
+ const entries: LineSeriesEntry[] = [];
171
+ for (const s of lineSeries) {
172
+ // Walk the per-category sample grid for this series, breaking
173
+ // into contiguous valid runs. Write directly into a pre-sized
174
+ // Float32 scratch — no boxed JS arrays, no `Float32Array.from`.
175
+ const scratch = ensureLineScratch(N * 2);
176
+ const runs: LineRun[] = [];
177
+ let write = 0;
178
+ let runStart = 0;
179
+ for (let c = 0; c < N; c++) {
180
+ const idx = c * S + s.seriesId;
181
+ const ok = (valid[idx >> 3] >> (idx & 7)) & 1;
182
+ if (ok) {
183
+ const x = positions ? positions[c] - xOrigin : c;
184
+ scratch[write++] = x;
185
+ scratch[write++] = samples[idx];
186
+ } else if (write > runStart) {
187
+ const count = (write - runStart) / 2;
188
+ if (count >= 2) {
189
+ runs.push({ offsetBytes: runStart * 4, count });
190
+ }
191
+
192
+ runStart = write;
193
+ }
194
+ }
195
+
196
+ if (write > runStart) {
197
+ const count = (write - runStart) / 2;
198
+ if (count >= 2) {
199
+ runs.push({ offsetBytes: runStart * 4, count });
200
+ }
201
+ }
202
+
203
+ if (runs.length === 0) {
204
+ continue;
205
+ }
206
+
207
+ const buf = gl.createBuffer()!;
208
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
209
+ gl.bufferData(
210
+ gl.ARRAY_BUFFER,
211
+ scratch.subarray(0, write),
212
+ gl.STATIC_DRAW,
213
+ );
214
+
215
+ entries.push({
216
+ seriesId: s.seriesId,
217
+ axis: s.axis,
218
+ color: [s.color[0], s.color[1], s.color[2]],
219
+ gpuBuffer: buf,
220
+ runs,
221
+ });
222
+ }
223
+
224
+ this._buffers = { series: entries };
225
+ }
226
+
227
+ /**
228
+ * Bind the persistent vertex buffers and dispatch one instanced draw
229
+ * per (series, run). Skips hidden series via `_hiddenSeries`.
230
+ */
231
+ draw(
232
+ chart: SeriesChart,
233
+ gl: GL,
234
+ glManager: WebGLContextManager,
235
+ projLeft: Float32Array,
236
+ projRight: Float32Array,
237
+ ): void {
238
+ const buf = this._buffers;
239
+ const cache = this._program;
240
+ if (!buf || !cache || buf.series.length === 0) {
241
+ return;
242
+ }
243
+
244
+ const dpr = glManager.dpr;
245
+ gl.useProgram(cache.program);
246
+ gl.uniform2f(cache.u_resolution, gl.canvas.width, gl.canvas.height);
247
+ gl.uniform1f(
248
+ cache.u_line_width,
249
+ chart._pluginConfig.line_width_px * dpr,
250
+ );
251
+
252
+ const instancing = getInstancing(glManager);
253
+ const { setDivisor, drawArraysInstanced } = instancing;
254
+
255
+ gl.bindBuffer(gl.ARRAY_BUFFER, cache.cornerBuffer);
256
+ gl.enableVertexAttribArray(cache.a_corner);
257
+ gl.vertexAttribPointer(cache.a_corner, 1, gl.FLOAT, false, 0, 0);
258
+ setDivisor(cache.a_corner, 0);
259
+
260
+ const stride = 2 * Float32Array.BYTES_PER_ELEMENT;
261
+ const hidden = chart._hiddenSeries;
262
+ for (const s of buf.series) {
263
+ if (hidden.has(s.seriesId)) {
264
+ continue;
265
+ }
266
+
267
+ gl.bindBuffer(gl.ARRAY_BUFFER, s.gpuBuffer);
268
+ gl.uniformMatrix4fv(
269
+ cache.u_projection,
270
+ false,
271
+ s.axis === 1 ? projRight : projLeft,
272
+ );
273
+
274
+ const color = chart._series[s.seriesId].color;
275
+ gl.uniform4f(cache.u_color, color[0], color[1], color[2], 1.0);
276
+
277
+ gl.enableVertexAttribArray(cache.a_start);
278
+ setDivisor(cache.a_start, 1);
279
+ gl.enableVertexAttribArray(cache.a_end);
280
+ setDivisor(cache.a_end, 1);
281
+
282
+ for (const run of s.runs) {
283
+ gl.vertexAttribPointer(
284
+ cache.a_start,
285
+ 2,
286
+ gl.FLOAT,
287
+ false,
288
+ stride,
289
+ run.offsetBytes,
290
+ );
291
+ gl.vertexAttribPointer(
292
+ cache.a_end,
293
+ 2,
294
+ gl.FLOAT,
295
+ false,
296
+ stride,
297
+ run.offsetBytes + stride,
298
+ );
299
+ drawArraysInstanced(gl.TRIANGLE_STRIP, 0, 4, run.count - 1);
300
+ }
301
+ }
302
+
303
+ setDivisor(cache.a_start, 0);
304
+ setDivisor(cache.a_end, 0);
305
+ }
306
+
307
+ destroy(chart: SeriesChart): void {
308
+ const gl = chart._glManager?.gl;
309
+ if (gl) {
310
+ this.invalidateBuffers(chart);
311
+ const cache = this._program;
312
+ if (cache) {
313
+ gl.deleteBuffer(cache.cornerBuffer);
314
+ }
315
+ }
316
+
317
+ this._program = null;
318
+ this._buffers = null;
319
+ }
320
+ }
@@ -0,0 +1,328 @@
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
+ import type { SeriesChart } from "../series";
15
+ import { compileProgram } from "../../../webgl/program-cache";
16
+ import scatterVert from "../../../shaders/y-scatter.vert.glsl";
17
+ import scatterFrag from "../../../shaders/y-scatter.frag.glsl";
18
+
19
+ type GL = WebGL2RenderingContext | WebGLRenderingContext;
20
+
21
+ interface ScatterProgramCache {
22
+ program: WebGLProgram;
23
+ posLeftBuffer: WebGLBuffer;
24
+ posRightBuffer: WebGLBuffer;
25
+ colorLeftBuffer: WebGLBuffer;
26
+ colorRightBuffer: WebGLBuffer;
27
+ u_projection: WebGLUniformLocation | null;
28
+ u_point_size: WebGLUniformLocation | null;
29
+ a_position: number;
30
+ a_color: number;
31
+ }
32
+
33
+ /**
34
+ * Persistent scatter glyph state — left/right axis position + color
35
+ * buffers built once at data load. Pan/zoom redraws rebind without
36
+ * uploading.
37
+ */
38
+ interface ScatterBuffers {
39
+ leftCount: number;
40
+ rightCount: number;
41
+ }
42
+
43
+ /**
44
+ * Reusable Float32 scratch for point assembly. Two buckets (positions
45
+ * and colors) packed into one buffer; sized lazily.
46
+ */
47
+ let _posScratch: Float32Array = new Float32Array(0);
48
+ let _colScratch: Float32Array = new Float32Array(0);
49
+
50
+ function ensureScratch(n: number): void {
51
+ if (_posScratch.length < n * 2) {
52
+ _posScratch = new Float32Array(Math.max(n * 2, _posScratch.length * 2));
53
+ }
54
+
55
+ if (_colScratch.length < n * 3) {
56
+ _colScratch = new Float32Array(Math.max(n * 3, _colScratch.length * 2));
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Scatter glyph for {@link SeriesChart}. Owns the program + per-axis
62
+ * (position, color) GPU buffers. Single program/buffer set; left and
63
+ * right axes are merged into shared buffers with sub-ranges.
64
+ */
65
+ export class ScatterGlyph {
66
+ private _program: ScatterProgramCache | null = null;
67
+ private _buffers: ScatterBuffers | null = null;
68
+
69
+ private ensureProgram(glManager: WebGLContextManager): ScatterProgramCache {
70
+ if (this._program) {
71
+ return this._program;
72
+ }
73
+
74
+ const gl = glManager.gl;
75
+ const partial = compileProgram<
76
+ Omit<
77
+ ScatterProgramCache,
78
+ | "posLeftBuffer"
79
+ | "posRightBuffer"
80
+ | "colorLeftBuffer"
81
+ | "colorRightBuffer"
82
+ >
83
+ >(
84
+ glManager,
85
+ "bar-scatter",
86
+ scatterVert,
87
+ scatterFrag,
88
+ ["u_projection", "u_point_size"],
89
+ ["a_position", "a_color"],
90
+ );
91
+ this._program = {
92
+ ...partial,
93
+ posLeftBuffer: gl.createBuffer()!,
94
+ posRightBuffer: gl.createBuffer()!,
95
+ colorLeftBuffer: gl.createBuffer()!,
96
+ colorRightBuffer: gl.createBuffer()!,
97
+ };
98
+ return this._program;
99
+ }
100
+
101
+ /**
102
+ * Drop persistent scatter buffer state. The underlying GL buffer
103
+ * objects on `_program` are reused (owned by the program cache,
104
+ * not the per-build buffer view).
105
+ */
106
+ invalidateBuffers(_chart: SeriesChart): void {
107
+ this._buffers = null;
108
+ }
109
+
110
+ /**
111
+ * Build merged per-axis (position, color) buffers for every visible
112
+ * scatter series and upload them. Hidden series are excluded — call
113
+ * this from data-load and from the legend-toggle path so the GPU
114
+ * buffers always reflect the current visible mask.
115
+ */
116
+ rebuildBuffers(chart: SeriesChart, glManager: WebGLContextManager): void {
117
+ const scatterSeries = chart._scatterSeries;
118
+ if (scatterSeries.length === 0) {
119
+ this._buffers = null;
120
+ return;
121
+ }
122
+
123
+ const N = chart._numCategories;
124
+ const S = chart._series.length;
125
+ if (N === 0 || S === 0) {
126
+ this._buffers = null;
127
+ return;
128
+ }
129
+
130
+ const cache = this.ensureProgram(glManager);
131
+ const gl = glManager.gl;
132
+
133
+ const samples = chart._samples;
134
+ const valid = chart._sampleValid;
135
+ const positions = chart._categoryPositions;
136
+ const xOrigin = chart._categoryOrigin;
137
+ const hidden = chart._hiddenSeries;
138
+
139
+ // Two-pass: first count to size scratch, then fill. Avoids a
140
+ // number[] growth path while still accommodating both axes in a
141
+ // single pair of buffers.
142
+ let leftCount = 0;
143
+ let rightCount = 0;
144
+ for (const s of scatterSeries) {
145
+ if (hidden.has(s.seriesId)) {
146
+ continue;
147
+ }
148
+
149
+ for (let c = 0; c < N; c++) {
150
+ const idx = c * S + s.seriesId;
151
+ if (!((valid[idx >> 3] >> (idx & 7)) & 1)) {
152
+ continue;
153
+ }
154
+
155
+ if (s.axis === 1) {
156
+ rightCount++;
157
+ } else {
158
+ leftCount++;
159
+ }
160
+ }
161
+ }
162
+
163
+ const total = leftCount + rightCount;
164
+ if (total === 0) {
165
+ this._buffers = null;
166
+ return;
167
+ }
168
+
169
+ ensureScratch(total);
170
+
171
+ // Fill left bucket from `[0, leftCount)`, right bucket from
172
+ // `[leftCount, total)` — single typed-array allocation each.
173
+ let leftWrite = 0;
174
+ let rightWrite = leftCount;
175
+ for (const s of scatterSeries) {
176
+ if (hidden.has(s.seriesId)) {
177
+ continue;
178
+ }
179
+
180
+ const r = s.color[0];
181
+ const g = s.color[1];
182
+ const b = s.color[2];
183
+ for (let c = 0; c < N; c++) {
184
+ const idx = c * S + s.seriesId;
185
+ if (!((valid[idx >> 3] >> (idx & 7)) & 1)) {
186
+ continue;
187
+ }
188
+
189
+ const x = positions ? positions[c] - xOrigin : c;
190
+ const v = samples[idx];
191
+ if (s.axis === 1) {
192
+ _posScratch[rightWrite * 2] = x;
193
+ _posScratch[rightWrite * 2 + 1] = v;
194
+ _colScratch[rightWrite * 3] = r;
195
+ _colScratch[rightWrite * 3 + 1] = g;
196
+ _colScratch[rightWrite * 3 + 2] = b;
197
+ rightWrite++;
198
+ } else {
199
+ _posScratch[leftWrite * 2] = x;
200
+ _posScratch[leftWrite * 2 + 1] = v;
201
+ _colScratch[leftWrite * 3] = r;
202
+ _colScratch[leftWrite * 3 + 1] = g;
203
+ _colScratch[leftWrite * 3 + 2] = b;
204
+ leftWrite++;
205
+ }
206
+ }
207
+ }
208
+
209
+ if (leftCount > 0) {
210
+ gl.bindBuffer(gl.ARRAY_BUFFER, cache.posLeftBuffer);
211
+ gl.bufferData(
212
+ gl.ARRAY_BUFFER,
213
+ _posScratch.subarray(0, leftCount * 2),
214
+ gl.STATIC_DRAW,
215
+ );
216
+ gl.bindBuffer(gl.ARRAY_BUFFER, cache.colorLeftBuffer);
217
+ gl.bufferData(
218
+ gl.ARRAY_BUFFER,
219
+ _colScratch.subarray(0, leftCount * 3),
220
+ gl.STATIC_DRAW,
221
+ );
222
+ }
223
+
224
+ if (rightCount > 0) {
225
+ gl.bindBuffer(gl.ARRAY_BUFFER, cache.posRightBuffer);
226
+ gl.bufferData(
227
+ gl.ARRAY_BUFFER,
228
+ _posScratch.subarray(leftCount * 2, total * 2),
229
+ gl.STATIC_DRAW,
230
+ );
231
+ gl.bindBuffer(gl.ARRAY_BUFFER, cache.colorRightBuffer);
232
+ gl.bufferData(
233
+ gl.ARRAY_BUFFER,
234
+ _colScratch.subarray(leftCount * 3, total * 3),
235
+ gl.STATIC_DRAW,
236
+ );
237
+ }
238
+
239
+ this._buffers = { leftCount, rightCount };
240
+ }
241
+
242
+ /**
243
+ * Bind the persistent left/right buffers and issue up to two draw
244
+ * calls. No per-frame allocations or buffer uploads.
245
+ */
246
+ draw(
247
+ chart: SeriesChart,
248
+ gl: GL,
249
+ glManager: WebGLContextManager,
250
+ projLeft: Float32Array,
251
+ projRight: Float32Array,
252
+ ): void {
253
+ const buf = this._buffers;
254
+ const cache = this._program;
255
+ if (!buf || !cache) {
256
+ return;
257
+ }
258
+
259
+ if (buf.leftCount === 0 && buf.rightCount === 0) {
260
+ return;
261
+ }
262
+
263
+ const dpr = glManager.dpr;
264
+ gl.useProgram(cache.program);
265
+ gl.uniform1f(
266
+ cache.u_point_size,
267
+ chart._pluginConfig.point_size_px * dpr,
268
+ );
269
+
270
+ drawBucket(
271
+ gl,
272
+ cache,
273
+ cache.posLeftBuffer,
274
+ cache.colorLeftBuffer,
275
+ buf.leftCount,
276
+ projLeft,
277
+ );
278
+ drawBucket(
279
+ gl,
280
+ cache,
281
+ cache.posRightBuffer,
282
+ cache.colorRightBuffer,
283
+ buf.rightCount,
284
+ projRight,
285
+ );
286
+ }
287
+
288
+ destroy(chart: SeriesChart): void {
289
+ const gl = chart._glManager?.gl;
290
+ if (gl) {
291
+ const cache = this._program;
292
+ if (cache) {
293
+ gl.deleteBuffer(cache.posLeftBuffer);
294
+ gl.deleteBuffer(cache.posRightBuffer);
295
+ gl.deleteBuffer(cache.colorLeftBuffer);
296
+ gl.deleteBuffer(cache.colorRightBuffer);
297
+ }
298
+ }
299
+
300
+ this._program = null;
301
+ this._buffers = null;
302
+ }
303
+ }
304
+
305
+ function drawBucket(
306
+ gl: GL,
307
+ cache: ScatterProgramCache,
308
+ posBuf: WebGLBuffer,
309
+ colBuf: WebGLBuffer,
310
+ count: number,
311
+ proj: Float32Array,
312
+ ): void {
313
+ if (count === 0) {
314
+ return;
315
+ }
316
+
317
+ gl.uniformMatrix4fv(cache.u_projection, false, proj);
318
+
319
+ gl.bindBuffer(gl.ARRAY_BUFFER, posBuf);
320
+ gl.enableVertexAttribArray(cache.a_position);
321
+ gl.vertexAttribPointer(cache.a_position, 2, gl.FLOAT, false, 0, 0);
322
+
323
+ gl.bindBuffer(gl.ARRAY_BUFFER, colBuf);
324
+ gl.enableVertexAttribArray(cache.a_color);
325
+ gl.vertexAttribPointer(cache.a_color, 3, gl.FLOAT, false, 0, 0);
326
+
327
+ gl.drawArrays(gl.POINTS, 0, count);
328
+ }