@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,101 @@
1
+ export default class SyncPool {
2
+
3
+ /**
4
+ *
5
+ * @param {Boolean} syncBounds
6
+ * @param {Boolean|'onShift'} syncTooltips
7
+ * @param {Boolean} syncDragState
8
+ */
9
+ constructor({ syncBounds=true, syncTooltips=true, syncDragState=false } = {}) {
10
+ this._stateControllers = new Set();
11
+ this._syncBounds = syncBounds;
12
+ this._syncTooltips = syncTooltips;
13
+ this._syncDragState = syncDragState;
14
+ }
15
+
16
+ add(stateController) {
17
+ this._stateControllers.add(stateController);
18
+
19
+ if (this._syncBounds) {
20
+ this.syncBounds(stateController);
21
+ }
22
+
23
+ if (this._syncTooltips) {
24
+ this.syncTooltips(stateController);
25
+ }
26
+
27
+ if (this._syncDragState) {
28
+ this.syncDragState(stateController);
29
+ }
30
+ }
31
+
32
+ remove(stateController) {
33
+ this._stateControllers.delete(stateController);
34
+
35
+ // TODO: remove listeners
36
+ // (though since this is currently only called by stateController dispose, it probably doesn't matter)
37
+ }
38
+
39
+ syncDragState(stateController) {
40
+ stateController.on('dragging_y_changed', (draggingY) => {
41
+ for (let otherStateController of this._stateControllers) {
42
+ if (stateController === otherStateController) {
43
+ continue;
44
+ }
45
+
46
+ if (draggingY) {
47
+ otherStateController.markDragStart();
48
+ } else {
49
+ otherStateController.finalizeDrag(null, null);
50
+ }
51
+ }
52
+ });
53
+ }
54
+
55
+ syncBounds(stateController) {
56
+ stateController.on('bound_calculator_changed', (boundCalculator) => {
57
+ for (let otherStateController of this._stateControllers) {
58
+ if (stateController === otherStateController) {
59
+ continue;
60
+ }
61
+
62
+ otherStateController.boundCalculator = boundCalculator;
63
+ }
64
+ });
65
+ }
66
+
67
+ syncTooltips(stateController) {
68
+ stateController.on('tooltip_state_changed', (tooltipState, stateArg) => {
69
+ if (this._tooltipIgnoreState === stateArg) { // avoid cascading changes
70
+ return;
71
+ }
72
+
73
+ const shouldHideTooltips = tooltipState.unsavedTooltipsCount === 0 ||
74
+ !tooltipState.mousePresent ||
75
+ (this._syncTooltips === 'onShift' && !stateController.shiftKeyPressedOnMove);
76
+
77
+ if (shouldHideTooltips) {
78
+ this._tooltipIgnoreState = Math.random();
79
+ }
80
+
81
+ for (let otherStateController of this._stateControllers) {
82
+ if (stateController === otherStateController) {
83
+ continue;
84
+ }
85
+
86
+ if (shouldHideTooltips) {
87
+ otherStateController.showOnlySavedTooltips(this._tooltipIgnoreState);
88
+ continue;
89
+ }
90
+
91
+ otherStateController.setTooltipMousePosition({
92
+ mouseX: tooltipState.mouseX,
93
+ mouseY: tooltipState.mouseY,
94
+ tooltipAllNext: true,
95
+ tooltipStateArg: this._tooltipIgnoreState
96
+ });
97
+ }
98
+ });
99
+ }
100
+
101
+ }
package/test/setup.js ADDED
@@ -0,0 +1,15 @@
1
+ const JSDOMGlobal = require('jsdom-global');
2
+ const chai = require('chai');
3
+ const sinonChai = require('sinon-chai');
4
+
5
+ JSDOMGlobal('<!doctype html><html><body></body></html>', {
6
+ url: 'http://localhost'
7
+ });
8
+
9
+ global.navigator = {
10
+ userAgent: 'node.js'
11
+ };
12
+
13
+ global.expect = require('chai').expect;
14
+
15
+ chai.use(sinonChai);
@@ -0,0 +1,434 @@
1
+ import dataSpaceToSelectedSpace from '../../src/state/space_conversions/data_space_to_selected_space';
2
+
3
+ describe('#dataSpaceToSelectedSpace', () => {
4
+ it('handles basic interpolation', () => {
5
+ const result = dataSpaceToSelectedSpace({
6
+ data: [[0, 0], [1, 1], [2, 2]],
7
+ minX: 0.5,
8
+ maxX: 1.5,
9
+ ignoreDiscontinuities: true,
10
+ square: false
11
+ });
12
+ expect(result.data).to.eql([[0.5, 0.5], [1, 1], [1.5, 1.5]]);
13
+
14
+ const result2 = dataSpaceToSelectedSpace({
15
+ data: [[0, 1], [1, 1], [2, 1], [3, 3]],
16
+ minX: 0.5,
17
+ maxX: 1.5,
18
+ ignoreDiscontinuities: true,
19
+ square: false,
20
+ swap: result
21
+ });
22
+ expect(result2.data).to.eql([[0.5, 1], [1, 1], [1.5, 1]]);
23
+ });
24
+
25
+ it('handles basic selection', () => {
26
+ const result = dataSpaceToSelectedSpace({
27
+ data: [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4]],
28
+ minX: 1,
29
+ maxX: 3,
30
+ ignoreDiscontinuities: true,
31
+ square: false
32
+ });
33
+ expect(result.data).to.eql([[1, 1], [2, 2], [3, 3]]);
34
+
35
+ const result2 = dataSpaceToSelectedSpace({
36
+ data: [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5]],
37
+ minX: 1,
38
+ maxX: 3,
39
+ ignoreDiscontinuities: true,
40
+ square: false,
41
+ swap: result
42
+ });
43
+ expect(result2.data).to.eql([[1, 1], [2, 2], [3, 3]]);
44
+
45
+ const result3 = dataSpaceToSelectedSpace({
46
+ data: [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5]],
47
+ minX: 0,
48
+ maxX: 5,
49
+ ignoreDiscontinuities: true,
50
+ square: false,
51
+ swap: result2
52
+ });
53
+ expect(result3.data).to.eql([[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]);
54
+ });
55
+
56
+ it('handles too-wide selection', () => {
57
+ const result = dataSpaceToSelectedSpace({
58
+ data: [[1, 1], [2, 2], [3, 3]],
59
+ minX: -10,
60
+ maxX: 30,
61
+ ignoreDiscontinuities: true,
62
+ square: false
63
+ });
64
+ expect(result.data).to.eql([[-10, null], [1, 1], [2, 2], [3, 3], [30, null]]);
65
+
66
+ const result2 = dataSpaceToSelectedSpace({
67
+ data: [[1, 1], [2, 2], [3, 3]],
68
+ minX: -10,
69
+ maxX: 30,
70
+ ignoreDiscontinuities: true,
71
+ square: false,
72
+ swap: result
73
+ });
74
+ expect(result2.data).to.eql([[-10, null], [1, 1], [2, 2], [3, 3], [30, null]]);
75
+ });
76
+
77
+ it('handles duplicate values at beginning and end', () => {
78
+ for (let square of [true, false]) {
79
+ const result = dataSpaceToSelectedSpace({
80
+ data: [[1, 1], [1, 1], [2, 1], [2, 2], [3, 2], [3, 3]],
81
+ minX: 1,
82
+ maxX: 3,
83
+ ignoreDiscontinuities: true,
84
+ square
85
+ });
86
+ expect(result.data).to.eql([[1, 1], [1, 1], [2, 1], [2, 2], [3, 2], [3, 3]]);
87
+
88
+ const result2 = dataSpaceToSelectedSpace({
89
+ data: [[1, 1], [1, 1], [2, 1], [2, 2], [3, 2], [3, 3]],
90
+ minX: 1,
91
+ maxX: 3,
92
+ ignoreDiscontinuities: true,
93
+ square,
94
+ swap: result
95
+ });
96
+ expect(result2.data).to.eql([[1, 1], [1, 1], [2, 1], [2, 2], [3, 2], [3, 3]]);
97
+ }
98
+ });
99
+
100
+ it('handles duplicate values at beginning and end with too wide bounds', () => {
101
+ for (let square of [true, false]) {
102
+ const result = dataSpaceToSelectedSpace({
103
+ data: [[1, 1], [1, 1], [2, 1], [2, 2], [3, 2], [3, 3]],
104
+ minX: 0,
105
+ maxX: 4,
106
+ ignoreDiscontinuities: true,
107
+ square
108
+ });
109
+ expect(result.data).to.eql([[0, null], [1, 1], [1, 1], [2, 1], [2, 2], [3, 2], [3, 3], [4, null]]);
110
+
111
+ const result2 = dataSpaceToSelectedSpace({
112
+ data: [[1, 1], [1, 1], [2, 1], [2, 2], [3, 2], [3, 3]],
113
+ minX: 0,
114
+ maxX: 4,
115
+ ignoreDiscontinuities: true,
116
+ square,
117
+ swap: result
118
+ });
119
+ expect(result2.data).to.eql([[0, null], [1, 1], [1, 1], [2, 1], [2, 2], [3, 2], [3, 3], [4, null]]);
120
+ }
121
+ });
122
+
123
+ it('handles square interpolation', () => {
124
+ const result = dataSpaceToSelectedSpace({
125
+ data: [[1, 1], [2, 1], [2, 2], [3, 2], [3, 3]],
126
+ minX: 1,
127
+ maxX: 2.5,
128
+ ignoreDiscontinuities: true,
129
+ square: true
130
+ });
131
+ expect(result.data).to.eql([[1, 1], [2, 1], [2, 2], [2.5, 2]]);
132
+
133
+ const result2 = dataSpaceToSelectedSpace({
134
+ data: [[1, 1], [2, 1], [2, 2], [3, 2], [3, 3]],
135
+ minX: 1,
136
+ maxX: 2.5,
137
+ ignoreDiscontinuities: true,
138
+ square: true,
139
+ swap: result
140
+ });
141
+ expect(result2.data).to.eql([[1, 1], [2, 1], [2, 2], [2.5, 2]]);
142
+ });
143
+
144
+ it('ignores discontinuities', () => {
145
+ const result = dataSpaceToSelectedSpace({
146
+ data: [[1, 1], [2, null], [3, 3]],
147
+ minX: -10,
148
+ maxX: 30,
149
+ ignoreDiscontinuities: true,
150
+ square: false
151
+ });
152
+ expect(result.data).to.eql([[-10, null], [1, 1], [3, 3], [30, null]]);
153
+
154
+ const result2 = dataSpaceToSelectedSpace({
155
+ data: [[1, 1], [2, null], [3, 3]],
156
+ minX: -10,
157
+ maxX: 30,
158
+ ignoreDiscontinuities: true,
159
+ square: false,
160
+ swap: result
161
+ });
162
+ expect(result2.data).to.eql([[-10, null], [1, 1], [3, 3], [30, null]]);
163
+ });
164
+
165
+ it('handles discontinuities at boundaries', () => {
166
+ const result = dataSpaceToSelectedSpace({
167
+ data: [[0, 0], [1, null], [2, 2], [3, null]],
168
+ minX: 1,
169
+ maxX: 3,
170
+ ignoreDiscontinuities: true,
171
+ square: false
172
+ });
173
+ expect(result.data).to.eql([[1, 1], [2, 2], [3, null]]);
174
+
175
+ const result2 = dataSpaceToSelectedSpace({
176
+ data: [[1, null], [2, 2], [3, null], [4, 4]],
177
+ minX: 1,
178
+ maxX: 3,
179
+ ignoreDiscontinuities: true,
180
+ square: false,
181
+ swap: result
182
+ });
183
+ expect(result2.data).to.eql([[1, null], [2, 2], [3, 3]]);
184
+ });
185
+
186
+ it('handles moving selection back', () => {
187
+ const result = dataSpaceToSelectedSpace({
188
+ data: [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5]],
189
+ minX: 2.5,
190
+ maxX: 5,
191
+ ignoreDiscontinuities: true,
192
+ square: false
193
+ });
194
+ expect(result.data).to.eql([[2.5, 2.5], [3, 3], [4, 4], [5, 5]]);
195
+
196
+ const result2 = dataSpaceToSelectedSpace({
197
+ data: [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5]],
198
+ minX: 1,
199
+ maxX: 3,
200
+ ignoreDiscontinuities: true,
201
+ square: false,
202
+ swap: result
203
+ });
204
+ expect(result2.data).to.eql([[1, 1], [2, 2], [3, 3]]);
205
+
206
+ const result3 = dataSpaceToSelectedSpace({
207
+ data: [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5]],
208
+ minX: 0.5,
209
+ maxX: 5,
210
+ ignoreDiscontinuities: true,
211
+ square: false,
212
+ swap: result2
213
+ });
214
+ expect(result3.data).to.eql([[0.5, 0.5], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]);
215
+ });
216
+
217
+ it('handles no data between bounds', () => {
218
+ const result = dataSpaceToSelectedSpace({
219
+ data: [[0, 0], [3, 3]],
220
+ minX: 1,
221
+ maxX: 2,
222
+ ignoreDiscontinuities: true,
223
+ square: false
224
+ });
225
+ expect(result.data).to.eql([[1, 1], [2, 2]]);
226
+
227
+ const result2 = dataSpaceToSelectedSpace({
228
+ data: [[0, 0], [3, 3]],
229
+ minX: 0.5,
230
+ maxX: 3,
231
+ ignoreDiscontinuities: true,
232
+ square: false,
233
+ swap: result
234
+ });
235
+ expect(result2.data).to.eql([[0.5, 0.5], [3, 3]]);
236
+ });
237
+
238
+ it('handles no data between bounds when square', () => {
239
+ const result = dataSpaceToSelectedSpace({
240
+ data: [[0, 0], [2, 2]],
241
+ minX: 1,
242
+ maxX: 1.5,
243
+ ignoreDiscontinuities: true,
244
+ square: true
245
+ });
246
+ expect(result.data).to.eql([[1, 0], [1.5, 0]]);
247
+
248
+ const result2 = dataSpaceToSelectedSpace({
249
+ data: [[0, 0], [2, 2]],
250
+ minX: 1,
251
+ maxX: 2,
252
+ ignoreDiscontinuities: true,
253
+ square: true,
254
+ swap: result
255
+ });
256
+ expect(result2.data).to.eql([[1, 0], [2, 2]]);
257
+ });
258
+
259
+ it('handles one point in data', () => {
260
+ const result = dataSpaceToSelectedSpace({
261
+ data: [[0, 0], [1, 1], [2, 2]],
262
+ minX: 1,
263
+ maxX: 1,
264
+ ignoreDiscontinuities: true,
265
+ square: true
266
+ });
267
+ expect(result.data).to.eql([[1, 0], [1, 1], [1, 1]]);
268
+ });
269
+
270
+ it('handles interpolation past nulls', () => {
271
+ const result = dataSpaceToSelectedSpace( {
272
+ data: [[1, 1], [2, null], [3, null], [4, 4], [5, 5], [6, null], [7, 7]],
273
+ minX: 3.5,
274
+ maxX: 5.5,
275
+ ignoreDiscontinuities: true,
276
+ square: false
277
+ });
278
+ expect(result.data).to.eql([[3.5, 3.5], [4, 4], [5, 5], [5.5, 5.5]]);
279
+
280
+ const result2 = dataSpaceToSelectedSpace( {
281
+ data: [[1, 1], [2, null], [3, null], [4, 4], [5, 5], [6, null], [7, 7]],
282
+ minX: 1,
283
+ maxX: 7,
284
+ ignoreDiscontinuities: true,
285
+ square: false,
286
+ swap: result
287
+ });
288
+ expect(result2.data).to.eql([[1, 1], [4, 4], [5, 5], [7, 7]]);
289
+
290
+ const result3 = dataSpaceToSelectedSpace( {
291
+ data: [[1, 1], [2, null], [3, null], [4, 4], [5, 5], [6, null], [7, 7]],
292
+ minX: 2,
293
+ maxX: 7,
294
+ ignoreDiscontinuities: false,
295
+ square: true,
296
+ swap: result2
297
+ });
298
+ expect(result3.data).to.eql([[2, null], [3, null], [4, 4], [5, 5], [6, null], [7, 7]]);
299
+
300
+ const result4 = dataSpaceToSelectedSpace( {
301
+ data: [[1, 1], [2, null], [3, null], [4, 4], [5, 5], [6, null], [7, 7]],
302
+ minX: 3,
303
+ maxX: 5.5,
304
+ ignoreDiscontinuities: false,
305
+ square: true,
306
+ swap: result3
307
+ });
308
+ expect(result4.data).to.eql([[3, null], [4, 4], [5, 5], [5.5, 5]]);
309
+
310
+ const result5 = dataSpaceToSelectedSpace( {
311
+ data: [[1, 1], [2, null], [3, null], [4, 4], [5, 5], [6, null], [7, 7]],
312
+ minX: 3,
313
+ maxX: 5.5,
314
+ ignoreDiscontinuities: false,
315
+ square: false,
316
+ swap: result4
317
+ });
318
+ expect(result5.data).to.eql([[3, null], [4, 4], [5, 5], [5.5, null]]);
319
+ });
320
+
321
+ it('handles changing ignoreDiscontinuities', () => {
322
+ const result = dataSpaceToSelectedSpace( {
323
+ data: [[0, 0], [1, null], [2, null], [3, 3], [4, null], [5, 5], [6, 6]],
324
+ minX: 1,
325
+ maxX: 5.5,
326
+ ignoreDiscontinuities: true,
327
+ square: false
328
+ });
329
+ expect(result.data).to.eql([[1, 1], [3, 3], [5, 5], [5.5, 5.5]]);
330
+
331
+ const result2 = dataSpaceToSelectedSpace( {
332
+ data: [[0, 0], [1, null], [2, null], [3, 3], [4, null], [5, 5], [6, 6]],
333
+ minX: 1,
334
+ maxX: 5.5,
335
+ ignoreDiscontinuities: false,
336
+ square: false,
337
+ swap: result
338
+ });
339
+ expect(result2.data).to.eql([[1, null], [2, null], [3, 3], [4, null], [5, 5], [5.5, 5.5]]);
340
+
341
+ const result3 = dataSpaceToSelectedSpace( {
342
+ data: [[0, 0], [1, null], [2, null], [3, 3], [4, null], [5, 5], [6, 6]],
343
+ minX: 0,
344
+ maxX: 4,
345
+ ignoreDiscontinuities: true,
346
+ square: true,
347
+ swap: result2
348
+ });
349
+ expect(result3.data).to.eql([[0, 0], [3, 3], [4, 3]]);
350
+ });
351
+
352
+ it('handles all null data when square', () => {
353
+ const result = dataSpaceToSelectedSpace( {
354
+ data: [[0, null], [1, null], [2, null]],
355
+ minX: 0,
356
+ maxX: 2,
357
+ ignoreDiscontinuities: true,
358
+ square: true
359
+ });
360
+ expect(result.data).to.eql([[0, null], [2, null]]);
361
+
362
+ const result2 = dataSpaceToSelectedSpace( {
363
+ data: [[0, null], [1, null], [2, null]],
364
+ minX: 0,
365
+ maxX: 2,
366
+ ignoreDiscontinuities: false,
367
+ square: true
368
+ });
369
+ expect(result2.data).to.eql([[0, null], [1, null], [2, null]]);
370
+ });
371
+
372
+ it('handles no data to some data', () => {
373
+ const result = dataSpaceToSelectedSpace({
374
+ data: [],
375
+ minX: 0.5,
376
+ maxX: 1.5,
377
+ ignoreDiscontinuities: true,
378
+ square: false
379
+ });
380
+ expect(result.data).to.eql([[0.5, null], [1.5, null]]);
381
+
382
+ const result2 = dataSpaceToSelectedSpace({
383
+ data: [[0, 1], [1, 1], [2, 1], [3, 3]],
384
+ minX: 0.5,
385
+ maxX: 1.5,
386
+ ignoreDiscontinuities: true,
387
+ square: false,
388
+ swap: result
389
+ });
390
+ expect(result2.data).to.eql([[0.5, 1], [1, 1], [1.5, 1]]);
391
+ });
392
+
393
+ it('handles null data to some data', () => {
394
+ const result = dataSpaceToSelectedSpace({
395
+ data: [[1, null], [2, null], [3, null]],
396
+ minX: 0.5,
397
+ maxX: 3.5,
398
+ ignoreDiscontinuities: true,
399
+ square: false
400
+ });
401
+ expect(result.data).to.eql([[0.5, null], [3.5, null]]);
402
+
403
+ const result2 = dataSpaceToSelectedSpace({
404
+ data: [[0, 0], [1, null], [2, null], [3, null], [3, 3], [4, 4]],
405
+ minX: 0.5,
406
+ maxX: 3.5,
407
+ ignoreDiscontinuities: false,
408
+ square: false,
409
+ swap: result
410
+ });
411
+ expect(result2.data).to.eql([[0.5, null], [1, null], [2, null], [3, null], [3, 3], [3.5, 3.5]]);
412
+ });
413
+
414
+ it('handles changing ignoreDiscontinuities with firstAdded true', () => {
415
+ const result = dataSpaceToSelectedSpace({
416
+ data: [[1, 1], [2, 2], [2.5, null], [3, 3]],
417
+ minX: 0.5,
418
+ maxX: 3.5,
419
+ ignoreDiscontinuities: true,
420
+ square: false
421
+ });
422
+ expect(result.data).to.eql([[0.5, null], [1, 1], [2, 2], [3, 3], [3.5, null]]);
423
+
424
+ const result2 = dataSpaceToSelectedSpace({
425
+ data: [[1, 1], [2, 2], [2.5, null], [3, 3]],
426
+ minX: 0,
427
+ maxX: 2.5,
428
+ ignoreDiscontinuities: false,
429
+ square: false,
430
+ swap: result
431
+ });
432
+ expect(result2.data).to.eql([[0, null], [1, 1], [2, 2], [2.5, null]]);
433
+ });
434
+ });
@@ -0,0 +1,109 @@
1
+ const path = require('path');
2
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
3
+ const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
4
+
5
+ const pages = require('./examples/page_list')
6
+ .map((pageOrSection) => typeof pageOrSection === 'string' ? pageOrSection : pageOrSection.pages)
7
+ .flat();
8
+
9
+ const entry = {
10
+ 'grapher': './src/grapher.js',
11
+ // 'example_page': './examples/render_page.js',
12
+ };
13
+
14
+ for (let page of pages) {
15
+ entry[page] = `./examples/${page}.js`;
16
+ }
17
+
18
+ module.exports = {
19
+ mode: "development",
20
+ entry,
21
+ devtool: 'eval-source-map',
22
+ output: {
23
+ filename: '[name].bundle.js',
24
+ publicPath: '/',
25
+ path: path.resolve(__dirname, 'examples'),
26
+ libraryTarget: 'umd'
27
+ },
28
+ optimization: {
29
+ minimize: false,
30
+ splitChunks: {
31
+ chunks: "all",
32
+ cacheGroups: {
33
+ rust: {
34
+ priority: 10,
35
+ reuseExistingChunk: false,
36
+ enforce: true,
37
+ test: /rust/
38
+ },
39
+ vendors: {
40
+ test: /[\\/]node_modules[\\/]/,
41
+ priority: -10
42
+ },
43
+ default: {
44
+ minChunks: 2,
45
+ priority: -20,
46
+ reuseExistingChunk: true
47
+ }
48
+ }
49
+ }
50
+ },
51
+ devServer: {
52
+ static: {
53
+ directory: path.resolve(__dirname, 'examples'),
54
+ },
55
+ compress: true,
56
+ port: 9090,
57
+ host: 'localhost',
58
+ open: false,
59
+ allowedHosts: 'all',
60
+ client: {
61
+ overlay: false
62
+ },
63
+ hot: true
64
+ },
65
+ experiments: {
66
+ syncWebAssembly: true
67
+ },
68
+ module: {
69
+ rules: [
70
+ {
71
+ test:/\.s?css$/,
72
+ exclude: /(node_modules|bower_components|build)/,
73
+ use:['style-loader', 'css-loader', 'sass-loader']
74
+ },
75
+ {
76
+ test: /\.js$/,
77
+ exclude: /(node_modules|bower_components|build)/,
78
+ use: {
79
+ loader: 'babel-loader',
80
+ options: {
81
+ presets: ["@babel/preset-env", "@babel/preset-react"]
82
+ }
83
+ }
84
+ },
85
+ {
86
+ test: /\.(vert|frag|glsl)$/,
87
+ use: 'webpack-glsl-loader'
88
+ }
89
+ ]
90
+ },
91
+ resolve: {
92
+ fallback: {
93
+ util: require.resolve("util/")
94
+ }
95
+ },
96
+ plugins: [
97
+ new WasmPackPlugin({
98
+ crateDirectory: path.resolve(__dirname, 'src', 'rust'),
99
+ outDir: path.resolve(__dirname, 'src', 'rust', 'pkg'),
100
+ extraArgs: '--no-typescript',
101
+ forceMode: 'production'
102
+ }),
103
+ ...pages.map((page) => new HtmlWebpackPlugin({
104
+ filename: `examples/${page}.html`,
105
+ template: 'examples/index.html',
106
+ chunks: ['grapher', page]
107
+ }))
108
+ ]
109
+ };