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.
- package/CANVAS_PROXY.md +602 -0
- package/README.md +854 -0
- package/favicon.ico +0 -0
- package/index.html +14 -0
- package/index.js +21 -0
- package/original/paths.canvas2d/bars.js +252 -0
- package/original/paths.canvas2d/catmullRomCentrip.js +125 -0
- package/original/paths.canvas2d/linear.js +170 -0
- package/original/paths.canvas2d/monotoneCubic.js +68 -0
- package/original/paths.canvas2d/points.js +66 -0
- package/original/paths.canvas2d/spline.js +103 -0
- package/original/paths.canvas2d/stepped.js +124 -0
- package/original/paths.canvas2d/utils.js +301 -0
- package/original/uPlot.canvas2d.js +3548 -0
- package/package.json +110 -0
- package/paths/bars.js +253 -0
- package/paths/catmullRomCentrip.js +126 -0
- package/paths/linear.js +171 -0
- package/paths/monotoneCubic.js +69 -0
- package/paths/points.js +67 -0
- package/paths/spline.js +104 -0
- package/paths/stepped.js +125 -0
- package/paths/utils.js +301 -0
- package/scripts/uPlot.css +168 -0
- package/scripts/uPlot.d.ts +26 -0
- package/scripts/uPlot.js +3687 -0
- package/scripts/utils/dom.js +124 -0
- package/scripts/utils/domClasses.js +22 -0
- package/scripts/utils/feats.js +13 -0
- package/scripts/utils/fmtDate.js +398 -0
- package/scripts/utils/opts.js +844 -0
- package/scripts/utils/strings.js +22 -0
- package/scripts/utils/sync.js +27 -0
- package/scripts/utils/utils.js +692 -0
- package/scripts/webgpu/GPUPath.d.ts +46 -0
- package/scripts/webgpu/GPUPath.js +633 -0
- package/scripts/webgpu/GPUPath.ts +634 -0
- package/scripts/webgpu/WebGPURenderer.d.ts +176 -0
- package/scripts/webgpu/WebGPURenderer.js +4256 -0
- package/scripts/webgpu/WebGPURenderer.ts +4257 -0
- package/scripts/webgpu/browserSmokeHarness.js +105 -0
- package/scripts/webgpu/exporters.d.ts +8 -0
- package/scripts/webgpu/exporters.js +212 -0
- package/scripts/webgpu/shaders.d.ts +2 -0
- package/scripts/webgpu/shaders.js +76 -0
- package/scripts/webgpu/shaders.ts +77 -0
- package/scripts/webgpu/smokeTest.d.ts +2 -0
- package/scripts/webgpu/smokeTest.js +144 -0
- package/scripts/webgpu/webgpu-ambient.d.ts +41 -0
- package/tinybuild.config.js +109 -0
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FEAT_TIME,
|
|
3
|
+
} from './feats.js';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
assign,
|
|
7
|
+
|
|
8
|
+
abs,
|
|
9
|
+
min,
|
|
10
|
+
max,
|
|
11
|
+
|
|
12
|
+
inf,
|
|
13
|
+
pow,
|
|
14
|
+
log2,
|
|
15
|
+
log10,
|
|
16
|
+
genIncrs,
|
|
17
|
+
round,
|
|
18
|
+
incrRoundUp,
|
|
19
|
+
roundDec,
|
|
20
|
+
floor,
|
|
21
|
+
fmtNum,
|
|
22
|
+
fixedDec,
|
|
23
|
+
|
|
24
|
+
retArg1,
|
|
25
|
+
noop,
|
|
26
|
+
ceil,
|
|
27
|
+
closestIdx,
|
|
28
|
+
} from './utils.js';
|
|
29
|
+
|
|
30
|
+
import {
|
|
31
|
+
hexBlack,
|
|
32
|
+
WIDTH,
|
|
33
|
+
HEIGHT,
|
|
34
|
+
LEGEND_DISP,
|
|
35
|
+
} from './strings.js';
|
|
36
|
+
|
|
37
|
+
import {
|
|
38
|
+
placeDiv,
|
|
39
|
+
setStylePx,
|
|
40
|
+
} from './dom.js';
|
|
41
|
+
|
|
42
|
+
import { DateZoned, fmtDate, floorSOP, PERIOD_DAY, PERIOD_MONTH, PERIOD_YEAR } from './fmtDate.js';
|
|
43
|
+
|
|
44
|
+
//export const series = [];
|
|
45
|
+
|
|
46
|
+
// default formatters:
|
|
47
|
+
|
|
48
|
+
const onlyWhole = v => v % 1 == 0;
|
|
49
|
+
|
|
50
|
+
const allMults = [1,2,2.5,5];
|
|
51
|
+
|
|
52
|
+
// ...0.01, 0.02, 0.025, 0.05, 0.1, 0.2, 0.25, 0.5
|
|
53
|
+
export const decIncrs = genIncrs(10, -32, 0, allMults);
|
|
54
|
+
|
|
55
|
+
// 1, 2, 2.5, 5, 10, 20, 25, 50...
|
|
56
|
+
export const oneIncrs = genIncrs(10, 0, 32, allMults);
|
|
57
|
+
|
|
58
|
+
// 1, 2, 5, 10, 20, 25, 50...
|
|
59
|
+
export const wholeIncrs = oneIncrs.filter(onlyWhole);
|
|
60
|
+
|
|
61
|
+
export const numIncrs = decIncrs.concat(oneIncrs);
|
|
62
|
+
|
|
63
|
+
const NL = "\n";
|
|
64
|
+
|
|
65
|
+
const yyyy = "{YYYY}";
|
|
66
|
+
const NLyyyy = NL + yyyy;
|
|
67
|
+
const md = "{M}/{D}";
|
|
68
|
+
const NLmd = NL + md;
|
|
69
|
+
const NLmdyy = NLmd + "/{YY}";
|
|
70
|
+
|
|
71
|
+
const aa = "{aa}";
|
|
72
|
+
const hmm = "{h}:{mm}";
|
|
73
|
+
const hmmaa = hmm + aa;
|
|
74
|
+
const NLhmmaa = NL + hmmaa;
|
|
75
|
+
const ss = ":{ss}";
|
|
76
|
+
|
|
77
|
+
const _ = null;
|
|
78
|
+
|
|
79
|
+
function genTimeStuffs(ms) {
|
|
80
|
+
let s = ms * 1e3,
|
|
81
|
+
m = s * 60,
|
|
82
|
+
h = m * 60,
|
|
83
|
+
d = h * 24,
|
|
84
|
+
mo = d * 30,
|
|
85
|
+
y = d * 365;
|
|
86
|
+
|
|
87
|
+
// min of 1e-3 prevents setting a temporal x ticks too small since Date objects cannot advance ticks smaller than 1ms
|
|
88
|
+
let subSecIncrs = ms == 1 ? genIncrs(10, 0, 3, allMults).filter(onlyWhole) : genIncrs(10, -3, 0, allMults);
|
|
89
|
+
|
|
90
|
+
let timeIncrs = subSecIncrs.concat([
|
|
91
|
+
// minute divisors (# of secs)
|
|
92
|
+
s,
|
|
93
|
+
s * 5,
|
|
94
|
+
s * 10,
|
|
95
|
+
s * 15,
|
|
96
|
+
s * 30,
|
|
97
|
+
// hour divisors (# of mins)
|
|
98
|
+
m,
|
|
99
|
+
m * 5,
|
|
100
|
+
m * 10,
|
|
101
|
+
m * 15,
|
|
102
|
+
m * 30,
|
|
103
|
+
// day divisors (# of hrs)
|
|
104
|
+
h,
|
|
105
|
+
h * 2,
|
|
106
|
+
h * 3,
|
|
107
|
+
h * 4,
|
|
108
|
+
h * 6,
|
|
109
|
+
h * 8,
|
|
110
|
+
h * 12,
|
|
111
|
+
// month divisors TODO: need more?
|
|
112
|
+
d,
|
|
113
|
+
d * 2,
|
|
114
|
+
d * 3,
|
|
115
|
+
d * 4,
|
|
116
|
+
d * 5,
|
|
117
|
+
d * 6,
|
|
118
|
+
d * 7,
|
|
119
|
+
d * 8,
|
|
120
|
+
d * 9,
|
|
121
|
+
d * 10,
|
|
122
|
+
d * 15,
|
|
123
|
+
// year divisors (# months, approx)
|
|
124
|
+
mo,
|
|
125
|
+
mo * 2,
|
|
126
|
+
mo * 3,
|
|
127
|
+
mo * 4,
|
|
128
|
+
mo * 6,
|
|
129
|
+
// century divisors
|
|
130
|
+
y,
|
|
131
|
+
y * 2,
|
|
132
|
+
y * 5,
|
|
133
|
+
y * 10,
|
|
134
|
+
y * 25,
|
|
135
|
+
y * 50,
|
|
136
|
+
y * 100,
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
// [0]: minimum num secs in the tick incr
|
|
140
|
+
// [1]: default tick format
|
|
141
|
+
// [2-7]: rollover tick formats
|
|
142
|
+
// [8]: mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
|
|
143
|
+
const _timeAxisStamps = [
|
|
144
|
+
// tick incr default year month day hour min sec mode
|
|
145
|
+
[y, yyyy, _, _, _, _, _, _, 1],
|
|
146
|
+
[d * 28, "{MMM}", NLyyyy, _, _, _, _, _, 1],
|
|
147
|
+
[d, md, NLyyyy, _, _, _, _, _, 1],
|
|
148
|
+
[h, "{h}" + aa, NLmdyy, _, NLmd, _, _, _, 1],
|
|
149
|
+
[m, hmmaa, NLmdyy, _, NLmd, _, _, _, 1],
|
|
150
|
+
[s, ss, NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
|
|
151
|
+
[ms, ss + ".{fff}", NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
// the ensures that axis ticks, values & grid are aligned to logical temporal breakpoints and not an arbitrary timestamp
|
|
155
|
+
// https://www.timeanddate.com/time/dst/
|
|
156
|
+
// https://www.timeanddate.com/time/dst/2019.html
|
|
157
|
+
// https://www.epochconverter.com/timezones
|
|
158
|
+
function timeAxisSplits(tzDate) {
|
|
159
|
+
return (self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace) => {
|
|
160
|
+
let splits = [];
|
|
161
|
+
let isYr = foundIncr >= y;
|
|
162
|
+
let isMo = foundIncr >= mo && foundIncr < y;
|
|
163
|
+
let isDays = foundIncr >= d && foundIncr < mo;
|
|
164
|
+
let isHours = foundIncr > h && foundIncr < d
|
|
165
|
+
|
|
166
|
+
// get the timezone-adjusted date
|
|
167
|
+
let minDate = tzDate(scaleMin);
|
|
168
|
+
let minDateTs = roundDec(minDate * ms, 3);
|
|
169
|
+
|
|
170
|
+
// get ts of 12am (this lands us at or before the original scaleMin)
|
|
171
|
+
let minMin = floorSOP(minDate, isYr || isMo ? PERIOD_YEAR : isDays ? PERIOD_MONTH : PERIOD_DAY); // should we do PERIOD_HOUR?
|
|
172
|
+
let minMinTs = roundDec(minMin * ms, 3);
|
|
173
|
+
|
|
174
|
+
if (isDays) {
|
|
175
|
+
let incrDays = foundIncr / d;
|
|
176
|
+
|
|
177
|
+
// incrs to add to month baseline
|
|
178
|
+
let skip = floor((minDate.getDate() - 1) / incrDays);
|
|
179
|
+
let split = minMinTs + (foundIncr * skip);
|
|
180
|
+
|
|
181
|
+
do {
|
|
182
|
+
let date = tzDate(split);
|
|
183
|
+
// adjust for DST misses
|
|
184
|
+
let hour = date.getHours();
|
|
185
|
+
if (hour != 0) {
|
|
186
|
+
split += hour > 12 ? h : -h;
|
|
187
|
+
date = tzDate(split);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// rolled over into next month onto non-divisible incr, reset baseline
|
|
191
|
+
if ((date.getDate() - 1) % incrDays > 0) {
|
|
192
|
+
date = floorSOP(date, PERIOD_MONTH);
|
|
193
|
+
split = date.getTime() * ms;
|
|
194
|
+
|
|
195
|
+
// make sure we're not rendering a collision between 31 and 1
|
|
196
|
+
if (split - splits[splits.length - 1] < foundIncr * 0.7)
|
|
197
|
+
splits.pop();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (split > scaleMax)
|
|
201
|
+
break;
|
|
202
|
+
|
|
203
|
+
if (split >= scaleMin)
|
|
204
|
+
splits.push(split);
|
|
205
|
+
|
|
206
|
+
split += foundIncr;
|
|
207
|
+
} while (1);
|
|
208
|
+
}
|
|
209
|
+
else if (isMo || isYr) {
|
|
210
|
+
let subIncrs = 1;
|
|
211
|
+
let subIncrDays = 1;
|
|
212
|
+
let periodType = 0;
|
|
213
|
+
let periodMin = 0;
|
|
214
|
+
|
|
215
|
+
if (isMo) {
|
|
216
|
+
subIncrs = foundIncr / mo;
|
|
217
|
+
subIncrDays = 32;
|
|
218
|
+
periodType = PERIOD_MONTH;
|
|
219
|
+
periodMin = minDate.getMonth();
|
|
220
|
+
}
|
|
221
|
+
else if (isYr) {
|
|
222
|
+
subIncrs = foundIncr / y;
|
|
223
|
+
subIncrDays = 366;
|
|
224
|
+
periodType = PERIOD_YEAR;
|
|
225
|
+
periodMin = minDate.getYear();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
foundIncr = subIncrs * subIncrDays * d;
|
|
229
|
+
|
|
230
|
+
let skip = floor(periodMin / subIncrDays);
|
|
231
|
+
let split = minMinTs + (foundIncr * skip);
|
|
232
|
+
|
|
233
|
+
do {
|
|
234
|
+
let date = floorSOP(tzDate(split), periodType);
|
|
235
|
+
split = date.getTime() * ms;
|
|
236
|
+
|
|
237
|
+
if (split > scaleMax)
|
|
238
|
+
break;
|
|
239
|
+
|
|
240
|
+
if (split >= scaleMin)
|
|
241
|
+
splits.push(split);
|
|
242
|
+
|
|
243
|
+
split += foundIncr;
|
|
244
|
+
} while (1);
|
|
245
|
+
}
|
|
246
|
+
else if (isHours) {
|
|
247
|
+
let incrHours = foundIncr / h;
|
|
248
|
+
|
|
249
|
+
let skip = floor(minDate.getHours() / incrHours);
|
|
250
|
+
let split = minMinTs + (foundIncr * skip);
|
|
251
|
+
|
|
252
|
+
do {
|
|
253
|
+
let date = tzDate(split);
|
|
254
|
+
|
|
255
|
+
// adjust for DST misses
|
|
256
|
+
let hour = date.getHours();
|
|
257
|
+
if (hour % incrHours > 0) {
|
|
258
|
+
let hour2 = tzDate(split - h).getHours();
|
|
259
|
+
split += hour2 % incrHours == 0 ? -h : h;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (split > scaleMax)
|
|
263
|
+
break;
|
|
264
|
+
|
|
265
|
+
if (split >= scaleMin)
|
|
266
|
+
splits.push(split);
|
|
267
|
+
|
|
268
|
+
split += foundIncr;
|
|
269
|
+
} while (1);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
let split = minMinTs + incrRoundUp(minDateTs - minMinTs, foundIncr);
|
|
273
|
+
|
|
274
|
+
do {
|
|
275
|
+
if (split > scaleMax)
|
|
276
|
+
break;
|
|
277
|
+
|
|
278
|
+
if (split >= scaleMin)
|
|
279
|
+
splits.push(split);
|
|
280
|
+
|
|
281
|
+
split += foundIncr;
|
|
282
|
+
} while (1);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return splits;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return [
|
|
290
|
+
timeIncrs,
|
|
291
|
+
_timeAxisStamps,
|
|
292
|
+
timeAxisSplits,
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export const [ timeIncrsMs, _timeAxisStampsMs, timeAxisSplitsMs ] = FEAT_TIME && genTimeStuffs(1);
|
|
297
|
+
export const [ timeIncrsS, _timeAxisStampsS, timeAxisSplitsS ] = FEAT_TIME && genTimeStuffs(1e-3);
|
|
298
|
+
|
|
299
|
+
// base 2
|
|
300
|
+
const binIncrs = genIncrs(2, -53, 53, [1]);
|
|
301
|
+
|
|
302
|
+
/*
|
|
303
|
+
console.log({
|
|
304
|
+
decIncrs,
|
|
305
|
+
oneIncrs,
|
|
306
|
+
wholeIncrs,
|
|
307
|
+
numIncrs,
|
|
308
|
+
timeIncrs,
|
|
309
|
+
fixedDec,
|
|
310
|
+
});
|
|
311
|
+
*/
|
|
312
|
+
|
|
313
|
+
export function timeAxisStamps(stampCfg, fmtDate) {
|
|
314
|
+
return stampCfg.map(s => s.map((v, i) =>
|
|
315
|
+
i == 0 || i == 8 || v == null ? v : fmtDate(i == 1 || s[8] == 0 ? v : s[1] + v)
|
|
316
|
+
));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// TODO: will need to accept spaces[] and pull incr into the loop when grid will be non-uniform, eg for log scales.
|
|
320
|
+
// currently we ignore this for months since they're *nearly* uniform and the added complexity is not worth it
|
|
321
|
+
export function timeAxisVals(tzDate, stamps) {
|
|
322
|
+
return (self, splits, axisIdx, foundSpace, foundIncr) => {
|
|
323
|
+
let s = stamps.find(s => foundIncr >= s[0]) || stamps[stamps.length - 1];
|
|
324
|
+
|
|
325
|
+
// these track boundaries when a full label is needed again
|
|
326
|
+
let prevYear;
|
|
327
|
+
let prevMnth;
|
|
328
|
+
let prevDate;
|
|
329
|
+
let prevHour;
|
|
330
|
+
let prevMins;
|
|
331
|
+
let prevSecs;
|
|
332
|
+
|
|
333
|
+
return splits.map(split => {
|
|
334
|
+
let date = tzDate(split);
|
|
335
|
+
|
|
336
|
+
let newYear = date.getFullYear();
|
|
337
|
+
let newMnth = date.getMonth();
|
|
338
|
+
let newDate = date.getDate();
|
|
339
|
+
let newHour = date.getHours();
|
|
340
|
+
let newMins = date.getMinutes();
|
|
341
|
+
let newSecs = date.getSeconds();
|
|
342
|
+
|
|
343
|
+
let stamp = (
|
|
344
|
+
newYear != prevYear && s[2] ||
|
|
345
|
+
newMnth != prevMnth && s[3] ||
|
|
346
|
+
newDate != prevDate && s[4] ||
|
|
347
|
+
newHour != prevHour && s[5] ||
|
|
348
|
+
newMins != prevMins && s[6] ||
|
|
349
|
+
newSecs != prevSecs && s[7] ||
|
|
350
|
+
s[1]
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
prevYear = newYear;
|
|
354
|
+
prevMnth = newMnth;
|
|
355
|
+
prevDate = newDate;
|
|
356
|
+
prevHour = newHour;
|
|
357
|
+
prevMins = newMins;
|
|
358
|
+
prevSecs = newSecs;
|
|
359
|
+
|
|
360
|
+
return stamp(date);
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// for when axis.values is defined as a static fmtDate template string
|
|
366
|
+
export function timeAxisVal(tzDate, dateTpl) {
|
|
367
|
+
let stamp = fmtDate(dateTpl);
|
|
368
|
+
return (self, splits, axisIdx, foundSpace, foundIncr) => splits.map(split => stamp(tzDate(split)));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function mkDate(y, m, d) {
|
|
372
|
+
return new Date(y, m, d);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function timeSeriesStamp(stampCfg, fmtDate) {
|
|
376
|
+
return fmtDate(stampCfg);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
export const _timeSeriesStamp = '{YYYY}-{MM}-{DD} {h}:{mm}{aa}';
|
|
380
|
+
|
|
381
|
+
export function timeSeriesVal(tzDate, stamp) {
|
|
382
|
+
return (self, val, seriesIdx, dataIdx) => dataIdx == null ? LEGEND_DISP : stamp(tzDate(val));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export function legendStroke(self, seriesIdx) {
|
|
386
|
+
let s = self.series[seriesIdx];
|
|
387
|
+
return s.width ? s.stroke(self, seriesIdx) : s.points.width ? s.points.stroke(self, seriesIdx) : null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function legendFill(self, seriesIdx) {
|
|
391
|
+
return self.series[seriesIdx].fill(self, seriesIdx);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export const legendOpts = {
|
|
395
|
+
show: true,
|
|
396
|
+
live: true,
|
|
397
|
+
isolate: false,
|
|
398
|
+
mount: noop,
|
|
399
|
+
markers: {
|
|
400
|
+
show: true,
|
|
401
|
+
width: 2,
|
|
402
|
+
stroke: legendStroke,
|
|
403
|
+
fill: legendFill,
|
|
404
|
+
dash: "solid",
|
|
405
|
+
},
|
|
406
|
+
idx: null,
|
|
407
|
+
idxs: null,
|
|
408
|
+
values: [],
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
function cursorPointShow(self, si) {
|
|
412
|
+
let o = self.cursor.points;
|
|
413
|
+
|
|
414
|
+
let pt = placeDiv();
|
|
415
|
+
|
|
416
|
+
let size = o.size(self, si);
|
|
417
|
+
setStylePx(pt, WIDTH, size);
|
|
418
|
+
setStylePx(pt, HEIGHT, size);
|
|
419
|
+
|
|
420
|
+
let mar = size / -2;
|
|
421
|
+
setStylePx(pt, "marginLeft", mar);
|
|
422
|
+
setStylePx(pt, "marginTop", mar);
|
|
423
|
+
|
|
424
|
+
let width = o.width(self, si, size);
|
|
425
|
+
width && setStylePx(pt, "borderWidth", width);
|
|
426
|
+
|
|
427
|
+
return pt;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function cursorPointFill(self, si) {
|
|
431
|
+
let sp = self.series[si].points;
|
|
432
|
+
return sp._fill || sp._stroke;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function cursorPointStroke(self, si) {
|
|
436
|
+
let sp = self.series[si].points;
|
|
437
|
+
return sp._stroke || sp._fill;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function cursorPointSize(self, si) {
|
|
441
|
+
let sp = self.series[si].points;
|
|
442
|
+
return sp.size;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const moveTuple = [0,0];
|
|
446
|
+
|
|
447
|
+
function cursorMove(self, mouseLeft1, mouseTop1) {
|
|
448
|
+
moveTuple[0] = mouseLeft1;
|
|
449
|
+
moveTuple[1] = mouseTop1;
|
|
450
|
+
return moveTuple;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function filtBtn0(self, targ, handle, onlyTarg = true) {
|
|
454
|
+
return e => {
|
|
455
|
+
e.button == 0 && (!onlyTarg || e.target == targ) && handle(e);
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function filtTarg(self, targ, handle, onlyTarg = true) {
|
|
460
|
+
return e => {
|
|
461
|
+
(!onlyTarg || e.target == targ) && handle(e);
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export const cursorOpts = {
|
|
466
|
+
show: true,
|
|
467
|
+
x: true,
|
|
468
|
+
y: true,
|
|
469
|
+
lock: false,
|
|
470
|
+
move: cursorMove,
|
|
471
|
+
points: {
|
|
472
|
+
one: false,
|
|
473
|
+
show: cursorPointShow,
|
|
474
|
+
size: cursorPointSize,
|
|
475
|
+
width: 0,
|
|
476
|
+
stroke: cursorPointStroke,
|
|
477
|
+
fill: cursorPointFill,
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
bind: {
|
|
481
|
+
mousedown: filtBtn0,
|
|
482
|
+
mouseup: filtBtn0,
|
|
483
|
+
click: filtBtn0, // legend clicks, not .u-over clicks
|
|
484
|
+
dblclick: filtBtn0,
|
|
485
|
+
|
|
486
|
+
mousemove: filtTarg,
|
|
487
|
+
mouseleave: filtTarg,
|
|
488
|
+
mouseenter: filtTarg,
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
drag: {
|
|
492
|
+
setScale: true,
|
|
493
|
+
x: true,
|
|
494
|
+
y: false,
|
|
495
|
+
dist: 0,
|
|
496
|
+
uni: null,
|
|
497
|
+
click: (self, e) => {
|
|
498
|
+
// e.preventDefault();
|
|
499
|
+
e.stopPropagation();
|
|
500
|
+
e.stopImmediatePropagation();
|
|
501
|
+
},
|
|
502
|
+
_x: false,
|
|
503
|
+
_y: false,
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
focus: {
|
|
507
|
+
dist: (self, seriesIdx, dataIdx, valPos, curPos) => valPos - curPos,
|
|
508
|
+
prox: -1,
|
|
509
|
+
bias: 0,
|
|
510
|
+
},
|
|
511
|
+
|
|
512
|
+
hover: {
|
|
513
|
+
skip: [void 0],
|
|
514
|
+
prox: null,
|
|
515
|
+
bias: 0,
|
|
516
|
+
},
|
|
517
|
+
|
|
518
|
+
left: -10,
|
|
519
|
+
top: -10,
|
|
520
|
+
idx: null,
|
|
521
|
+
dataIdx: null,
|
|
522
|
+
idxs: null,
|
|
523
|
+
|
|
524
|
+
event: null,
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const axisLines = {
|
|
528
|
+
show: true,
|
|
529
|
+
stroke: "rgba(0,0,0,0.07)",
|
|
530
|
+
width: 2,
|
|
531
|
+
// dash: [],
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
const grid = assign({}, axisLines, {
|
|
535
|
+
filter: retArg1,
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
const ticks = assign({}, grid, {
|
|
539
|
+
size: 10,
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
const border = assign({}, axisLines, {
|
|
543
|
+
show: false,
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
const font = '12px system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"';
|
|
547
|
+
const labelFont = "bold " + font;
|
|
548
|
+
const lineGap = 1.5; // font-size multiplier
|
|
549
|
+
|
|
550
|
+
export const xAxisOpts = {
|
|
551
|
+
show: true,
|
|
552
|
+
scale: "x",
|
|
553
|
+
stroke: hexBlack,
|
|
554
|
+
space: 50,
|
|
555
|
+
gap: 5,
|
|
556
|
+
alignTo: 1,
|
|
557
|
+
size: 50,
|
|
558
|
+
labelGap: 0,
|
|
559
|
+
labelSize: 30,
|
|
560
|
+
labelFont,
|
|
561
|
+
side: 2,
|
|
562
|
+
// class: "x-vals",
|
|
563
|
+
// incrs: timeIncrs,
|
|
564
|
+
// values: timeVals,
|
|
565
|
+
// filter: retArg1,
|
|
566
|
+
grid,
|
|
567
|
+
ticks,
|
|
568
|
+
border,
|
|
569
|
+
font,
|
|
570
|
+
lineGap,
|
|
571
|
+
rotate: 0,
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
export const numSeriesLabel = "Value";
|
|
575
|
+
export const timeSeriesLabel = "Time";
|
|
576
|
+
|
|
577
|
+
export const xSeriesOpts = {
|
|
578
|
+
show: true,
|
|
579
|
+
scale: "x",
|
|
580
|
+
auto: false,
|
|
581
|
+
sorted: 1,
|
|
582
|
+
// label: "Time",
|
|
583
|
+
// value: v => stamp(new Date(v * 1e3)),
|
|
584
|
+
|
|
585
|
+
// internal caches
|
|
586
|
+
min: inf,
|
|
587
|
+
max: -inf,
|
|
588
|
+
idxs: [],
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
export function numAxisVals(self, splits, axisIdx, foundSpace, foundIncr) {
|
|
592
|
+
return splits.map(v => v == null ? "" : fmtNum(v));
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export function numAxisSplits(self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace, forceMin) {
|
|
596
|
+
let splits = [];
|
|
597
|
+
|
|
598
|
+
let numDec = fixedDec.get(foundIncr) || 0;
|
|
599
|
+
|
|
600
|
+
scaleMin = forceMin ? scaleMin : roundDec(incrRoundUp(scaleMin, foundIncr), numDec);
|
|
601
|
+
|
|
602
|
+
for (let val = scaleMin; val <= scaleMax; val = roundDec(val + foundIncr, numDec))
|
|
603
|
+
splits.push(Object.is(val, -0) ? 0 : val); // coalesces -0
|
|
604
|
+
|
|
605
|
+
return splits;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// this doesnt work for sin, which needs to come off from 0 independently in pos and neg dirs
|
|
609
|
+
export function logAxisSplits(self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace, forceMin) {
|
|
610
|
+
const splits = [];
|
|
611
|
+
|
|
612
|
+
const logBase = self.scales[self.axes[axisIdx].scale].log;
|
|
613
|
+
|
|
614
|
+
const logFn = logBase == 10 ? log10 : log2;
|
|
615
|
+
|
|
616
|
+
const exp = floor(logFn(scaleMin));
|
|
617
|
+
|
|
618
|
+
foundIncr = pow(logBase, exp);
|
|
619
|
+
|
|
620
|
+
// boo: 10 ** -24 === 1.0000000000000001e-24
|
|
621
|
+
// this grabs the proper 1e-24 one
|
|
622
|
+
if (logBase == 10)
|
|
623
|
+
foundIncr = numIncrs[closestIdx(foundIncr, numIncrs)];
|
|
624
|
+
|
|
625
|
+
let split = foundIncr;
|
|
626
|
+
let nextMagIncr = foundIncr * logBase;
|
|
627
|
+
|
|
628
|
+
if (logBase == 10)
|
|
629
|
+
nextMagIncr = numIncrs[closestIdx(nextMagIncr, numIncrs)];
|
|
630
|
+
|
|
631
|
+
do {
|
|
632
|
+
if (split >= scaleMin)
|
|
633
|
+
splits.push(split);
|
|
634
|
+
|
|
635
|
+
split = split + foundIncr;
|
|
636
|
+
|
|
637
|
+
if (logBase == 10 && !fixedDec.has(split))
|
|
638
|
+
split = roundDec(split, fixedDec.get(foundIncr));
|
|
639
|
+
|
|
640
|
+
if (split >= nextMagIncr) {
|
|
641
|
+
foundIncr = split;
|
|
642
|
+
nextMagIncr = foundIncr * logBase;
|
|
643
|
+
|
|
644
|
+
if (logBase == 10)
|
|
645
|
+
nextMagIncr = numIncrs[closestIdx(nextMagIncr, numIncrs)];
|
|
646
|
+
}
|
|
647
|
+
} while (split <= scaleMax);
|
|
648
|
+
|
|
649
|
+
return splits;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
export function asinhAxisSplits(self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace, forceMin) {
|
|
653
|
+
let sc = self.scales[self.axes[axisIdx].scale];
|
|
654
|
+
|
|
655
|
+
let linthresh = sc.asinh;
|
|
656
|
+
|
|
657
|
+
let posSplits = scaleMax > linthresh ? logAxisSplits(self, axisIdx, max(linthresh, scaleMin), scaleMax, foundIncr, foundSpace, forceMin) : [linthresh];
|
|
658
|
+
let zero = scaleMax >= 0 && scaleMin <= 0 ? [0] : [];
|
|
659
|
+
let negSplits = scaleMin < -linthresh ? logAxisSplits(self, axisIdx, max(linthresh, -scaleMax), -scaleMin, foundIncr, foundSpace, forceMin): [linthresh];
|
|
660
|
+
|
|
661
|
+
return negSplits.reverse().map(v => -v).concat(zero, posSplits);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
const RE_ALL = /./;
|
|
665
|
+
const RE_12357 = /[12357]/;
|
|
666
|
+
const RE_125 = /[125]/;
|
|
667
|
+
const RE_1 = /1/;
|
|
668
|
+
|
|
669
|
+
const _filt = (splits, distr, re, keepMod) => splits.map((v, i) => ((distr == 4 && v == 0) || i % keepMod == 0 && re.test(v.toExponential()[v < 0 ? 1 : 0])) ? v : null);
|
|
670
|
+
|
|
671
|
+
export function log10AxisValsFilt(self, splits, axisIdx, foundSpace, foundIncr) {
|
|
672
|
+
let axis = self.axes[axisIdx];
|
|
673
|
+
let scaleKey = axis.scale;
|
|
674
|
+
let sc = self.scales[scaleKey];
|
|
675
|
+
|
|
676
|
+
// if (sc.distr == 3 && sc.log == 2)
|
|
677
|
+
// return splits;
|
|
678
|
+
|
|
679
|
+
let valToPos = self.valToPos;
|
|
680
|
+
|
|
681
|
+
let minSpace = axis._space;
|
|
682
|
+
|
|
683
|
+
let _10 = valToPos(10, scaleKey);
|
|
684
|
+
|
|
685
|
+
let re = (
|
|
686
|
+
valToPos(9, scaleKey) - _10 >= minSpace ? RE_ALL :
|
|
687
|
+
valToPos(7, scaleKey) - _10 >= minSpace ? RE_12357 :
|
|
688
|
+
valToPos(5, scaleKey) - _10 >= minSpace ? RE_125 :
|
|
689
|
+
RE_1
|
|
690
|
+
);
|
|
691
|
+
|
|
692
|
+
if (re == RE_1) {
|
|
693
|
+
let magSpace = abs(valToPos(1, scaleKey) - _10);
|
|
694
|
+
|
|
695
|
+
if (magSpace < minSpace)
|
|
696
|
+
return _filt(splits.slice().reverse(), sc.distr, re, ceil(minSpace / magSpace)).reverse(); // max->min skip
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return _filt(splits, sc.distr, re, 1);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
export function log2AxisValsFilt(self, splits, axisIdx, foundSpace, foundIncr) {
|
|
703
|
+
let axis = self.axes[axisIdx];
|
|
704
|
+
let scaleKey = axis.scale;
|
|
705
|
+
let minSpace = axis._space;
|
|
706
|
+
let valToPos = self.valToPos;
|
|
707
|
+
|
|
708
|
+
let magSpace = abs(valToPos(1, scaleKey) - valToPos(2, scaleKey));
|
|
709
|
+
|
|
710
|
+
if (magSpace < minSpace)
|
|
711
|
+
return _filt(splits.slice().reverse(), 3, RE_ALL, ceil(minSpace / magSpace)).reverse(); // max->min skip
|
|
712
|
+
|
|
713
|
+
return splits;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
export function numSeriesVal(self, val, seriesIdx, dataIdx) {
|
|
717
|
+
return dataIdx == null ? LEGEND_DISP : val == null ? "" : fmtNum(val);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
export const yAxisOpts = {
|
|
721
|
+
show: true,
|
|
722
|
+
scale: "y",
|
|
723
|
+
stroke: hexBlack,
|
|
724
|
+
space: 30,
|
|
725
|
+
gap: 5,
|
|
726
|
+
alignTo: 1,
|
|
727
|
+
size: 50,
|
|
728
|
+
labelGap: 0,
|
|
729
|
+
labelSize: 30,
|
|
730
|
+
labelFont,
|
|
731
|
+
side: 3,
|
|
732
|
+
// class: "y-vals",
|
|
733
|
+
// incrs: numIncrs,
|
|
734
|
+
// values: (vals, space) => vals,
|
|
735
|
+
// filter: retArg1,
|
|
736
|
+
grid,
|
|
737
|
+
ticks,
|
|
738
|
+
border,
|
|
739
|
+
font,
|
|
740
|
+
lineGap,
|
|
741
|
+
rotate: 0,
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
// takes stroke width
|
|
745
|
+
export function ptDia(width, mult) {
|
|
746
|
+
let dia = 3 + (width || 1) * 2;
|
|
747
|
+
return roundDec(dia * mult, 3);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function seriesPointsShow(self, si) {
|
|
751
|
+
let { scale, idxs } = self.series[0];
|
|
752
|
+
let xData = self._data[0];
|
|
753
|
+
let p0 = self.valToPos(xData[idxs[0]], scale, true);
|
|
754
|
+
let p1 = self.valToPos(xData[idxs[1]], scale, true);
|
|
755
|
+
let dim = abs(p1 - p0);
|
|
756
|
+
|
|
757
|
+
let s = self.series[si];
|
|
758
|
+
// const dia = ptDia(s.width, self.pxRatio);
|
|
759
|
+
let maxPts = dim / (s.points.space * self.pxRatio);
|
|
760
|
+
return idxs[1] - idxs[0] <= maxPts;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const facet = {
|
|
764
|
+
scale: null,
|
|
765
|
+
auto: true,
|
|
766
|
+
sorted: 0,
|
|
767
|
+
|
|
768
|
+
// internal caches
|
|
769
|
+
min: inf,
|
|
770
|
+
max: -inf,
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
const gaps = (self, seriesIdx, idx0, idx1, nullGaps) => nullGaps;
|
|
774
|
+
|
|
775
|
+
export const xySeriesOpts = {
|
|
776
|
+
show: true,
|
|
777
|
+
auto: true,
|
|
778
|
+
sorted: 0,
|
|
779
|
+
gaps,
|
|
780
|
+
alpha: 1,
|
|
781
|
+
facets: [
|
|
782
|
+
assign({}, facet, {scale: 'x'}),
|
|
783
|
+
assign({}, facet, {scale: 'y'}),
|
|
784
|
+
],
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
export const ySeriesOpts = {
|
|
788
|
+
scale: "y",
|
|
789
|
+
auto: true,
|
|
790
|
+
sorted: 0,
|
|
791
|
+
show: true,
|
|
792
|
+
spanGaps: false,
|
|
793
|
+
gaps,
|
|
794
|
+
alpha: 1,
|
|
795
|
+
points: {
|
|
796
|
+
show: seriesPointsShow,
|
|
797
|
+
filter: null,
|
|
798
|
+
// paths:
|
|
799
|
+
// stroke: "#000",
|
|
800
|
+
// fill: "#fff",
|
|
801
|
+
// width: 1,
|
|
802
|
+
// size: 10,
|
|
803
|
+
},
|
|
804
|
+
// label: "Value",
|
|
805
|
+
// value: v => v,
|
|
806
|
+
values: null,
|
|
807
|
+
|
|
808
|
+
// internal caches
|
|
809
|
+
min: inf,
|
|
810
|
+
max: -inf,
|
|
811
|
+
idxs: [],
|
|
812
|
+
|
|
813
|
+
path: null,
|
|
814
|
+
clip: null,
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
export function clampScale(self, val, scaleMin, scaleMax, scaleKey) {
|
|
818
|
+
/*
|
|
819
|
+
if (val < 0) {
|
|
820
|
+
let cssHgt = self.bbox.height / self.pxRatio;
|
|
821
|
+
let absPos = self.valToPos(abs(val), scaleKey);
|
|
822
|
+
let fromBtm = cssHgt - absPos;
|
|
823
|
+
return self.posToVal(cssHgt + fromBtm, scaleKey);
|
|
824
|
+
}
|
|
825
|
+
*/
|
|
826
|
+
return scaleMin / 10;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
export const xScaleOpts = {
|
|
830
|
+
time: FEAT_TIME,
|
|
831
|
+
auto: true,
|
|
832
|
+
distr: 1,
|
|
833
|
+
log: 10,
|
|
834
|
+
asinh: 1,
|
|
835
|
+
min: null,
|
|
836
|
+
max: null,
|
|
837
|
+
dir: 1,
|
|
838
|
+
ori: 0,
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
export const yScaleOpts = assign({}, xScaleOpts, {
|
|
842
|
+
time: false,
|
|
843
|
+
ori: 1,
|
|
844
|
+
});
|