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,692 @@
1
+ // binary search for index of closest value
2
+ export function closestIdx(num?: any, arr?: any, lo?: any, hi?: any, ..._extra: any[]) {
3
+ let mid;
4
+ lo = lo || 0;
5
+ hi = hi || arr.length - 1;
6
+ let bitwise = hi <= 2147483647;
7
+
8
+ while (hi - lo > 1) {
9
+ mid = bitwise ? (lo + hi) >> 1 : floor((lo + hi) / 2);
10
+
11
+ if (arr[mid] < num)
12
+ lo = mid;
13
+ else
14
+ hi = mid;
15
+ }
16
+
17
+ if (num - arr[lo] <= arr[hi] - num)
18
+ return lo;
19
+
20
+ return hi;
21
+ }
22
+
23
+ function makeIndexOfs(predicate?: any, ..._extra: any[]) {
24
+ let indexOfs = (data?: any, _i0?: any, _i1?: any, ..._extra: any[]) => {
25
+ let i0 = -1;
26
+ let i1 = -1;
27
+
28
+ for (let i = _i0; i <= _i1; i++) {
29
+ if (predicate(data[i])) {
30
+ i0 = i;
31
+ break;
32
+ }
33
+ }
34
+
35
+ for (let i = _i1; i >= _i0; i--) {
36
+ if (predicate(data[i])) {
37
+ i1 = i;
38
+ break;
39
+ }
40
+ }
41
+
42
+ return [i0, i1];
43
+ };
44
+
45
+ return indexOfs;
46
+ }
47
+
48
+ const notNullish = (v?: any, ..._extra: any[]) => v != null;
49
+ const isPositive = (v?: any, ..._extra: any[]) => v != null && v > 0;
50
+
51
+ export const nonNullIdxs = makeIndexOfs(notNullish);
52
+ export const positiveIdxs = makeIndexOfs(isPositive);
53
+
54
+ export function getMinMax(data?: any, _i0?: any, _i1?: any, sorted: any = 0, log: any = false, ..._extra: any[]) {
55
+ // console.log("getMinMax()");
56
+
57
+ let getEdgeIdxs = log ? positiveIdxs : nonNullIdxs;
58
+ let predicate = log ? isPositive : notNullish;
59
+
60
+ [_i0, _i1] = getEdgeIdxs(data, _i0, _i1);
61
+
62
+ let _min = data[_i0];
63
+ let _max = data[_i0];
64
+
65
+ if (_i0 > -1) {
66
+ if (sorted == 1) {
67
+ _min = data[_i0];
68
+ _max = data[_i1];
69
+ }
70
+ else if (sorted == -1) {
71
+ _min = data[_i1];
72
+ _max = data[_i0];
73
+ }
74
+ else {
75
+ for (let i = _i0; i <= _i1; i++) {
76
+ let v = data[i];
77
+
78
+ if (predicate(v)) {
79
+ if (v < _min)
80
+ _min = v;
81
+ else if (v > _max)
82
+ _max = v;
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ return [_min ?? inf, _max ?? -inf]; // todo: fix to return nulls
89
+ }
90
+
91
+ export function rangeLog(min?: any, max?: any, base?: any, fullMags?: any, ..._extra: any[]) {
92
+ if (base == 2)
93
+ fullMags = true;
94
+
95
+ let minSign = sign(min);
96
+ let maxSign = sign(max);
97
+
98
+ if (min == max) {
99
+ if (minSign == -1) {
100
+ min *= base;
101
+ max /= base;
102
+ }
103
+ else {
104
+ min /= base;
105
+ max *= base;
106
+ }
107
+ }
108
+
109
+ let logFn = base == 10 ? log10 : log2;
110
+
111
+ let growMinAbs = minSign == 1 ? floor : ceil;
112
+ let growMaxAbs = maxSign == 1 ? ceil : floor;
113
+
114
+ let minLogAbs = logFn(abs(min))
115
+ let maxLogAbs = logFn(abs(max));
116
+
117
+ let minExp = growMinAbs(minLogAbs);
118
+ let maxExp = growMaxAbs(maxLogAbs);
119
+
120
+ let minIncr = pow(base, minExp);
121
+ let maxIncr = pow(base, maxExp);
122
+
123
+ // fix values like Math.pow(10, -5) === 0.000009999999999999999
124
+ if (base == 10) {
125
+ if (minExp < 0)
126
+ minIncr = roundDec(minIncr, -minExp);
127
+ if (maxExp < 0)
128
+ maxIncr = roundDec(maxIncr, -maxExp);
129
+ }
130
+
131
+ if (fullMags) {
132
+ min = minIncr * minSign;
133
+ max = maxIncr * maxSign;
134
+ }
135
+ else {
136
+ min = incrRoundDn(min, pow(base, floor(minLogAbs)), false);
137
+ max = incrRoundUp(max, pow(base, floor(maxLogAbs)), false);
138
+ }
139
+
140
+ return [min, max];
141
+ }
142
+
143
+ export function rangeAsinh(min?: any, max?: any, base?: any, fullMags?: any, ..._extra: any[]) {
144
+ let minMax = rangeLog(min, max, base, fullMags);
145
+
146
+ if (min == 0)
147
+ minMax[0] = 0;
148
+
149
+ if (max == 0)
150
+ minMax[1] = 0;
151
+
152
+ return minMax;
153
+ }
154
+
155
+ export const rangePad = 0.1;
156
+
157
+ export const autoRangePart = {
158
+ mode: 3,
159
+ pad: rangePad,
160
+ };
161
+
162
+ const _eqRangePart = {
163
+ pad: 0,
164
+ soft: null,
165
+ mode: 0,
166
+ };
167
+
168
+ const _eqRange = {
169
+ min: _eqRangePart,
170
+ max: _eqRangePart,
171
+ };
172
+
173
+ // this ensures that non-temporal/numeric y-axes get multiple-snapped padding added above/below
174
+ // TODO: also account for incrs when snapping to ensure top of axis gets a tick & value
175
+ export function rangeNum(_min?: any, _max?: any, mult?: any, extra?: any, ..._extra: any[]) {
176
+ if (isObj(mult))
177
+ return _rangeNum(_min, _max, mult);
178
+
179
+ _eqRangePart.pad = mult;
180
+ _eqRangePart.soft = extra ? 0 : null;
181
+ _eqRangePart.mode = extra ? 3 : 0;
182
+
183
+ return _rangeNum(_min, _max, _eqRange);
184
+ }
185
+
186
+ // nullish coalesce
187
+ export function ifNull(lh?: any, rh?: any, ..._extra: any[]) {
188
+ return lh == null ? rh : lh;
189
+ }
190
+
191
+ // checks if given index range in an array contains a non-null value
192
+ // aka a range-bounded Array.some()
193
+ export function hasData(data?: any, idx0?: any, idx1?: any, ..._extra: any[]) {
194
+ idx0 = ifNull(idx0, 0);
195
+ idx1 = ifNull(idx1, data.length - 1);
196
+
197
+ while (idx0 <= idx1) {
198
+ if (data[idx0] != null)
199
+ return true;
200
+ idx0++;
201
+ }
202
+
203
+ return false;
204
+ }
205
+
206
+ function _rangeNum(_min?: any, _max?: any, cfg?: any, ..._extra: any[]) {
207
+ let cmin = cfg.min;
208
+ let cmax = cfg.max;
209
+
210
+ let padMin = ifNull(cmin.pad, 0);
211
+ let padMax = ifNull(cmax.pad, 0);
212
+
213
+ let hardMin = ifNull(cmin.hard, -inf);
214
+ let hardMax = ifNull(cmax.hard, inf);
215
+
216
+ let softMin = ifNull(cmin.soft, inf);
217
+ let softMax = ifNull(cmax.soft, -inf);
218
+
219
+ let softMinMode = ifNull(cmin.mode, 0);
220
+ let softMaxMode = ifNull(cmax.mode, 0);
221
+
222
+ let delta = _max - _min;
223
+ let deltaMag = log10(delta);
224
+
225
+ let scalarMax = max(abs(_min), abs(_max));
226
+ let scalarMag = log10(scalarMax);
227
+
228
+ let scalarMagDelta = abs(scalarMag - deltaMag);
229
+
230
+ // this handles situations like 89.7, 89.69999999999999
231
+ // by assuming 0.001x deltas are precision errors
232
+ // if (delta > 0 && delta < abs(_max) / 1e3)
233
+ // delta = 0;
234
+
235
+ // treat data as flat if delta is less than 1e-24
236
+ // or range is 11+ orders of magnitude below raw values, e.g. 99999999.99999996 - 100000000.00000004
237
+ if (delta < 1e-24 || scalarMagDelta > 10) {
238
+ delta = 0;
239
+
240
+ // if soft mode is 2 and all vals are flat at 0, avoid the 0.1 * 1e3 fallback
241
+ // this prevents 0,0,0 from ranging to -100,100 when softMin/softMax are -1,1
242
+ if (_min == 0 || _max == 0) {
243
+ delta = 1e-24;
244
+
245
+ if (softMinMode == 2 && softMin != inf)
246
+ padMin = 0;
247
+
248
+ if (softMaxMode == 2 && softMax != -inf)
249
+ padMax = 0;
250
+ }
251
+ }
252
+
253
+ let nonZeroDelta = delta || scalarMax || 1e3;
254
+ let mag = log10(nonZeroDelta);
255
+ let base = pow(10, floor(mag));
256
+
257
+ let _padMin = nonZeroDelta * (delta == 0 ? (_min == 0 ? .1 : 1) : padMin);
258
+ let _newMin = roundDec(incrRoundDn(_min - _padMin, base/10), 24);
259
+ let _softMin = _min >= softMin && (softMinMode == 1 || softMinMode == 3 && _newMin <= softMin || softMinMode == 2 && _newMin >= softMin) ? softMin : inf;
260
+ let minLim = max(hardMin, _newMin < _softMin && _min >= _softMin ? _softMin : min(_softMin, _newMin));
261
+
262
+ let _padMax = nonZeroDelta * (delta == 0 ? (_max == 0 ? .1 : 1) : padMax);
263
+ let _newMax = roundDec(incrRoundUp(_max + _padMax, base/10), 24);
264
+ let _softMax = _max <= softMax && (softMaxMode == 1 || softMaxMode == 3 && _newMax >= softMax || softMaxMode == 2 && _newMax <= softMax) ? softMax : -inf;
265
+ let maxLim = min(hardMax, _newMax > _softMax && _max <= _softMax ? _softMax : max(_softMax, _newMax));
266
+
267
+ // handle case when delta was small enough to cause fixFloat to have rounded off 6 decimals and result in min === max
268
+ if (minLim == maxLim) {
269
+ if (minLim == 0)
270
+ maxLim = 100;
271
+ else if (minLim < 0) {
272
+ minLim *= 2;
273
+ maxLim = 0;
274
+ }
275
+ else {
276
+ minLim = 0;
277
+ maxLim *= 2;
278
+ }
279
+ }
280
+
281
+ return [minLim, maxLim];
282
+ }
283
+
284
+ // alternative: https://stackoverflow.com/a/2254896
285
+ const numFormatter = new Intl.NumberFormat();
286
+ export const fmtNum = (val?: any, ..._extra: any[]) => numFormatter.format(val);
287
+
288
+ const M = Math;
289
+
290
+ export const PI = M.PI;
291
+ export const abs = M.abs;
292
+ export const floor = M.floor;
293
+ export const round = M.round;
294
+ export const ceil = M.ceil;
295
+ export const min = M.min;
296
+ export const max = M.max;
297
+ export const pow = M.pow;
298
+ export const sqrt = M.sqrt;
299
+ export const sign = M.sign;
300
+ export const log10 = M.log10;
301
+ export const log2 = M.log2;
302
+ // TODO: seems like this needs to match asinh impl if the passed v is tweaked?
303
+ export const sinh = (v?: any, linthresh: any = 1, ..._extra: any[]) => M.sinh(v) * linthresh;
304
+ export const asinh = (v?: any, linthresh: any = 1, ..._extra: any[]) => M.asinh(v / linthresh);
305
+
306
+ export const inf = Infinity;
307
+
308
+ export function numIntDigits(x?: any, ..._extra: any[]) {
309
+ return (log10((x ^ (x >> 31)) - (x >> 31)) | 0) + 1;
310
+ }
311
+
312
+ export function clamp(num?: any, _min?: any, _max?: any, ..._extra: any[]) {
313
+ return min(max(num, _min), _max);
314
+ }
315
+
316
+ export function isFn(v?: any, ..._extra: any[]) {
317
+ return typeof v == "function";
318
+ }
319
+
320
+ export function fnOrSelf(v?: any, ..._extra: any[]) {
321
+ return isFn(v) ? v : () => v;
322
+ }
323
+
324
+ export const noop = () => {};
325
+
326
+ // note: these identity fns may get deoptimized if reused for different arg types
327
+ // a TS version would enforce they stay monotyped and require making variants
328
+ export const retArg0 = (_0?: any, ..._extra: any[]) => _0;
329
+
330
+ export const retArg1 = (_0?: any, _1?: any, ..._extra: any[]) => _1;
331
+
332
+ export const retNull = (_?: any, ..._extra: any[]) => null;
333
+
334
+ export const retTrue = (_?: any, ..._extra: any[]) => true;
335
+
336
+ export const retEq = (a?: any, b?: any, ..._extra: any[]) => a == b;
337
+
338
+ const regex6 = /\.\d*?(?=9{6,}|0{6,})/gm;
339
+
340
+ // e.g. 17999.204999999998 -> 17999.205
341
+ const fixFloat = (val?: any, ..._extra: any[]) => {
342
+ if (isInt(val) || fixedDec.has(val))
343
+ return val;
344
+
345
+ const str = `${val}`;
346
+
347
+ const match = str.match(regex6);
348
+
349
+ if (match == null)
350
+ return val;
351
+
352
+ let len = match[0].length - 1;
353
+
354
+ // e.g. 1.0000000000000001e-24
355
+ if (str.indexOf('e-') != -1) {
356
+ let [num, exp] = str.split('e');
357
+ return +`${fixFloat(num)}e${exp}`;
358
+ }
359
+
360
+ return roundDec(val, len);
361
+ }
362
+
363
+ export function incrRound(num?: any, incr?: any, _fixFloat: any = true, ..._extra: any[]) {
364
+ return _fixFloat ? fixFloat(roundDec(fixFloat(num/incr))*incr) : roundDec(num/incr)*incr;
365
+ }
366
+
367
+ export function incrRoundUp(num?: any, incr?: any, _fixFloat: any = true, ..._extra: any[]) {
368
+ return _fixFloat ? fixFloat(ceil(fixFloat(num/incr))*incr) : ceil(num/incr)*incr;
369
+ }
370
+
371
+ export function incrRoundDn(num?: any, incr?: any, _fixFloat: any = true, ..._extra: any[]) {
372
+ return _fixFloat ? fixFloat(floor(fixFloat(num/incr))*incr) : floor(num/incr)*incr;
373
+ }
374
+
375
+ // https://stackoverflow.com/a/48764436
376
+ // rounds half away from zero
377
+ export function roundDec(val?: any, dec: any = 0, ..._extra: any[]) {
378
+ if (isInt(val))
379
+ return val;
380
+ // else if (dec == 0)
381
+ // return round(val);
382
+
383
+ let p = 10 ** dec;
384
+ let n = (val * p) * (1 + Number.EPSILON);
385
+ return round(n) / p;
386
+ }
387
+
388
+ // https://stackoverflow.com/questions/14879691/get-number-of-digits-with-javascript/28203456#28203456
389
+ export function numDigits(x?: any, ..._extra: any[]) {
390
+ return (log10((x ^ (x >> 31)) - (x >> 31)) | 0) + 1;
391
+ }
392
+
393
+ export const fixedDec = new Map();
394
+
395
+ export function guessDec(num?: any, ..._extra: any[]) {
396
+ return ((""+num).split(".")[1] || "").length;
397
+ }
398
+
399
+ export function genIncrs(base?: any, minExp?: any, maxExp?: any, mults?: any, ..._extra: any[]) {
400
+ let incrs = [];
401
+
402
+ let multDec = mults.map(guessDec);
403
+
404
+ for (let exp = minExp; exp < maxExp; exp++) {
405
+ let expa = abs(exp);
406
+ let mag = roundDec(pow(base, exp), expa);
407
+
408
+ for (let i = 0; i < mults.length; i++) {
409
+ let _incr = base == 10 ? +`${mults[i]}e${exp}` : mults[i] * mag;
410
+ let dec = (exp >= 0 ? 0 : expa) + (exp >= multDec[i] ? 0 : multDec[i]);
411
+ let incr = base == 10 ? _incr : roundDec(_incr, dec);
412
+ incrs.push(incr);
413
+ fixedDec.set(incr, dec);
414
+ }
415
+ }
416
+
417
+ return incrs;
418
+ }
419
+
420
+ //export const assign = Object.assign;
421
+
422
+ export const EMPTY_OBJ = {};
423
+ export const EMPTY_ARR = [];
424
+
425
+ export const nullNullTuple = [null, null];
426
+
427
+ export const isArr = Array.isArray;
428
+ export const isInt = Number.isInteger;
429
+ export const isUndef = (v?: any, ..._extra: any[]) => v === void 0;
430
+
431
+ export function isStr(v?: any, ..._extra: any[]) {
432
+ return typeof v == 'string';
433
+ }
434
+
435
+ export function cmpObj(a?: any, b?: any, ..._extra: any[]) {
436
+ for (let k in a) {
437
+ if (b[k] != a[k])
438
+ return false;
439
+ }
440
+
441
+ return true;
442
+ }
443
+
444
+ export function isObj(v?: any, ..._extra: any[]) {
445
+ let is = false;
446
+
447
+ if (v != null) {
448
+ let c = v.constructor;
449
+ is = c == null || c == Object;
450
+ }
451
+
452
+ return is;
453
+ }
454
+
455
+ export function fastIsObj(v?: any, ..._extra: any[]) {
456
+ return v != null && typeof v == 'object';
457
+ }
458
+
459
+ const TypedArray = Object.getPrototypeOf(Uint8Array);
460
+
461
+ const __proto__ = "__proto__";
462
+
463
+ export function copy(o?: any, _isObj: any = isObj, ..._extra: any[]) {
464
+ let out;
465
+
466
+ if (isArr(o)) {
467
+ let val = o.find(v => v != null);
468
+
469
+ if (isArr(val) || _isObj(val)) {
470
+ out = Array(o.length);
471
+ for (let i = 0; i < o.length; i++)
472
+ out[i] = copy(o[i], _isObj);
473
+ }
474
+ else
475
+ out = o.slice();
476
+ }
477
+ else if (o instanceof TypedArray) // also (ArrayBuffer.isView(o) && !(o instanceof DataView))
478
+ out = o.slice();
479
+ else if (_isObj(o)) {
480
+ out = {};
481
+ for (let k in o) {
482
+ if (k != __proto__)
483
+ out[k] = copy(o[k], _isObj);
484
+ }
485
+ }
486
+ else
487
+ out = o;
488
+
489
+ return out;
490
+ }
491
+
492
+ export function assign(targ?: any, ..._extra: any[]) {
493
+ let args = arguments;
494
+
495
+ for (let i = 1; i < args.length; i++) {
496
+ let src = args[i];
497
+
498
+ for (let key in src) {
499
+ if (key != __proto__) {
500
+ if (isObj(targ[key]))
501
+ assign(targ[key], copy(src[key]));
502
+ else
503
+ targ[key] = copy(src[key]);
504
+ }
505
+ }
506
+ }
507
+
508
+ return targ;
509
+ }
510
+
511
+ // nullModes
512
+ const NULL_REMOVE = 0; // nulls are converted to undefined (e.g. for spanGaps: true)
513
+ const NULL_RETAIN = 1; // nulls are retained, with alignment artifacts set to undefined (default)
514
+ const NULL_EXPAND = 2; // nulls are expanded to include any adjacent alignment artifacts
515
+
516
+ // sets undefined values to nulls when adjacent to existing nulls (minesweeper)
517
+ function nullExpand(yVals?: any, nullIdxs?: any, alignedLen?: any, ..._extra: any[]) {
518
+ for (let i = 0, xi, lastNullIdx = -1; i < nullIdxs.length; i++) {
519
+ let nullIdx = nullIdxs[i];
520
+
521
+ if (nullIdx > lastNullIdx) {
522
+ xi = nullIdx - 1;
523
+ while (xi >= 0 && yVals[xi] == null)
524
+ yVals[xi--] = null;
525
+
526
+ xi = nullIdx + 1;
527
+ while (xi < alignedLen && yVals[xi] == null)
528
+ yVals[lastNullIdx = xi++] = null;
529
+ }
530
+ }
531
+ }
532
+
533
+ // nullModes is a tables-matched array indicating how to treat nulls in each series
534
+ // output is sorted ASC on the joined field (table[0]) and duplicate join values are collapsed
535
+ export function join(tables?: any, nullModes?: any, ..._extra: any[]) {
536
+ if (allHeadersSame(tables)) {
537
+ // console.log('cheap join!');
538
+
539
+ let table = tables[0].slice();
540
+
541
+ for (let i = 1; i < tables.length; i++)
542
+ table.push(...tables[i].slice(1));
543
+
544
+ if (!isAsc(table[0]))
545
+ table = sortCols(table);
546
+
547
+ return table;
548
+ }
549
+
550
+ let xVals = new Set<any>();
551
+
552
+ for (let ti = 0; ti < tables.length; ti++) {
553
+ let t = tables[ti];
554
+ let xs = t[0];
555
+ let len = xs.length;
556
+
557
+ for (let i = 0; i < len; i++)
558
+ xVals.add(xs[i]);
559
+ }
560
+
561
+ let data = [Array.from(xVals).sort((a, b) => a - b)];
562
+
563
+ let alignedLen = data[0].length;
564
+
565
+ let xIdxs = new Map();
566
+
567
+ for (let i = 0; i < alignedLen; i++)
568
+ xIdxs.set(data[0][i], i);
569
+
570
+ for (let ti = 0; ti < tables.length; ti++) {
571
+ let t = tables[ti];
572
+ let xs = t[0];
573
+
574
+ for (let si = 1; si < t.length; si++) {
575
+ let ys = t[si];
576
+
577
+ let yVals = Array(alignedLen).fill(undefined);
578
+
579
+ let nullMode = nullModes ? nullModes[ti][si] : NULL_RETAIN;
580
+
581
+ let nullIdxs = [];
582
+
583
+ for (let i = 0; i < ys.length; i++) {
584
+ let yVal = ys[i];
585
+ let alignedIdx = xIdxs.get(xs[i]);
586
+
587
+ if (yVal === null) {
588
+ if (nullMode != NULL_REMOVE) {
589
+ yVals[alignedIdx] = yVal;
590
+
591
+ if (nullMode == NULL_EXPAND)
592
+ nullIdxs.push(alignedIdx);
593
+ }
594
+ }
595
+ else
596
+ yVals[alignedIdx] = yVal;
597
+ }
598
+
599
+ nullExpand(yVals, nullIdxs, alignedLen);
600
+
601
+ data.push(yVals);
602
+ }
603
+ }
604
+
605
+ return data;
606
+ }
607
+
608
+ export const microTask = typeof queueMicrotask == "undefined" ? fn => Promise.resolve().then(fn) : queueMicrotask;
609
+
610
+ // TODO: https://github.com/dy/sort-ids (~2x faster for 1e5+ arrays)
611
+ function sortCols(table?: any, ..._extra: any[]) {
612
+ let head = table[0];
613
+ let rlen = head.length;
614
+
615
+ let idxs = Array(rlen);
616
+ for (let i = 0; i < idxs.length; i++)
617
+ idxs[i] = i;
618
+
619
+ idxs.sort((i0, i1) => head[i0] - head[i1]);
620
+
621
+ let table2 = [];
622
+ for (let i = 0; i < table.length; i++) {
623
+ let row = table[i];
624
+ let row2 = Array(rlen);
625
+
626
+ for (let j = 0; j < rlen; j++)
627
+ row2[j] = row[idxs[j]];
628
+
629
+ table2.push(row2);
630
+ }
631
+
632
+ return table2;
633
+ }
634
+
635
+ // test if we can do cheap join (all join fields same)
636
+ function allHeadersSame(tables?: any, ..._extra: any[]) {
637
+ let vals0 = tables[0][0];
638
+ let len0 = vals0.length;
639
+
640
+ for (let i = 1; i < tables.length; i++) {
641
+ let vals1 = tables[i][0];
642
+
643
+ if (vals1.length != len0)
644
+ return false;
645
+
646
+ if (vals1 != vals0) {
647
+ for (let j = 0; j < len0; j++) {
648
+ if (vals1[j] != vals0[j])
649
+ return false;
650
+ }
651
+ }
652
+ }
653
+
654
+ return true;
655
+ }
656
+
657
+ function isAsc(vals?: any, samples: any = 100, ..._extra: any[]) {
658
+ const len = vals.length;
659
+
660
+ // empty or single value
661
+ if (len <= 1)
662
+ return true;
663
+
664
+ // skip leading & trailing nullish
665
+ let firstIdx = 0;
666
+ let lastIdx = len - 1;
667
+
668
+ while (firstIdx <= lastIdx && vals[firstIdx] == null)
669
+ firstIdx++;
670
+
671
+ while (lastIdx >= firstIdx && vals[lastIdx] == null)
672
+ lastIdx--;
673
+
674
+ // all nullish or one value surrounded by nullish
675
+ if (lastIdx <= firstIdx)
676
+ return true;
677
+
678
+ const stride = max(1, floor((lastIdx - firstIdx + 1) / samples));
679
+
680
+ for (let prevVal = vals[firstIdx], i = firstIdx + stride; i <= lastIdx; i += stride) {
681
+ const v = vals[i];
682
+
683
+ if (v != null) {
684
+ if (v <= prevVal)
685
+ return false;
686
+
687
+ prevVal = v;
688
+ }
689
+ }
690
+
691
+ return true;
692
+ }