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.
- package/index.js +0 -17
- package/index.ts +5 -0
- package/package.json +4 -69
- package/paths/ts/bars.ts +253 -0
- package/paths/ts/catmullRomCentrip.ts +127 -0
- package/paths/ts/index.ts +9 -0
- package/paths/ts/linear.ts +172 -0
- package/paths/ts/monotoneCubic.ts +70 -0
- package/paths/ts/points.ts +70 -0
- package/paths/ts/spline.ts +105 -0
- package/paths/ts/stepped.ts +126 -0
- package/paths/ts/types.ts +143 -0
- package/paths/ts/utils.ts +303 -0
- package/scripts/ts/uPlot.ts +3732 -0
- package/scripts/ts/utils/dom.ts +124 -0
- package/scripts/ts/utils/domClasses.ts +22 -0
- package/scripts/ts/utils/feats.ts +13 -0
- package/scripts/ts/utils/fmtDate.ts +398 -0
- package/scripts/ts/utils/opts.ts +844 -0
- package/scripts/ts/utils/strings.ts +22 -0
- package/scripts/ts/utils/sync.ts +27 -0
- package/scripts/ts/utils/utils.ts +692 -0
- package/scripts/{webgpu → ts/webgpu}/GPUPath.ts +92 -41
- package/scripts/{webgpu → ts/webgpu}/WebGPURenderer.ts +176 -84
- package/scripts/ts/webgpu/exporters.ts +221 -0
- package/scripts/ts/webgpu/index.ts +31 -0
- package/scripts/{webgpu → ts/webgpu}/shaders.ts +0 -1
- package/scripts/uPlot.js +0 -2
- package/scripts/webgpu/GPUPath.js +513 -606
- package/scripts/webgpu/WebGPURenderer.js +3484 -4018
- package/scripts/webgpu/exporters.js +191 -201
- package/scripts/webgpu/index.js +12 -0
- package/scripts/webgpu/shaders.js +6 -3
- package/tinybuild.config.js +6 -6
- package/tsconfig.json +64 -0
- package/scripts/uPlot.d.ts +0 -26
- package/scripts/webgpu/GPUPath.d.ts +0 -46
- package/scripts/webgpu/WebGPURenderer.d.ts +0 -176
- package/scripts/webgpu/exporters.d.ts +0 -8
- package/scripts/webgpu/shaders.d.ts +0 -2
- package/scripts/webgpu/smokeTest.d.ts +0 -2
- 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); };
|