@windborne/grapher 1.0.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 (112) hide show
  1. package/.eslintrc.js +85 -0
  2. package/.idea/codeStyles/Project.xml +19 -0
  3. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  4. package/.idea/grapher.iml +12 -0
  5. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  6. package/.idea/misc.xml +6 -0
  7. package/.idea/modules.xml +8 -0
  8. package/.idea/vcs.xml +6 -0
  9. package/0.bundle.js +2 -0
  10. package/0.bundle.js.map +1 -0
  11. package/1767282193a714f63082.module.wasm +0 -0
  12. package/537.bundle.js +2 -0
  13. package/537.bundle.js.map +1 -0
  14. package/831.bundle.js +2 -0
  15. package/831.bundle.js.map +1 -0
  16. package/bundle.js +2 -0
  17. package/bundle.js.map +1 -0
  18. package/package.json +75 -0
  19. package/readme.md +129 -0
  20. package/src/components/annotations.js +62 -0
  21. package/src/components/context_menu.js +73 -0
  22. package/src/components/draggable_points.js +114 -0
  23. package/src/components/graph_body.js +292 -0
  24. package/src/components/graph_title.js +16 -0
  25. package/src/components/options.js +111 -0
  26. package/src/components/percentile_button.js +72 -0
  27. package/src/components/range_graph.js +352 -0
  28. package/src/components/range_selection.js +175 -0
  29. package/src/components/range_selection_button.js +26 -0
  30. package/src/components/range_selection_button_base.js +51 -0
  31. package/src/components/series_key.js +235 -0
  32. package/src/components/series_key_axis_container.js +70 -0
  33. package/src/components/series_key_item.js +52 -0
  34. package/src/components/sidebar.js +76 -0
  35. package/src/components/tooltip.js +244 -0
  36. package/src/components/vertical_lines.js +70 -0
  37. package/src/components/x_axis.js +124 -0
  38. package/src/components/y_axis.js +239 -0
  39. package/src/eventable.js +65 -0
  40. package/src/grapher.js +367 -0
  41. package/src/grapher.scss +914 -0
  42. package/src/helpers/axis_sizes.js +2 -0
  43. package/src/helpers/binary_search.js +67 -0
  44. package/src/helpers/color_to_vector.js +35 -0
  45. package/src/helpers/colors.js +27 -0
  46. package/src/helpers/custom_prop_types.js +159 -0
  47. package/src/helpers/flatten_simple_data.js +81 -0
  48. package/src/helpers/format.js +233 -0
  49. package/src/helpers/generator_params_equal.js +10 -0
  50. package/src/helpers/name_for_series.js +16 -0
  51. package/src/helpers/place_grid.js +257 -0
  52. package/src/helpers/pyodide_ready.js +13 -0
  53. package/src/multigrapher.js +105 -0
  54. package/src/renderer/background.frag +7 -0
  55. package/src/renderer/background.vert +7 -0
  56. package/src/renderer/background_program.js +48 -0
  57. package/src/renderer/circle.frag +26 -0
  58. package/src/renderer/circle.vert +12 -0
  59. package/src/renderer/create_gl_program.js +36 -0
  60. package/src/renderer/draw_area.js +159 -0
  61. package/src/renderer/draw_background.js +15 -0
  62. package/src/renderer/draw_bars.js +80 -0
  63. package/src/renderer/draw_line.js +69 -0
  64. package/src/renderer/draw_zero_line.js +24 -0
  65. package/src/renderer/extract_vertices.js +137 -0
  66. package/src/renderer/graph_body_renderer.js +293 -0
  67. package/src/renderer/line.frag +51 -0
  68. package/src/renderer/line.vert +32 -0
  69. package/src/renderer/line_program.js +125 -0
  70. package/src/renderer/paths_from.js +72 -0
  71. package/src/renderer/scale_bounds.js +28 -0
  72. package/src/renderer/size_canvas.js +59 -0
  73. package/src/rust/Cargo.lock +233 -0
  74. package/src/rust/Cargo.toml +35 -0
  75. package/src/rust/pkg/grapher_rs.d.ts +42 -0
  76. package/src/rust/pkg/grapher_rs.js +351 -0
  77. package/src/rust/pkg/grapher_rs_bg.d.ts +11 -0
  78. package/src/rust/pkg/grapher_rs_bg.wasm +0 -0
  79. package/src/rust/pkg/index.js +342 -0
  80. package/src/rust/pkg/index_bg.wasm +0 -0
  81. package/src/rust/pkg/package.json +14 -0
  82. package/src/rust/src/extract_vertices.rs +83 -0
  83. package/src/rust/src/get_point_number.rs +50 -0
  84. package/src/rust/src/lib.rs +15 -0
  85. package/src/rust/src/selected_space_to_render_space.rs +131 -0
  86. package/src/state/average_loop_times.js +15 -0
  87. package/src/state/bound_calculator_from_selection.js +36 -0
  88. package/src/state/bound_calculators.js +41 -0
  89. package/src/state/calculate_annotations_state.js +59 -0
  90. package/src/state/calculate_data_bounds.js +104 -0
  91. package/src/state/calculate_tooltip_state.js +241 -0
  92. package/src/state/data_types.js +13 -0
  93. package/src/state/expand_bounds.js +58 -0
  94. package/src/state/find_matching_axis.js +31 -0
  95. package/src/state/get_default_bounds_calculator.js +15 -0
  96. package/src/state/hooks.js +164 -0
  97. package/src/state/infer_type.js +74 -0
  98. package/src/state/merge_bounds.js +64 -0
  99. package/src/state/multigraph_state_controller.js +334 -0
  100. package/src/state/selection_from_global_bounds.js +25 -0
  101. package/src/state/space_conversions/condense_data_space.js +115 -0
  102. package/src/state/space_conversions/data_space_to_selected_space.js +328 -0
  103. package/src/state/space_conversions/selected_space_to_background_space.js +144 -0
  104. package/src/state/space_conversions/selected_space_to_render_space.js +161 -0
  105. package/src/state/space_conversions/simple_series_to_data_space.js +229 -0
  106. package/src/state/state_controller.js +1770 -0
  107. package/src/state/sync_pool.js +101 -0
  108. package/test/setup.js +15 -0
  109. package/test/space_conversions/data_space_to_selected_space.test.js +434 -0
  110. package/webpack.dev.config.js +109 -0
  111. package/webpack.prod.config.js +60 -0
  112. package/webpack.test.config.js +59 -0
@@ -0,0 +1,328 @@
1
+ import binarySearch from '../../helpers/binary_search';
2
+
3
+ function validateSelectedSpaceConversion({ data, inSelectedSpace, firstIndex, lastIndex, ignoreDiscontinuities }) { // eslint-disable-line no-unused-vars
4
+ let correct = data.slice(firstIndex + 1, lastIndex);
5
+ if (ignoreDiscontinuities) {
6
+ correct = correct.filter(([_x, y]) => (y !== null && y !== undefined));
7
+ }
8
+
9
+ if (correct.length !== inSelectedSpace.length) {
10
+ console.log({ // eslint-disable-line no-console
11
+ data,
12
+ attempt: inSelectedSpace.map(([x, y]) => [undateify(x), y]),
13
+ correct: correct.map(([x, y]) => [undateify(x), y]),
14
+ sdl: window.sdl
15
+ });
16
+ window.tacomaDataPaused = true;
17
+ throw new Error('Failed to select via swap');
18
+ }
19
+
20
+ for (let i = 0; i < correct.length; i++) {
21
+ if (undateify(correct[i][0]) !== undateify(inSelectedSpace[i][0]) || correct[i][1] !== inSelectedSpace[i][1]) {
22
+ console.log({ // eslint-disable-line no-console
23
+ i,
24
+ attempt: inSelectedSpace.map(([x, y]) => [undateify(x), y]),
25
+ correct: correct.map(([x, y]) => [undateify(x), y]),
26
+ correctX: undateify(correct[i][0]),
27
+ correctY: correct[i][1],
28
+ attemptX: undateify(inSelectedSpace[i][0]),
29
+ attemptY: inSelectedSpace[i][1]
30
+ });
31
+ window.tacomaDataPaused = true;
32
+ throw new Error('Failed to select via swap');
33
+ }
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Converts from data space to selected space
39
+ *
40
+ * @param {Array<Array<Number>>} data
41
+ * @param {{data: Array<Array<Number>>}|*} [swap]
42
+ * @param {Number} minX
43
+ * @param {Number} maxX
44
+ * @param {Boolean} ignoreDiscontinuities
45
+ * @param {Boolean} square
46
+ * @return {{data: Array<Array<Number>>, lastAdded: boolean, firstAdded: boolean, afterIndex: Number, ignoreDiscontinuities: boolean, minX: Number, maxX: Number, beforeIndex: Number}}
47
+ */
48
+ export default function dataSpaceToSelectedSpace({ data, swap, minX, maxX, ignoreDiscontinuities, square }) {
49
+ if (!data.length || data.length && minX > data[data.length - 1][0] || data.length && maxX < data[0][0]) {
50
+ //let previouslyEmpty = !data.length;
51
+ return {
52
+ data: [
53
+ [minX, null],
54
+ [maxX, null]
55
+ ],
56
+ firstAdded: true,
57
+ lastAdded: true
58
+ //previouslyEmpty
59
+ };
60
+ }
61
+
62
+ let beforeIndex = binarySearch(data, minX, { searchType: 'before', returnIndex: true }) || 0;
63
+ let afterIndex = binarySearch(data, maxX, { searchType: 'after', returnIndex: true }) || 0;
64
+ if (afterIndex === -1) {
65
+ afterIndex = 0;
66
+ }
67
+
68
+ while (beforeIndex >= 0 && data[beforeIndex][0] >= minX) {
69
+ beforeIndex --;
70
+ }
71
+
72
+ while (afterIndex < data.length && data[afterIndex][0] <= maxX) {
73
+ afterIndex ++;
74
+ }
75
+
76
+ let inSelectedSpace;
77
+ let addToEndOnly = false;
78
+
79
+ let firstAdded = false;
80
+ let lastAdded = false;
81
+
82
+ let beginningInterpolationIndex = beforeIndex;
83
+ let endInterpolationIndex = afterIndex;
84
+ if (ignoreDiscontinuities) {
85
+ while (beginningInterpolationIndex >= 0 && data[beginningInterpolationIndex][1] === null) {
86
+ beginningInterpolationIndex --;
87
+ }
88
+ while (endInterpolationIndex < data.length && data[endInterpolationIndex][1] === null) {
89
+ endInterpolationIndex++;
90
+ }
91
+ }
92
+
93
+ if (swap) {
94
+ inSelectedSpace = swap.data;
95
+ if (swap.lastAdded) {
96
+ inSelectedSpace.pop();
97
+ }
98
+ if (minX === swap.minX) {
99
+ addToEndOnly = true;
100
+ }
101
+ if (beforeIndex !== swap.beforeIndex) {
102
+ addToEndOnly = false;
103
+ if (swap.firstAdded) {
104
+ inSelectedSpace.shift();
105
+ }
106
+ let index = swap.beforeIndex;
107
+ while (index >= 0 && inSelectedSpace.length && inSelectedSpace[0][0] >= minX) {
108
+ if (!ignoreDiscontinuities || data[index][1] || data[index][1] === 0) {
109
+ inSelectedSpace.unshift(data[index]);
110
+ }
111
+ index --;
112
+ }
113
+ while (index < data.length && inSelectedSpace.length && inSelectedSpace[0][0] < minX) {
114
+ inSelectedSpace.shift();
115
+ index ++;
116
+ }
117
+ }
118
+
119
+ if (ignoreDiscontinuities && !swap.ignoreDiscontinuities) {
120
+ inSelectedSpace = inSelectedSpace.filter(([_x, y]) => (y !== null && y !== undefined));
121
+ }
122
+
123
+ if (beforeIndex === swap.beforeIndex && swap.firstAdded) {
124
+ firstAdded = true;
125
+
126
+ let interpolationIndex = beforeIndex + 1;
127
+ if (ignoreDiscontinuities) {
128
+ while (interpolationIndex < data.length && data[interpolationIndex][1] === null) {
129
+ interpolationIndex ++;
130
+ }
131
+ }
132
+
133
+ if (beginningInterpolationIndex === -1 && inSelectedSpace.length) {
134
+ inSelectedSpace[0] = [minX, null];
135
+ } else {
136
+ if (square && inSelectedSpace.length) {
137
+ inSelectedSpace[0] = [minX, data[beginningInterpolationIndex][1]];
138
+ } else {
139
+ if (inSelectedSpace.length) {
140
+ inSelectedSpace[0] = [minX, interpolate(data, beginningInterpolationIndex, interpolationIndex, minX)];
141
+ }
142
+ }
143
+ }
144
+ if (data[beforeIndex + 1][0] === inSelectedSpace[0][0] && data[beforeIndex + 1][1] === inSelectedSpace[0][1]) {
145
+ firstAdded = false;
146
+ }
147
+ }
148
+ let lastIncluded = swap.afterIndex;
149
+ if (swap.ignoreDiscontinuities && !ignoreDiscontinuities) {
150
+ let nullIndex = beforeIndex + 1;
151
+ let selectedIndex = 0;
152
+ if (firstAdded) {
153
+ selectedIndex ++;
154
+ }
155
+ while (selectedIndex <= inSelectedSpace.length && nullIndex < data.length && nullIndex < afterIndex) {
156
+ if (data[nullIndex][0] <= maxX && data[nullIndex][1] === null && (!inSelectedSpace[selectedIndex] || inSelectedSpace[selectedIndex][0] !== data[nullIndex][0] || inSelectedSpace[selectedIndex][1] !== data[nullIndex][1])) {
157
+ inSelectedSpace.splice(selectedIndex, 0, data[nullIndex]);
158
+ if (nullIndex >= lastIncluded) {
159
+ lastIncluded = nullIndex + 1;
160
+ }
161
+ }
162
+ nullIndex ++;
163
+ selectedIndex ++;
164
+ }
165
+ }
166
+
167
+ let dataIndex = lastIncluded || 0;
168
+ while (dataIndex < data.length && data[dataIndex][0] <= maxX) {
169
+ if (data[dataIndex][0] >= minX) {
170
+ if (!ignoreDiscontinuities || data[dataIndex][1] || data[dataIndex][1] === 0) {
171
+ inSelectedSpace.push(data[dataIndex]);
172
+ }
173
+ }
174
+ dataIndex ++;
175
+ }
176
+ while (inSelectedSpace.length && inSelectedSpace[inSelectedSpace.length - 1][0] > maxX) {
177
+ inSelectedSpace.pop();
178
+ }
179
+
180
+ } else {
181
+ inSelectedSpace = data.slice(beforeIndex + 1, afterIndex);
182
+ if (ignoreDiscontinuities) {
183
+ inSelectedSpace = inSelectedSpace.filter(([_x, y]) => (y !== null && y !== undefined));
184
+ }
185
+ }
186
+
187
+ if (!inSelectedSpace.length) {
188
+ if (square) {
189
+ if (beginningInterpolationIndex < 0) {
190
+ inSelectedSpace = [[minX, null], [maxX, null]];
191
+ } else {
192
+ inSelectedSpace = [[minX, data[beginningInterpolationIndex][1]], [maxX, data[beginningInterpolationIndex][1]]];
193
+ }
194
+ } else {
195
+ inSelectedSpace = [[minX, interpolate(data, beginningInterpolationIndex, endInterpolationIndex, minX)], [maxX, interpolate(data, beginningInterpolationIndex, endInterpolationIndex, maxX)]];
196
+ }
197
+ firstAdded = true;
198
+ lastAdded = true;
199
+ }
200
+
201
+ if (!addToEndOnly) {
202
+ let interpolationIndex = beforeIndex + 1;
203
+ if (ignoreDiscontinuities) {
204
+ while (interpolationIndex < data.length && data[interpolationIndex][1] === null) {
205
+ interpolationIndex ++;
206
+ }
207
+ }
208
+
209
+ if (inSelectedSpace.length && inSelectedSpace[0][0] > minX) {
210
+ firstAdded = true;
211
+ if (beginningInterpolationIndex === -1) {
212
+ inSelectedSpace.unshift([minX, null]);
213
+ } else {
214
+ if (square) {
215
+ inSelectedSpace.unshift([minX, data[beginningInterpolationIndex][1]]);
216
+ } else {
217
+ inSelectedSpace.unshift([minX, interpolate(data, beginningInterpolationIndex, interpolationIndex, minX)]);
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ if (inSelectedSpace.length && inSelectedSpace[inSelectedSpace.length - 1][0] < maxX) {
224
+ lastAdded = true;
225
+
226
+ let interpolationIndex = afterIndex - 1;
227
+ if (ignoreDiscontinuities) {
228
+ while (interpolationIndex >= 0 && data[interpolationIndex][1] === null) {
229
+ interpolationIndex --;
230
+ }
231
+ }
232
+
233
+ if (endInterpolationIndex === data.length) {
234
+ inSelectedSpace.push([maxX, null]);
235
+ } else {
236
+ if (square) {
237
+ inSelectedSpace.push([maxX, inSelectedSpace[inSelectedSpace.length - 1][1]]);
238
+ } else {
239
+ inSelectedSpace.push([maxX, interpolate(data, interpolationIndex, endInterpolationIndex, maxX)]);
240
+ }
241
+ }
242
+ }
243
+
244
+ if (inSelectedSpace.length === 1) {
245
+ let begPoint;
246
+ let endPoint;
247
+ if (square) {
248
+ if (beginningInterpolationIndex < 0) {
249
+ begPoint = [minX, null];
250
+ } else {
251
+ begPoint = [minX, data[beginningInterpolationIndex][1]];
252
+ }
253
+ endPoint = [maxX, inSelectedSpace[0][1]];
254
+ } else {
255
+ begPoint = [minX, interpolate(data, beginningInterpolationIndex, beforeIndex + 1, minX)];
256
+ endPoint = [maxX, interpolate(data, afterIndex - 1, endInterpolationIndex, maxX)];
257
+ }
258
+ inSelectedSpace.unshift(begPoint);
259
+ inSelectedSpace.push(endPoint);
260
+ firstAdded = true;
261
+ lastAdded = true;
262
+ }
263
+
264
+ return {
265
+ data: inSelectedSpace,
266
+ minX,
267
+ maxX,
268
+ beforeIndex,
269
+ afterIndex,
270
+ firstAdded,
271
+ lastAdded,
272
+ ignoreDiscontinuities
273
+ //previouslyEmpty: false
274
+ };
275
+ }
276
+
277
+ function undateify(potentialDate) {
278
+ if (potentialDate instanceof Date) {
279
+ return potentialDate.valueOf();
280
+ }
281
+
282
+ return potentialDate;
283
+ }
284
+
285
+ /**
286
+ * Finds the point at the boundary via interpolation
287
+ *
288
+ * @param {Array<Array<Number>>} data
289
+ * @param {Number} firstIndex
290
+ * @param {Number} secondIndex
291
+ * @param {Number} boundary
292
+ * @return {null|*}
293
+ */
294
+ function interpolate(data, firstIndex, secondIndex, boundary) {
295
+ if (firstIndex < 0 || secondIndex < 0) {
296
+ return null;
297
+ }
298
+
299
+ if (firstIndex >= data.length || secondIndex >= data.length) {
300
+ return null;
301
+ }
302
+
303
+ if (firstIndex === secondIndex) {
304
+ return data[firstIndex][1];
305
+ }
306
+
307
+ const [xBefore, yBefore] = data[firstIndex];
308
+ const [xAfter, yAfter] = data[secondIndex];
309
+
310
+ if (boundary === xBefore && yBefore !== null) {
311
+ return yBefore;
312
+ }
313
+
314
+ if (boundary === xAfter && yAfter !== null) {
315
+ return yAfter;
316
+ }
317
+
318
+ if (yBefore === null || yAfter === null) {
319
+ return null;
320
+ }
321
+
322
+ const percent = (boundary - xBefore)/(xAfter - xBefore);
323
+ if (percent < 0 || percent > 1) {
324
+ return null;
325
+ }
326
+
327
+ return percent*(yAfter - yBefore) + yBefore;
328
+ }
@@ -0,0 +1,144 @@
1
+ export function selectedSpaceToBackgroundSpace({ data, background, minX, maxX }) {
2
+ if (!background) {
3
+ return null;
4
+ }
5
+
6
+ const conditions = [];
7
+ for (let [key, color] of Object.entries(background)) {
8
+ if (typeof color === 'object') {
9
+ if (typeof color.evaluator !== 'function') {
10
+ throw new Error('Invalid background declaration: ' + key + ' (evaluator must be a function)');
11
+ }
12
+
13
+ conditions.push(Object.assign({
14
+ key,
15
+ comparator: 'custom',
16
+ comparedAgainst: null
17
+ }, color));
18
+ continue;
19
+ }
20
+
21
+ if (key === 'null') {
22
+ conditions.push({
23
+ evaluator: (y) => y === null && color,
24
+ color,
25
+ key,
26
+ comparator: '=',
27
+ comparedAgainst: null
28
+ });
29
+ continue;
30
+ }
31
+
32
+ const [comparator, value] = key.split(' ');
33
+ if (!comparator || !value || isNaN(parseFloat(value))) {
34
+ throw new Error('Invalid background declaration: ' + key);
35
+ }
36
+
37
+ const parsedValue = parseFloat(value);
38
+
39
+ let evaluator;
40
+ if (comparator === '=') {
41
+ evaluator = (y) => typeof y === 'number' && y === parsedValue && color;
42
+ } else if (comparator === '<') {
43
+ evaluator = (y) => typeof y === 'number' && y < parsedValue && color;
44
+ } else if (comparator === '>') {
45
+ evaluator = (y) => typeof y === 'number' && y > parsedValue && color;
46
+ } else if (comparator === '<=') {
47
+ evaluator = (y) => typeof y === 'number' && y <= parsedValue && color;
48
+ } else if (comparator === '>=') {
49
+ evaluator = (y) => typeof y === 'number' && y >= parsedValue && color;
50
+ } else {
51
+ throw new Error('Invalid background declaration: ' + key);
52
+ }
53
+
54
+ conditions.push({
55
+ evaluator,
56
+ color,
57
+ key,
58
+ comparator,
59
+ comparedAgainst: parsedValue
60
+ });
61
+ }
62
+
63
+ const inBackgroundSpace = [];
64
+ let currentSection = null;
65
+
66
+ for (let i = 0; i < data.length; i++) {
67
+ let [x, y] = data[i];
68
+ if (x instanceof Date) {
69
+ x = x.valueOf();
70
+ }
71
+
72
+ for (let condition of currentSection ? [currentSection.condition, ...conditions] : conditions) {
73
+ const color = condition.evaluator(y);
74
+
75
+ if (currentSection) {
76
+ if (currentSection.color === color) {
77
+ break;
78
+ }
79
+
80
+ let interpolatedMaxX = x;
81
+ if (i > 0) {
82
+ let [prevX, prevY] = data[i - 1];
83
+ if (prevX instanceof Date) {
84
+ prevX = prevX.valueOf();
85
+ }
86
+
87
+ if (currentSection.condition.comparedAgainst === null) {
88
+ interpolatedMaxX = x;
89
+ } else if (y === null) {
90
+ interpolatedMaxX = prevX;
91
+ } else {
92
+ interpolatedMaxX = prevX + (condition.comparedAgainst - prevY)/(y - prevY)*(x - prevX);
93
+ }
94
+ }
95
+
96
+ inBackgroundSpace.push({
97
+ ...currentSection,
98
+ maxX: interpolatedMaxX,
99
+ maxXt: (interpolatedMaxX - minX)/(maxX - minX)
100
+ });
101
+ currentSection = null;
102
+ }
103
+
104
+ if (color) {
105
+ let interpolatedMinX = x;
106
+ if (i > 0) {
107
+ let [prevX, prevY] = data[i - 1];
108
+ if (prevX instanceof Date) {
109
+ prevX = prevX.valueOf();
110
+ }
111
+
112
+ if (condition.comparedAgainst === null) {
113
+ interpolatedMinX = prevX;
114
+ } else if (prevY === null) {
115
+ interpolatedMinX = x;
116
+ } else {
117
+ interpolatedMinX = prevX + (condition.comparedAgainst - prevY)/(y - prevY)*(x - prevX);
118
+ }
119
+ }
120
+
121
+ currentSection = {
122
+ minX: interpolatedMinX,
123
+ minXt: (interpolatedMinX - minX)/(maxX - minX),
124
+ color,
125
+ condition
126
+ };
127
+
128
+ break;
129
+ }
130
+ }
131
+ }
132
+
133
+ if (currentSection) {
134
+ inBackgroundSpace.push({
135
+ ...currentSection,
136
+ maxX: data[data.length - 1][0],
137
+ maxXt: (data[data.length - 1][0] - minX)/(maxX - minX)
138
+ });
139
+ }
140
+
141
+ return {
142
+ data: inBackgroundSpace
143
+ };
144
+ }
@@ -0,0 +1,161 @@
1
+ import scaleBounds from '../../renderer/scale_bounds';
2
+ let RustAPI;
3
+ import('../../rust/pkg/index.js').then((module) => {
4
+ RustAPI = module;
5
+ });
6
+
7
+ function selectedSpaceToRenderSpaceInPlace({ data, renderWidth, renderHeight, minX, maxX, minY, maxY, scale }, { nullMask, yValues, minYValues, maxYValues }) {
8
+ let i = 0;
9
+ let prevI = i - 1;
10
+
11
+ for (let pixelX = 0; pixelX < renderWidth; pixelX++) {
12
+ // find the x value that corresponds to the x pixel
13
+ const x = (pixelX/(renderWidth - 1))*(maxX - minX) + minX;
14
+
15
+ // set i such that data[i][0] < x <= data[i+1][0]
16
+ let minSeenY = null;
17
+ let maxSeenY = null;
18
+
19
+ if (i > 0 && i <= data.length && data[i - 1][1] === null) {
20
+ i--;
21
+ }
22
+
23
+ if (i < data.length - 2 && data[i + 1][0] < x) {
24
+ i++;
25
+ }
26
+
27
+ for (i; i < data.length - 2 && data[i + 1][0] < x; i++) {
28
+ const curY = data[i][1];
29
+
30
+ if (curY === null) {
31
+ continue;
32
+ }
33
+
34
+ if (minSeenY === null || curY < minSeenY) {
35
+ minSeenY = curY;
36
+ }
37
+
38
+ if (maxSeenY === null || curY > maxSeenY) {
39
+ maxSeenY = curY;
40
+ }
41
+ }
42
+
43
+ minYValues[pixelX] = minSeenY === null ? 0 : renderHeight*(1 - ((scale === 'log' ? Math.log10(minSeenY) : minSeenY) - minY)/(maxY - minY));
44
+ maxYValues[pixelX] = maxSeenY === null ? 0 : renderHeight*(1 - ((scale === 'log' ? Math.log10(maxSeenY) : maxSeenY) - minY)/(maxY - minY));
45
+
46
+ // pass any discontinuities along
47
+ if (i >= data.length - 1 || data[i][1] === null || data[i + 1][1] === null) {
48
+ const y = i >= data.length - 1 ? null : data[i][1];
49
+
50
+ nullMask[pixelX] = ((y === null) << 0) | ((minSeenY === null) << 1) | ((maxSeenY === null) << 2);
51
+ yValues[pixelX] = y === null ? 0 : renderHeight*(1 - ((scale === 'log' ? Math.log10(y) : y) - minY)/(maxY - minY));
52
+
53
+ i++;
54
+
55
+ continue;
56
+ }
57
+
58
+ // interpolate
59
+ const [xBefore, yBefore] = data[i];
60
+ const [xAfter, yAfter] = data[i + 1];
61
+
62
+ const percent = (x - xBefore) / (xAfter - xBefore);
63
+ let y = percent * (yAfter - yBefore) + yBefore;
64
+
65
+ // we're at the first point after the direction changed. Don't interpolate
66
+ if (prevI !== i) {
67
+ y = yBefore;
68
+ }
69
+
70
+ yValues[pixelX] = y === null ? 0 : renderHeight*(1 - ((scale === 'log' ? Math.log10(y) : y) - minY)/(maxY - minY));
71
+ nullMask[pixelX] = ((y === null) << 0) | ((minSeenY === null) << 1) | ((maxSeenY === null) << 2);
72
+
73
+ prevI = i;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Convert from selected space to value space
79
+ *
80
+ * @param data
81
+ * @param {Object} [swap]
82
+ * @param {Number} renderWidth - width, in pixels, of the area in which the graph is rendered
83
+ * @param {Number} renderHeight - height, in pixels, of the area in which the graph is rendered
84
+ * @param {Number} minX - the minimum x value that is rendered
85
+ * @param {Number} maxX - the maximum x value that is rendered
86
+ * @param {Number} minY - the minimum y value that is rendered
87
+ * @param {Number} maxY - the maximum y value that is rendered
88
+ * @param {'log'|'linear'} scale
89
+ * @param {Boolean} [dataChanged] - if true, will not rely on the prior data state from swap being accurate
90
+ * @return {{nullMask: Uint8Array, maxYValues: Float64Array, minYValues: Float64Array, yValues: Float64Array, dataF64: Float64Array, dataNullMask: Uint8Array}}
91
+ */
92
+ export default function selectedSpaceToRenderSpace({ data, swap, renderWidth, renderHeight, minX, maxX, minY, maxY, scale, dataChanged }) {
93
+ if (swap && swap.yValues.length !== renderWidth) {
94
+ swap = null;
95
+ }
96
+
97
+ const nullMask = (swap && swap.nullMask) || new Uint8Array(renderWidth);
98
+ nullMask.fill(0);
99
+ const yValues = new Float64Array(renderWidth);
100
+ const minYValues = new Float64Array(renderWidth);
101
+ const maxYValues = new Float64Array(renderWidth);
102
+
103
+ const scaledBounds = scaleBounds({ minY, maxY, scale});
104
+ minY = scaledBounds.minY;
105
+ maxY = scaledBounds.maxY;
106
+
107
+ const inParams = { data, renderWidth, renderHeight, minX, maxX, minY, maxY, scale };
108
+
109
+ let dataF64, dataNullMask;
110
+
111
+ if (RustAPI) {
112
+ let copyIndexStart = 0;
113
+
114
+ const hasSwap = swap && swap.dataNullMask && swap.dataF64;
115
+ const useSwap = !dataChanged && hasSwap && swap.minX === minX && swap.maxX <= maxX && swap.length <= data.length;
116
+
117
+ if (!useSwap || swap.dataNullMask.length < data.length) {
118
+ const extraSpaceFactor = 1.25;
119
+ dataF64 = new Float64Array(Math.floor(data.length*2*extraSpaceFactor));
120
+ dataNullMask = new Uint8Array(Math.floor(data.length*extraSpaceFactor));
121
+
122
+ if (useSwap) {
123
+ dataNullMask.set(swap.dataNullMask);
124
+ dataF64.set(swap.dataF64);
125
+ }
126
+ } else {
127
+ dataF64 = swap.dataF64;
128
+ dataNullMask = swap.dataNullMask;
129
+ }
130
+
131
+ if (useSwap) {
132
+ copyIndexStart = Math.max(swap.length - 1, 0);
133
+ }
134
+
135
+ for (let i = copyIndexStart; i < data.length; i++) {
136
+ dataF64[2*i] = data[i][0];
137
+ dataF64[2*i + 1] = data[i][1];
138
+
139
+ if (data[i][1] === null) {
140
+ dataNullMask[i] = 1;
141
+ } else {
142
+ dataNullMask[i] = 0;
143
+ }
144
+ }
145
+ RustAPI.selected_space_to_render_space(data.length, dataF64, dataNullMask, inParams, nullMask, yValues, minYValues, maxYValues);
146
+ } else {
147
+ selectedSpaceToRenderSpaceInPlace(inParams, { nullMask, yValues, minYValues, maxYValues });
148
+ }
149
+
150
+ return {
151
+ nullMask,
152
+ yValues,
153
+ minYValues,
154
+ maxYValues,
155
+ dataF64,
156
+ dataNullMask,
157
+ minX,
158
+ maxX,
159
+ length: data.length
160
+ };
161
+ }