uplot-webgpu 0.1.0 → 0.2.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 (42) hide show
  1. package/index.js +0 -17
  2. package/index.ts +5 -0
  3. package/package.json +4 -69
  4. package/paths/ts/bars.ts +253 -0
  5. package/paths/ts/catmullRomCentrip.ts +127 -0
  6. package/paths/ts/index.ts +9 -0
  7. package/paths/ts/linear.ts +172 -0
  8. package/paths/ts/monotoneCubic.ts +70 -0
  9. package/paths/ts/points.ts +70 -0
  10. package/paths/ts/spline.ts +105 -0
  11. package/paths/ts/stepped.ts +126 -0
  12. package/paths/ts/types.ts +143 -0
  13. package/paths/ts/utils.ts +303 -0
  14. package/scripts/ts/uPlot.ts +3732 -0
  15. package/scripts/ts/utils/dom.ts +124 -0
  16. package/scripts/ts/utils/domClasses.ts +22 -0
  17. package/scripts/ts/utils/feats.ts +13 -0
  18. package/scripts/ts/utils/fmtDate.ts +398 -0
  19. package/scripts/ts/utils/opts.ts +844 -0
  20. package/scripts/ts/utils/strings.ts +22 -0
  21. package/scripts/ts/utils/sync.ts +27 -0
  22. package/scripts/ts/utils/utils.ts +692 -0
  23. package/scripts/{webgpu → ts/webgpu}/GPUPath.ts +92 -41
  24. package/scripts/{webgpu → ts/webgpu}/WebGPURenderer.ts +176 -84
  25. package/scripts/ts/webgpu/exporters.ts +221 -0
  26. package/scripts/ts/webgpu/index.ts +31 -0
  27. package/scripts/{webgpu → ts/webgpu}/shaders.ts +0 -1
  28. package/scripts/uPlot.js +0 -2
  29. package/scripts/webgpu/GPUPath.js +513 -606
  30. package/scripts/webgpu/WebGPURenderer.js +3484 -4018
  31. package/scripts/webgpu/exporters.js +191 -201
  32. package/scripts/webgpu/index.js +12 -0
  33. package/scripts/webgpu/shaders.js +6 -3
  34. package/tinybuild.config.js +6 -6
  35. package/tsconfig.json +64 -0
  36. package/scripts/uPlot.d.ts +0 -26
  37. package/scripts/webgpu/GPUPath.d.ts +0 -46
  38. package/scripts/webgpu/WebGPURenderer.d.ts +0 -176
  39. package/scripts/webgpu/exporters.d.ts +0 -8
  40. package/scripts/webgpu/shaders.d.ts +0 -2
  41. package/scripts/webgpu/smokeTest.d.ts +0 -2
  42. package/scripts/webgpu/webgpu-ambient.d.ts +0 -41
@@ -0,0 +1,303 @@
1
+ import { GPUPath } from '../../scripts/ts/webgpu/GPUPath.js';
2
+ import { round, incrRound, retArg0, min, EMPTY_ARR, ifNull } from "../../scripts/ts/utils/utils.js";
3
+ import type { ArcFn, BandClipPath, BezierCurveToFn, GapList, LineToFn,
4
+ MoveToFn, OrientedPathCallback, PathData, PixelRound, RectFn, UPlotPathLike } from './types.js';
5
+
6
+ export const BAND_CLIP_FILL = 1 << 0;
7
+ export const BAND_CLIP_STROKE = 1 << 1;
8
+
9
+ export function orient<T>(u: UPlotPathLike, seriesIdx: number, cb: OrientedPathCallback<T>): T {
10
+ const mode = u.mode;
11
+ const series = u.series[seriesIdx];
12
+ const data = mode == 2 ? u._data[seriesIdx] : u._data;
13
+ const scales = u.scales;
14
+ const bbox = u.bbox;
15
+
16
+ let dx = data[0],
17
+ dy = mode == 2 ? data[1] : data[seriesIdx],
18
+ sx = mode == 2 ? scales[series.facets[0].scale] : scales[u.series[0].scale],
19
+ sy = mode == 2 ? scales[series.facets[1].scale] : scales[series.scale],
20
+ l = bbox.left,
21
+ t = bbox.top,
22
+ w = bbox.width,
23
+ h = bbox.height,
24
+ H = u.valToPosH,
25
+ V = u.valToPosV;
26
+
27
+ return (sx.ori == 0
28
+ ? cb(
29
+ series,
30
+ dx,
31
+ dy,
32
+ sx,
33
+ sy,
34
+ H,
35
+ V,
36
+ l,
37
+ t,
38
+ w,
39
+ h,
40
+ moveToH,
41
+ lineToH,
42
+ rectH,
43
+ arcH,
44
+ bezierCurveToH,
45
+ )
46
+ : cb(
47
+ series,
48
+ dx,
49
+ dy,
50
+ sx,
51
+ sy,
52
+ V,
53
+ H,
54
+ t,
55
+ l,
56
+ h,
57
+ w,
58
+ moveToV,
59
+ lineToV,
60
+ rectV,
61
+ arcV,
62
+ bezierCurveToV,
63
+ )
64
+ );
65
+ }
66
+
67
+ export function bandFillClipDirs(self: UPlotPathLike, seriesIdx: number): [fillDir: number, clipDir: number] {
68
+ let fillDir = 0;
69
+
70
+ // 2 bits, -1 | 1
71
+ let clipDirs = 0;
72
+
73
+ let bands = ifNull(self.bands, EMPTY_ARR);
74
+
75
+ for (let i = 0; i < bands.length; i++) {
76
+ let b = bands[i];
77
+
78
+ // is a "from" band edge
79
+ if (b.series[0] == seriesIdx)
80
+ fillDir = b.dir;
81
+ // is a "to" band edge
82
+ else if (b.series[1] == seriesIdx) {
83
+ if (b.dir == 1)
84
+ clipDirs |= 1;
85
+ else
86
+ clipDirs |= 2;
87
+ }
88
+ }
89
+
90
+ return [
91
+ fillDir,
92
+ (
93
+ clipDirs == 1 ? -1 : // neg only
94
+ clipDirs == 2 ? 1 : // pos only
95
+ clipDirs == 3 ? 2 : // both
96
+ 0 // neither
97
+ )
98
+ ];
99
+ }
100
+
101
+ export function seriesFillTo(self: UPlotPathLike, seriesIdx: number, dataMin: number, dataMax: number, bandFillDir: number): number {
102
+ let mode = self.mode;
103
+ let series = self.series[seriesIdx];
104
+ let scaleKey = mode == 2 ? series.facets[1].scale : series.scale;
105
+ let scale = self.scales[scaleKey];
106
+
107
+ return (
108
+ bandFillDir == -1 ? scale.min :
109
+ bandFillDir == 1 ? scale.max :
110
+ scale.distr == 3 ? (
111
+ scale.dir == 1 ? scale.min :
112
+ scale.max
113
+ ) : 0
114
+ );
115
+ }
116
+
117
+ // creates inverted band clip path (from stroke path -> yMax || yMin)
118
+ // clipDir is always inverse of fillDir
119
+ // default clip dir is upwards (1), since default band fill is downwards/fillBelowTo (-1) (highIdx -> lowIdx)
120
+ export function clipBandLine(self: UPlotPathLike, seriesIdx: number, idx0: number, idx1: number, strokePath: any, clipDir: number): BandClipPath {
121
+ return orient(self, seriesIdx, (series, dataX, dataY, scaleX, scaleY, valToPosX, valToPosY, xOff, yOff, xDim, yDim) => {
122
+ let pxRound = series.pxRound;
123
+
124
+ const dir = scaleX.dir * (scaleX.ori == 0 ? 1 : -1);
125
+ const lineTo = scaleX.ori == 0 ? lineToH : lineToV;
126
+
127
+ let frIdx, toIdx;
128
+
129
+ if (dir == 1) {
130
+ frIdx = idx0;
131
+ toIdx = idx1;
132
+ }
133
+ else {
134
+ frIdx = idx1;
135
+ toIdx = idx0;
136
+ }
137
+
138
+ // path start
139
+ let x0 = pxRound(valToPosX(dataX[frIdx], scaleX, xDim, xOff));
140
+ let y0 = pxRound(valToPosY(dataY[frIdx], scaleY, yDim, yOff));
141
+ // path end x
142
+ let x1 = pxRound(valToPosX(dataX[toIdx], scaleX, xDim, xOff));
143
+ // upper or lower y limit
144
+ let yLimit = pxRound(valToPosY(clipDir == 1 ? scaleY.max : scaleY.min, scaleY, yDim, yOff));
145
+
146
+ let clip = new GPUPath(strokePath);
147
+
148
+ lineTo(clip, x1, yLimit);
149
+ lineTo(clip, x0, yLimit);
150
+ lineTo(clip, x0, y0);
151
+
152
+ return clip;
153
+ });
154
+ }
155
+
156
+ export function clipGaps(gaps: GapList, ori: number, plotLft: number, plotTop: number, plotWid: number, plotHgt: number): import('../../scripts/ts/webgpu/GPUPath.js').GPUPath | null {
157
+ let clip = null;
158
+
159
+ // create clip path (invert gaps and non-gaps)
160
+ if (gaps.length > 0) {
161
+ clip = new GPUPath();
162
+
163
+ const rect = ori == 0 ? rectH : rectV;
164
+
165
+ let prevGapEnd = plotLft;
166
+
167
+ for (let i = 0; i < gaps.length; i++) {
168
+ let g = gaps[i];
169
+
170
+ if (g[1] > g[0]) {
171
+ let w = g[0] - prevGapEnd;
172
+
173
+ w > 0 && rect(clip, prevGapEnd, plotTop, w, plotTop + plotHgt);
174
+
175
+ prevGapEnd = g[1];
176
+ }
177
+ }
178
+
179
+ let w = plotLft + plotWid - prevGapEnd;
180
+
181
+ // hack to ensure we expand the clip enough to avoid cutting off strokes at edges
182
+ let maxStrokeWidth = 10;
183
+
184
+ w > 0 && rect(clip, prevGapEnd, plotTop - maxStrokeWidth / 2, w, plotTop + plotHgt + maxStrokeWidth);
185
+ }
186
+
187
+ return clip;
188
+ }
189
+
190
+ export function addGap(gaps: GapList, fromX: number, toX: number): void {
191
+ let prevGap = gaps[gaps.length - 1];
192
+
193
+ if (prevGap && prevGap[0] == fromX) // TODO: gaps must be encoded at stroke widths?
194
+ prevGap[1] = toX;
195
+ else
196
+ gaps.push([fromX, toX]);
197
+ }
198
+
199
+ export function findGaps(xs: ArrayLike<number>, ys: PathData, idx0: number, idx1: number, dir: number, pixelForX: (value: number) => number, align: number): GapList {
200
+ let gaps = [];
201
+ let len = xs.length;
202
+
203
+ for (let i = dir == 1 ? idx0 : idx1; i >= idx0 && i <= idx1; i += dir) {
204
+ let yVal = ys[i];
205
+
206
+ if (yVal === null) {
207
+ let fr = i, to = i;
208
+
209
+ if (dir == 1) {
210
+ while (++i <= idx1 && ys[i] === null)
211
+ to = i;
212
+ }
213
+ else {
214
+ while (--i >= idx0 && ys[i] === null)
215
+ to = i;
216
+ }
217
+
218
+ let frPx = pixelForX(xs[fr]);
219
+ let toPx = to == fr ? frPx : pixelForX(xs[to]);
220
+
221
+ // if value adjacent to edge null is same pixel, then it's partially
222
+ // filled and gap should start at next pixel
223
+ let fri2 = fr - dir;
224
+ let frPx2 = align <= 0 && fri2 >= 0 && fri2 < len ? pixelForX(xs[fri2]) : frPx;
225
+ // if (frPx2 == frPx)
226
+ // frPx++;
227
+ // else
228
+ frPx = frPx2;
229
+
230
+ let toi2 = to + dir;
231
+ let toPx2 = align >= 0 && toi2 >= 0 && toi2 < len ? pixelForX(xs[toi2]) : toPx;
232
+ // if (toPx2 == toPx)
233
+ // toPx--;
234
+ // else
235
+ toPx = toPx2;
236
+
237
+ if (toPx >= frPx)
238
+ gaps.push([frPx, toPx]); // addGap
239
+ }
240
+ }
241
+
242
+ return gaps;
243
+ }
244
+
245
+ export function pxRoundGen(pxAlign: number): PixelRound {
246
+ return pxAlign == 0 ? retArg0 : pxAlign == 1 ? round : v => incrRound(v, pxAlign);
247
+ }
248
+
249
+ /*
250
+ // inefficient linear interpolation that does bi-directinal scans on each call
251
+ export function costlyLerp(i: number, idx0: number, idx1: number, _dirX: number, dataY: PathData): number {
252
+ let prevNonNull = nonNullIdx(dataY, _dirX == 1 ? idx0 : idx1, i, -_dirX);
253
+ let nextNonNull = nonNullIdx(dataY, i, _dirX == 1 ? idx1 : idx0, _dirX);
254
+
255
+ let prevVal = dataY[prevNonNull];
256
+ let nextVal = dataY[nextNonNull];
257
+
258
+ return prevVal + (i - prevNonNull) / (nextNonNull - prevNonNull) * (nextVal - prevVal);
259
+ }
260
+ */
261
+
262
+ function rect(ori: 0 | 1): RectFn {
263
+ let moveTo = ori == 0 ?
264
+ moveToH :
265
+ moveToV;
266
+
267
+ let arcTo: (p: any, x1: number, y1: number, x2: number, y2: number, r: number) => void = ori == 0 ?
268
+ (p, x1, y1, x2, y2, r) => { p.arcTo(x1, y1, x2, y2, r); } :
269
+ (p, y1, x1, y2, x2, r) => { p.arcTo(x1, y1, x2, y2, r); };
270
+
271
+ let rectRaw: (p: any, x: number, y: number, w: number, h: number) => void = ori == 0 ?
272
+ (p, x, y, w, h) => { p.rect(x, y, w, h); } :
273
+ (p, y, x, h, w) => { p.rect(x, y, w, h); };
274
+
275
+ return (p, x, y, w, h, endRad = 0, baseRad = 0) => {
276
+ if (endRad == 0 && baseRad == 0)
277
+ rectRaw(p, x, y, w, h);
278
+ else {
279
+ endRad = min(endRad, w / 2, h / 2);
280
+ baseRad = min(baseRad, w / 2, h / 2);
281
+
282
+ // adapted from https://stackoverflow.com/questions/1255512/how-to-draw-a-rounded-rectangle-using-html-canvas/7838871#7838871
283
+ moveTo(p, x + endRad, y);
284
+ arcTo(p, x + w, y, x + w, y + h, endRad);
285
+ arcTo(p, x + w, y + h, x, y + h, baseRad);
286
+ arcTo(p, x, y + h, x, y, baseRad);
287
+ arcTo(p, x, y, x + w, y, endRad);
288
+ p.closePath();
289
+ }
290
+ };
291
+ }
292
+
293
+ // orientation-inverting canvas functions
294
+ export const moveToH: MoveToFn = (p, x, y) => { p.moveTo(x, y); };
295
+ export const moveToV: MoveToFn = (p, y, x) => { p.moveTo(x, y); };
296
+ export const lineToH: LineToFn = (p, x, y) => { p.lineTo(x, y); };
297
+ export const lineToV: LineToFn = (p, y, x) => { p.lineTo(x, y); };
298
+ export const rectH: RectFn = rect(0);
299
+ export const rectV: RectFn = rect(1);
300
+ export const arcH: ArcFn = (p, x, y, r, startAngle, endAngle) => { p.arc(x, y, r, startAngle, endAngle); };
301
+ export const arcV: ArcFn = (p, y, x, r, startAngle, endAngle) => { p.arc(x, y, r, startAngle, endAngle); };
302
+ export const bezierCurveToH: BezierCurveToFn = (p, bp1x, bp1y, bp2x, bp2y, p2x, p2y) => { p.bezierCurveTo(bp1x, bp1y, bp2x, bp2y, p2x, p2y); };
303
+ export const bezierCurveToV: BezierCurveToFn = (p, bp1y, bp1x, bp2y, bp2x, p2y, p2x) => { p.bezierCurveTo(bp1x, bp1y, bp2x, bp2y, p2x, p2y); };