uplot-webgpu 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CANVAS_PROXY.md +602 -0
  2. package/README.md +854 -0
  3. package/favicon.ico +0 -0
  4. package/index.html +14 -0
  5. package/index.js +21 -0
  6. package/original/paths.canvas2d/bars.js +252 -0
  7. package/original/paths.canvas2d/catmullRomCentrip.js +125 -0
  8. package/original/paths.canvas2d/linear.js +170 -0
  9. package/original/paths.canvas2d/monotoneCubic.js +68 -0
  10. package/original/paths.canvas2d/points.js +66 -0
  11. package/original/paths.canvas2d/spline.js +103 -0
  12. package/original/paths.canvas2d/stepped.js +124 -0
  13. package/original/paths.canvas2d/utils.js +301 -0
  14. package/original/uPlot.canvas2d.js +3548 -0
  15. package/package.json +110 -0
  16. package/paths/bars.js +253 -0
  17. package/paths/catmullRomCentrip.js +126 -0
  18. package/paths/linear.js +171 -0
  19. package/paths/monotoneCubic.js +69 -0
  20. package/paths/points.js +67 -0
  21. package/paths/spline.js +104 -0
  22. package/paths/stepped.js +125 -0
  23. package/paths/utils.js +301 -0
  24. package/scripts/uPlot.css +168 -0
  25. package/scripts/uPlot.d.ts +26 -0
  26. package/scripts/uPlot.js +3687 -0
  27. package/scripts/utils/dom.js +124 -0
  28. package/scripts/utils/domClasses.js +22 -0
  29. package/scripts/utils/feats.js +13 -0
  30. package/scripts/utils/fmtDate.js +398 -0
  31. package/scripts/utils/opts.js +844 -0
  32. package/scripts/utils/strings.js +22 -0
  33. package/scripts/utils/sync.js +27 -0
  34. package/scripts/utils/utils.js +692 -0
  35. package/scripts/webgpu/GPUPath.d.ts +46 -0
  36. package/scripts/webgpu/GPUPath.js +633 -0
  37. package/scripts/webgpu/GPUPath.ts +634 -0
  38. package/scripts/webgpu/WebGPURenderer.d.ts +176 -0
  39. package/scripts/webgpu/WebGPURenderer.js +4256 -0
  40. package/scripts/webgpu/WebGPURenderer.ts +4257 -0
  41. package/scripts/webgpu/browserSmokeHarness.js +105 -0
  42. package/scripts/webgpu/exporters.d.ts +8 -0
  43. package/scripts/webgpu/exporters.js +212 -0
  44. package/scripts/webgpu/shaders.d.ts +2 -0
  45. package/scripts/webgpu/shaders.js +76 -0
  46. package/scripts/webgpu/shaders.ts +77 -0
  47. package/scripts/webgpu/smokeTest.d.ts +2 -0
  48. package/scripts/webgpu/smokeTest.js +144 -0
  49. package/scripts/webgpu/webgpu-ambient.d.ts +41 -0
  50. package/tinybuild.config.js +109 -0
package/package.json ADDED
@@ -0,0 +1,110 @@
1
+ {
2
+ "name": "uplot-webgpu",
3
+ "version": "0.1.0",
4
+ "description": "Dummy tinybuild app, for building. Replace description before publishing.",
5
+ "main": "./dist/uPlot.js",
6
+ "module": "./dist/uPlot.esm.js",
7
+ "types": "./dist/uPlot.d.ts",
8
+ "type": "module",
9
+ "scripts": {
10
+ "start": "tinybuild",
11
+ "build": "tinybuild build",
12
+ "serve": "tinybuild serve",
13
+ "init": "node tinybuild/init.js",
14
+ "concurrent": "concurrently \"npm run python\" \"npm start\"",
15
+ "dev": "npm run pip && npm i --save-dev concurrently && npm i --save-dev nodemon && npm run concurrent",
16
+ "startdev": "nodemon --exec \"node tinybuild.js\" -e ejs,js,ts,jsx,tsx,css,html,jpg,png,scss,txt,csv",
17
+ "python": "python python/server.py",
18
+ "pip": "pip install quart && pip install websockets",
19
+ "pwa": "npm i workbox-cli && workbox generateSW node_server/pwa/workbox-config.js && npm run build && npm start",
20
+ "electron": "electron ./electron",
21
+ "android": "npx cap open android",
22
+ "ios": "npx cap open ios",
23
+ "tauri": "tauri",
24
+ "parity": "node scripts/parity_check.mjs",
25
+ "dropin": "node scripts/dropin_check.mjs",
26
+ "size:audit": "node scripts/size_audit.mjs"
27
+ },
28
+ "author": "",
29
+ "license": "",
30
+ "keywords": [
31
+ "esbuild"
32
+ ],
33
+ "devDependencies": {
34
+ "@webgpu/types": "~0.1.70"
35
+ },
36
+ "nodemonConfig": {
37
+ "env": {
38
+ "NODEMON": true
39
+ },
40
+ "ignore": [
41
+ "dist/",
42
+ ".temp/"
43
+ ]
44
+ },
45
+ "exports": {
46
+ ".": {
47
+ "types": "./index.d.ts",
48
+ "import": "./index.js",
49
+ "default": "./index.js"
50
+ },
51
+ "./uPlot": {
52
+ "types": "./uPlot.d.ts",
53
+ "import": "./uPlot.js",
54
+ "default": "./uPlot.js"
55
+ },
56
+ "./webgpu": {
57
+ "types": "./webgpu/WebGPURenderer.d.ts",
58
+ "import": "./webgpu/WebGPURenderer.js",
59
+ "default": "./webgpu/WebGPURenderer.js"
60
+ },
61
+ "./webgpu/WebGPURenderer": {
62
+ "types": "./webgpu/WebGPURenderer.d.ts",
63
+ "import": "./webgpu/WebGPURenderer.js",
64
+ "default": "./webgpu/WebGPURenderer.js"
65
+ },
66
+ "./webgpu/GPUPath": {
67
+ "types": "./webgpu/GPUPath.d.ts",
68
+ "import": "./webgpu/GPUPath.js",
69
+ "default": "./webgpu/GPUPath.js"
70
+ },
71
+ "./core": {
72
+ "types": "./index.d.ts",
73
+ "import": "./index.js",
74
+ "default": "./index.js"
75
+ },
76
+ "./full": {
77
+ "types": "./index.full.d.ts",
78
+ "import": "./index.full.js",
79
+ "default": "./index.full.js"
80
+ },
81
+ "./bench": {
82
+ "types": "./index.bench.d.ts",
83
+ "import": "./index.bench.js",
84
+ "default": "./index.bench.js"
85
+ },
86
+ "./renderer": {
87
+ "types": "./index.renderer.d.ts",
88
+ "import": "./index.renderer.js",
89
+ "default": "./index.renderer.js"
90
+ },
91
+ "./canvas2d-reference": {
92
+ "types": "./old/uPlot.canvas2d.d.ts",
93
+ "import": "./old/uPlot.canvas2d.js",
94
+ "default": "./old/uPlot.canvas2d.js"
95
+ },
96
+ "./smoke": {
97
+ "import": "./webgpu/smokeTest.js",
98
+ "default": "./webgpu/smokeTest.js"
99
+ },
100
+ "./uPlot.css": {
101
+ "import": "./uPlot.css",
102
+ "default": "./uPlot.css"
103
+ },
104
+ "./webgpu/exporters": {
105
+ "types": "./webgpu/exporters.d.ts",
106
+ "import": "./webgpu/exporters.js",
107
+ "default": "./webgpu/exporters.js"
108
+ }
109
+ }
110
+ }
package/paths/bars.js ADDED
@@ -0,0 +1,253 @@
1
+ import { GPUPath } from '../scripts/webgpu/GPUPath.js';
2
+ import { abs, floor, min, max, inf, ifNull, EMPTY_OBJ, fnOrSelf, clamp, retArg0, EMPTY_ARR } from '../scripts/utils/utils.js';
3
+ import { orient, rectV, rectH } from './utils.js';
4
+
5
+ function findColWidth(dataX, dataY, valToPosX, scaleX, xDim, xOff, colWid = inf) {
6
+ if (dataX.length > 1) {
7
+ // prior index with non-undefined y data
8
+ let prevIdx = null;
9
+
10
+ // scan full dataset for smallest adjacent delta
11
+ // will not work properly for non-linear x scales, since does not do expensive valToPosX calcs till end
12
+ for (let i = 0, minDelta = Infinity; i < dataX.length; i++) {
13
+ if (dataY[i] !== undefined) {
14
+ if (prevIdx != null) {
15
+ let delta = abs(dataX[i] - dataX[prevIdx]);
16
+
17
+ if (delta < minDelta) {
18
+ minDelta = delta;
19
+ colWid = abs(valToPosX(dataX[i], scaleX, xDim, xOff) - valToPosX(dataX[prevIdx], scaleX, xDim, xOff));
20
+ }
21
+ }
22
+
23
+ prevIdx = i;
24
+ }
25
+ }
26
+ }
27
+
28
+ return colWid;
29
+ }
30
+
31
+ export function bars(opts) {
32
+ opts = opts || EMPTY_OBJ;
33
+ const size = ifNull(opts.size, [0.6, inf, 1]);
34
+ const align = opts.align || 0;
35
+ const _extraGap = (opts.gap || 0);
36
+
37
+ let ro = opts.radius;
38
+
39
+ ro =
40
+ // [valueRadius, baselineRadius]
41
+ ro == null ? [0, 0] :
42
+ typeof ro == 'number' ? [ro, 0] : ro;
43
+
44
+ const radiusFn = fnOrSelf(ro);
45
+
46
+ const gapFactor = 1 - size[0];
47
+ const _maxWidth = ifNull(size[1], inf);
48
+ const _minWidth = ifNull(size[2], 1);
49
+
50
+ const disp = ifNull(opts.disp, EMPTY_OBJ);
51
+ const _each = ifNull(opts.each, _ => {});
52
+
53
+ const { fill: dispFills, stroke: dispStrokes } = disp;
54
+
55
+ return (u, seriesIdx, idx0, idx1) => {
56
+ let { pxRatio } = u;
57
+
58
+ return orient(u, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim) => {
59
+ let pxRound = series.pxRound;
60
+ let _align = align;
61
+
62
+ let extraGap = _extraGap * pxRatio;
63
+ let maxWidth = _maxWidth * pxRatio;
64
+ let minWidth = _minWidth * pxRatio;
65
+
66
+ let valRadius, baseRadius;
67
+
68
+ if (scaleX.ori == 0)
69
+ [valRadius, baseRadius] = radiusFn(u, seriesIdx);
70
+ else
71
+ [baseRadius, valRadius] = radiusFn(u, seriesIdx);
72
+
73
+ const _dirX = scaleX.dir * (scaleX.ori == 0 ? 1 : -1);
74
+ // const _dirY = scaleY.dir * (scaleY.ori == 1 ? 1 : -1);
75
+
76
+ let rect = scaleX.ori == 0 ? rectH : rectV;
77
+
78
+ let each = scaleX.ori == 0 ? _each : (u, seriesIdx, i, top, lft, hgt, wid) => {
79
+ _each(u, seriesIdx, i, lft, top, wid, hgt);
80
+ };
81
+
82
+ // band where this series is the "from" edge
83
+ let band = ifNull(u.bands, EMPTY_ARR).find(b => b.series[0] == seriesIdx);
84
+
85
+ let fillDir = band != null ? band.dir : 0;
86
+ let fillTo = series.fillTo(u, seriesIdx, series.min, series.max, fillDir);
87
+ let fillToY = pxRound(valToPosY(fillTo, scaleY, yDim, yOff))
88
+
89
+ // barWid is to center of stroke
90
+ let xShift, barWid, fullGap, colWid = xDim;
91
+
92
+ let strokeWidth = pxRound(series.width * pxRatio);
93
+
94
+ let multiPath = false;
95
+
96
+ let fillColors = null;
97
+ let fillPaths = null;
98
+ let strokeColors = null;
99
+ let strokePaths = null;
100
+
101
+ if (dispFills != null && (strokeWidth == 0 || dispStrokes != null)) {
102
+ multiPath = true;
103
+
104
+ fillColors = dispFills.values(u, seriesIdx, idx0, idx1);
105
+ fillPaths = new Map();
106
+ (new Set(fillColors)).forEach(color => {
107
+ if (color != null)
108
+ fillPaths.set(color, new GPUPath());
109
+ });
110
+
111
+ if (strokeWidth > 0) {
112
+ strokeColors = dispStrokes.values(u, seriesIdx, idx0, idx1);
113
+ strokePaths = new Map();
114
+ (new Set(strokeColors)).forEach(color => {
115
+ if (color != null)
116
+ strokePaths.set(color, new GPUPath());
117
+ });
118
+ }
119
+ }
120
+
121
+ let { x0, size } = disp;
122
+
123
+ if (x0 != null && size != null) {
124
+ _align = 1;
125
+ dataX = x0.values(u, seriesIdx, idx0, idx1);
126
+
127
+ if (x0.unit == 2)
128
+ dataX = dataX.map(pct => u.posToVal(xOff + pct * xDim, scaleX.key, true));
129
+
130
+ // assumes uniform sizes, for now
131
+ let sizes = size.values(u, seriesIdx, idx0, idx1);
132
+
133
+ if (size.unit == 2)
134
+ barWid = sizes[0] * xDim;
135
+ else
136
+ barWid = valToPosX(sizes[0], scaleX, xDim, xOff) - valToPosX(0, scaleX, xDim, xOff); // assumes linear scale (delta from 0)
137
+
138
+ colWid = findColWidth(dataX, dataY, valToPosX, scaleX, xDim, xOff, colWid);
139
+
140
+ let gapWid = colWid - barWid;
141
+ fullGap = gapWid + extraGap;
142
+ }
143
+ else {
144
+ colWid = findColWidth(dataX, dataY, valToPosX, scaleX, xDim, xOff, colWid);
145
+
146
+ let gapWid = colWid * gapFactor;
147
+
148
+ fullGap = gapWid + extraGap;
149
+ barWid = colWid - fullGap;
150
+ }
151
+
152
+ if (fullGap < 1)
153
+ fullGap = 0;
154
+
155
+ if (strokeWidth >= barWid / 2)
156
+ strokeWidth = 0;
157
+
158
+ // for small gaps, disable pixel snapping since gap inconsistencies become noticible and annoying
159
+ if (fullGap < 5)
160
+ pxRound = retArg0;
161
+
162
+ let insetStroke = fullGap > 0;
163
+
164
+ let rawBarWid = colWid - fullGap - (insetStroke ? strokeWidth : 0);
165
+
166
+ barWid = pxRound(clamp(rawBarWid, minWidth, maxWidth));
167
+
168
+ xShift = (_align == 0 ? barWid / 2 : _align == _dirX ? 0 : barWid) - _align * _dirX * ((_align == 0 ? extraGap / 2 : 0) + (insetStroke ? strokeWidth / 2 : 0));
169
+
170
+
171
+ const _paths = {stroke: null, fill: null, clip: null, band: null, gaps: null, flags: 0}; // disp, geom
172
+
173
+ const stroke = multiPath ? null : new GPUPath();
174
+
175
+ let dataY0 = null;
176
+
177
+ if (band != null)
178
+ dataY0 = u.data[band.series[1]];
179
+ else {
180
+ let { y0, y1 } = disp;
181
+
182
+ if (y0 != null && y1 != null) {
183
+ dataY = y1.values(u, seriesIdx, idx0, idx1);
184
+ dataY0 = y0.values(u, seriesIdx, idx0, idx1);
185
+ }
186
+ }
187
+
188
+ let radVal = valRadius * barWid;
189
+ let radBase = baseRadius * barWid;
190
+
191
+ for (let i = _dirX == 1 ? idx0 : idx1; i >= idx0 && i <= idx1; i += _dirX) {
192
+ let yVal = dataY[i];
193
+
194
+ if (yVal == null)
195
+ continue;
196
+
197
+ if (dataY0 != null) {
198
+ let yVal0 = dataY0[i] ?? 0;
199
+
200
+ if (yVal - yVal0 == 0)
201
+ continue;
202
+
203
+ fillToY = valToPosY(yVal0, scaleY, yDim, yOff);
204
+ }
205
+
206
+ let xVal = scaleX.distr != 2 || disp != null ? dataX[i] : i;
207
+
208
+ // TODO: all xPos can be pre-computed once for all series in aligned set
209
+ let xPos = valToPosX(xVal, scaleX, xDim, xOff);
210
+ let yPos = valToPosY(ifNull(yVal, fillTo), scaleY, yDim, yOff);
211
+
212
+ let lft = pxRound(xPos - xShift);
213
+ let btm = pxRound(max(yPos, fillToY));
214
+ let top = pxRound(min(yPos, fillToY));
215
+ // this includes the stroke
216
+ let barHgt = btm - top;
217
+
218
+ if (yVal != null && yVal != fillTo) {
219
+ let rv = yVal < 0 ? radBase : radVal;
220
+ let rb = yVal < 0 ? radVal : radBase;
221
+
222
+ if (multiPath) {
223
+ if (strokeWidth > 0 && strokeColors[i] != null)
224
+ rect(strokePaths.get(strokeColors[i]), lft, top + floor(strokeWidth / 2), barWid, max(0, barHgt - strokeWidth), rv, rb);
225
+
226
+ if (fillColors[i] != null)
227
+ rect(fillPaths.get(fillColors[i]), lft, top + floor(strokeWidth / 2), barWid, max(0, barHgt - strokeWidth), rv, rb);
228
+ }
229
+ else
230
+ rect(stroke, lft, top + floor(strokeWidth / 2), barWid, max(0, barHgt - strokeWidth), rv, rb);
231
+
232
+ each(u, seriesIdx, i,
233
+ lft - strokeWidth / 2,
234
+ top,
235
+ barWid + strokeWidth,
236
+ barHgt,
237
+ );
238
+ }
239
+ }
240
+
241
+ if (strokeWidth > 0)
242
+ _paths.stroke = multiPath ? strokePaths : stroke;
243
+ else if (!multiPath) {
244
+ _paths._fill = series.width == 0 ? series._fill : series._stroke ?? series._fill;
245
+ _paths.width = 0;
246
+ }
247
+
248
+ _paths.fill = multiPath ? fillPaths : stroke;
249
+
250
+ return _paths;
251
+ });
252
+ };
253
+ }
@@ -0,0 +1,126 @@
1
+ import { GPUPath } from '../scripts/webgpu/GPUPath.js';
2
+ import { pow, sqrt } from '../scripts/utils/utils.js';
3
+ import { splineInterp } from "./spline.js";
4
+
5
+ export function catmullRomCentrip(opts) {
6
+ return splineInterp(catmullRomFitting, opts);
7
+ }
8
+
9
+ // adapted from https://gist.github.com/nicholaswmin/c2661eb11cad5671d816 (MIT)
10
+ /**
11
+ * Interpolates a Catmull-Rom Spline through a series of x/y points
12
+ * Converts the CR Spline to Cubic Beziers for use with SVG items
13
+ *
14
+ * If 'alpha' is 0.5 then the 'Centripetal' variant is used
15
+ * If 'alpha' is 1 then the 'Chordal' variant is used
16
+ *
17
+ */
18
+ function catmullRomFitting(xCoords, yCoords, moveTo, lineTo, bezierCurveTo, pxRound) {
19
+ const alpha = 0.5;
20
+
21
+ const path = new GPUPath();
22
+
23
+ const dataLen = xCoords.length;
24
+
25
+ let p0x,
26
+ p0y,
27
+ p1x,
28
+ p1y,
29
+ p2x,
30
+ p2y,
31
+ p3x,
32
+ p3y,
33
+ bp1x,
34
+ bp1y,
35
+ bp2x,
36
+ bp2y,
37
+ d1,
38
+ d2,
39
+ d3,
40
+ A,
41
+ B,
42
+ N,
43
+ M,
44
+ d3powA,
45
+ d2powA,
46
+ d3pow2A,
47
+ d2pow2A,
48
+ d1pow2A,
49
+ d1powA;
50
+
51
+ moveTo(path, pxRound(xCoords[0]), pxRound(yCoords[0]));
52
+
53
+ for (let i = 0; i < dataLen - 1; i++) {
54
+ let p0i = i == 0 ? 0 : i - 1;
55
+
56
+ p0x = xCoords[p0i];
57
+ p0y = yCoords[p0i];
58
+
59
+ p1x = xCoords[i];
60
+ p1y = yCoords[i];
61
+
62
+ p2x = xCoords[i + 1];
63
+ p2y = yCoords[i + 1];
64
+
65
+ if (i + 2 < dataLen) {
66
+ p3x = xCoords[i + 2];
67
+ p3y = yCoords[i + 2];
68
+ } else {
69
+ p3x = p2x;
70
+ p3y = p2y;
71
+ }
72
+
73
+ d1 = sqrt(pow(p0x - p1x, 2) + pow(p0y - p1y, 2));
74
+ d2 = sqrt(pow(p1x - p2x, 2) + pow(p1y - p2y, 2));
75
+ d3 = sqrt(pow(p2x - p3x, 2) + pow(p2y - p3y, 2));
76
+
77
+ // Catmull-Rom to Cubic Bezier conversion matrix
78
+
79
+ // A = 2d1^2a + 3d1^a * d2^a + d3^2a
80
+ // B = 2d3^2a + 3d3^a * d2^a + d2^2a
81
+
82
+ // [ 0 1 0 0 ]
83
+ // [ -d2^2a /N A/N d1^2a /N 0 ]
84
+ // [ 0 d3^2a /M B/M -d2^2a /M ]
85
+ // [ 0 0 1 0 ]
86
+
87
+ d3powA = pow(d3, alpha);
88
+ d3pow2A = pow(d3, alpha * 2);
89
+ d2powA = pow(d2, alpha);
90
+ d2pow2A = pow(d2, alpha * 2);
91
+ d1powA = pow(d1, alpha);
92
+ d1pow2A = pow(d1, alpha * 2);
93
+
94
+ A = 2 * d1pow2A + 3 * d1powA * d2powA + d2pow2A;
95
+ B = 2 * d3pow2A + 3 * d3powA * d2powA + d2pow2A;
96
+ N = 3 * d1powA * (d1powA + d2powA);
97
+
98
+ if (N > 0)
99
+ N = 1 / N;
100
+
101
+ M = 3 * d3powA * (d3powA + d2powA);
102
+
103
+ if (M > 0)
104
+ M = 1 / M;
105
+
106
+ bp1x = (-d2pow2A * p0x + A * p1x + d1pow2A * p2x) * N;
107
+ bp1y = (-d2pow2A * p0y + A * p1y + d1pow2A * p2y) * N;
108
+
109
+ bp2x = (d3pow2A * p1x + B * p2x - d2pow2A * p3x) * M;
110
+ bp2y = (d3pow2A * p1y + B * p2y - d2pow2A * p3y) * M;
111
+
112
+ if (bp1x == 0 && bp1y == 0) {
113
+ bp1x = p1x;
114
+ bp1y = p1y;
115
+ }
116
+
117
+ if (bp2x == 0 && bp2y == 0) {
118
+ bp2x = p2x;
119
+ bp2y = p2y;
120
+ }
121
+
122
+ bezierCurveTo(path, bp1x, bp1y, bp2x, bp2y, p2x, p2y);
123
+ }
124
+
125
+ return path;
126
+ }
@@ -0,0 +1,171 @@
1
+ import { GPUPath } from '../scripts/webgpu/GPUPath.js';
2
+ import { nonNullIdxs, ifNull } from '../scripts/utils/utils.js';
3
+ import { orient, clipGaps, lineToH, lineToV, clipBandLine, BAND_CLIP_FILL, bandFillClipDirs, findGaps } from './utils.js';
4
+
5
+ function _drawAcc(lineTo) {
6
+ return (stroke, accX, minY, maxY, inY, outY) => {
7
+ if (minY != maxY) {
8
+ if (inY != minY && outY != minY)
9
+ lineTo(stroke, accX, minY);
10
+ if (inY != maxY && outY != maxY)
11
+ lineTo(stroke, accX, maxY);
12
+
13
+ lineTo(stroke, accX, outY);
14
+ }
15
+ };
16
+ }
17
+
18
+ const drawAccH = _drawAcc(lineToH);
19
+ const drawAccV = _drawAcc(lineToV);
20
+
21
+ export function linear(opts) {
22
+ return (u, seriesIdx, idx0, idx1) => {
23
+ return orient(u, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim) => {
24
+ [idx0, idx1] = nonNullIdxs(dataY, idx0, idx1);
25
+
26
+ let pxRound = series.pxRound;
27
+
28
+ let alignGaps = opts?.alignGaps ?? series.alignGaps ?? 0;
29
+
30
+ let pixelForX = val => pxRound(valToPosX(val, scaleX, xDim, xOff));
31
+ let pixelForY = val => pxRound(valToPosY(val, scaleY, yDim, yOff));
32
+
33
+ let lineTo, drawAcc;
34
+
35
+ if (scaleX.ori == 0) {
36
+ lineTo = lineToH;
37
+ drawAcc = drawAccH;
38
+ }
39
+ else {
40
+ lineTo = lineToV;
41
+ drawAcc = drawAccV;
42
+ }
43
+
44
+ const dir = scaleX.dir * (scaleX.ori == 0 ? 1 : -1);
45
+
46
+ const _paths = {stroke: new GPUPath(), fill: null, clip: null, band: null, gaps: null, flags: BAND_CLIP_FILL};
47
+ const stroke = _paths.stroke;
48
+
49
+ let hasGap = false;
50
+
51
+ // decimate when number of points >= 4x available pixels
52
+ const decimate = idx1 - idx0 >= xDim * 4;
53
+
54
+ if (decimate) {
55
+ let xForPixel = pos => u.posToVal(pos, scaleX.key, true);
56
+
57
+ let minY = null,
58
+ maxY = null,
59
+ inY, outY, drawnAtX;
60
+
61
+ let accX = pixelForX(dataX[dir == 1 ? idx0 : idx1]);
62
+
63
+ let idx0px = pixelForX(dataX[idx0]);
64
+ let idx1px = pixelForX(dataX[idx1]);
65
+
66
+ // tracks limit of current x bucket to avoid having to get x pixel for every x value
67
+ let nextAccXVal = xForPixel(dir == 1 ? idx0px + 1 : idx1px - 1);
68
+
69
+ for (let i = dir == 1 ? idx0 : idx1; i >= idx0 && i <= idx1; i += dir) {
70
+ let xVal = dataX[i];
71
+ let reuseAccX = dir == 1 ? (xVal < nextAccXVal) : (xVal > nextAccXVal);
72
+ let x = reuseAccX ? accX : pixelForX(xVal);
73
+
74
+ let yVal = dataY[i];
75
+
76
+ if (x == accX) {
77
+ if (yVal != null) {
78
+ outY = yVal;
79
+
80
+ if (minY == null) {
81
+ lineTo(stroke, x, pixelForY(outY));
82
+ inY = minY = maxY = outY;
83
+ } else {
84
+ if (outY < minY)
85
+ minY = outY;
86
+ else if (outY > maxY)
87
+ maxY = outY;
88
+ }
89
+ }
90
+ else {
91
+ if (yVal === null)
92
+ hasGap = true;
93
+ }
94
+ }
95
+ else {
96
+ if (minY != null)
97
+ drawAcc(stroke, accX, pixelForY(minY), pixelForY(maxY), pixelForY(inY), pixelForY(outY));
98
+
99
+ if (yVal != null) {
100
+ outY = yVal;
101
+ lineTo(stroke, x, pixelForY(outY));
102
+ minY = maxY = inY = outY;
103
+ }
104
+ else {
105
+ minY = maxY = null;
106
+
107
+ if (yVal === null)
108
+ hasGap = true;
109
+ }
110
+
111
+ accX = x;
112
+ nextAccXVal = xForPixel(accX + dir);
113
+ }
114
+ }
115
+
116
+ if (minY != null && minY != maxY && drawnAtX != accX)
117
+ drawAcc(stroke, accX, pixelForY(minY), pixelForY(maxY), pixelForY(inY), pixelForY(outY));
118
+ }
119
+ else {
120
+ for (let i = dir == 1 ? idx0 : idx1; i >= idx0 && i <= idx1; i += dir) {
121
+ let yVal = dataY[i];
122
+
123
+ if (yVal === null)
124
+ hasGap = true;
125
+ else if (yVal != null)
126
+ lineTo(stroke, pixelForX(dataX[i]), pixelForY(yVal));
127
+ }
128
+ }
129
+
130
+ let [ bandFillDir, bandClipDir ] = bandFillClipDirs(u, seriesIdx);
131
+
132
+ if (series.fill != null || bandFillDir != 0) {
133
+ let fill = _paths.fill = new GPUPath(stroke);
134
+
135
+ let fillToVal = series.fillTo(u, seriesIdx, series.min, series.max, bandFillDir);
136
+ let fillToY = pixelForY(fillToVal);
137
+
138
+ let frX = pixelForX(dataX[idx0]);
139
+ let toX = pixelForX(dataX[idx1]);
140
+
141
+ if (dir == -1)
142
+ [toX, frX] = [frX, toX];
143
+
144
+ lineTo(fill, toX, fillToY);
145
+ lineTo(fill, frX, fillToY);
146
+ }
147
+
148
+ if (!series.spanGaps) { // skip in mode: 2?
149
+ // console.time('gaps');
150
+ let gaps = hasGap ? findGaps(dataX, dataY, idx0, idx1, dir, pixelForX, alignGaps) : [];
151
+
152
+ // console.timeEnd('gaps');
153
+
154
+ // console.log('gaps', JSON.stringify(gaps));
155
+
156
+ _paths.gaps = gaps = series.gaps(u, seriesIdx, idx0, idx1, gaps);
157
+
158
+ _paths.clip = clipGaps(gaps, scaleX.ori, xOff, yOff, xDim, yDim);
159
+ }
160
+
161
+ if (bandClipDir != 0) {
162
+ _paths.band = bandClipDir == 2 ? [
163
+ clipBandLine(u, seriesIdx, idx0, idx1, stroke, -1),
164
+ clipBandLine(u, seriesIdx, idx0, idx1, stroke, 1),
165
+ ] : clipBandLine(u, seriesIdx, idx0, idx1, stroke, bandClipDir);
166
+ }
167
+
168
+ return _paths;
169
+ });
170
+ };
171
+ }