@jrkasprzyk/parcoord-es 3.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 (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -0
  3. package/dist/parcoords.css +61 -0
  4. package/dist/parcoords.esm.js +4132 -0
  5. package/dist/parcoords.esm.js.map +1 -0
  6. package/dist/parcoords.js +4130 -0
  7. package/dist/parcoords.js.map +1 -0
  8. package/dist/parcoords.standalone.js +11233 -0
  9. package/dist/parcoords.standalone.js.map +1 -0
  10. package/package.json +89 -0
  11. package/src/api/adjacentPairs.js +10 -0
  12. package/src/api/applyAxisConfig.js +35 -0
  13. package/src/api/applyDimensionDefaults.js +25 -0
  14. package/src/api/autoscale.js +133 -0
  15. package/src/api/axisDots.js +27 -0
  16. package/src/api/brushMode.js +50 -0
  17. package/src/api/brushReset.js +34 -0
  18. package/src/api/brushable.js +81 -0
  19. package/src/api/clear.js +21 -0
  20. package/src/api/commonScale.js +45 -0
  21. package/src/api/computeRealCentroids.js +8 -0
  22. package/src/api/createAxes.js +101 -0
  23. package/src/api/detectDimensionTypes.js +12 -0
  24. package/src/api/detectDimensions.js +7 -0
  25. package/src/api/flip.js +11 -0
  26. package/src/api/getOrderedDimensionKeys.js +8 -0
  27. package/src/api/highlight.js +26 -0
  28. package/src/api/init.js +53 -0
  29. package/src/api/interactive.js +7 -0
  30. package/src/api/intersection.js +16 -0
  31. package/src/api/mark.js +20 -0
  32. package/src/api/mergeParcoords.js +86 -0
  33. package/src/api/removeAxes.js +9 -0
  34. package/src/api/render.js +26 -0
  35. package/src/api/renderBrushed.js +39 -0
  36. package/src/api/renderDefault.js +27 -0
  37. package/src/api/renderMarked.js +34 -0
  38. package/src/api/reorder.js +37 -0
  39. package/src/api/reorderable.js +41 -0
  40. package/src/api/resize.js +36 -0
  41. package/src/api/scale.js +10 -0
  42. package/src/api/selected.js +209 -0
  43. package/src/api/shadows.js +9 -0
  44. package/src/api/sortDimensions.js +13 -0
  45. package/src/api/sortDimensionsByRowData.js +20 -0
  46. package/src/api/toString.js +11 -0
  47. package/src/api/toType.js +9 -0
  48. package/src/api/toTypeCoerceNumbers.js +7 -0
  49. package/src/api/unhighlight.js +12 -0
  50. package/src/api/unmark.js +12 -0
  51. package/src/api/updateAxes.js +103 -0
  52. package/src/bindEvents.js +60 -0
  53. package/src/brush/1d/brushExtents.js +73 -0
  54. package/src/brush/1d/brushFor.js +92 -0
  55. package/src/brush/1d/brushReset.js +36 -0
  56. package/src/brush/1d/index.js +20 -0
  57. package/src/brush/1d/install.js +41 -0
  58. package/src/brush/1d/selected.js +79 -0
  59. package/src/brush/1d/uninstall.js +12 -0
  60. package/src/brush/1d-multi/brushExtents.js +101 -0
  61. package/src/brush/1d-multi/brushFor.js +13 -0
  62. package/src/brush/1d-multi/brushReset.js +53 -0
  63. package/src/brush/1d-multi/drawBrushes.js +36 -0
  64. package/src/brush/1d-multi/index.js +20 -0
  65. package/src/brush/1d-multi/install.js +29 -0
  66. package/src/brush/1d-multi/newBrush.js +102 -0
  67. package/src/brush/1d-multi/selected.js +160 -0
  68. package/src/brush/1d-multi/uninstall.js +12 -0
  69. package/src/brush/angular/brushReset.js +14 -0
  70. package/src/brush/angular/index.js +19 -0
  71. package/src/brush/angular/install.js +141 -0
  72. package/src/brush/angular/onDrag.js +109 -0
  73. package/src/brush/angular/onDragEnd.js +30 -0
  74. package/src/brush/angular/onDragStart.js +38 -0
  75. package/src/brush/angular/removeStrum.js +12 -0
  76. package/src/brush/angular/selected.js +63 -0
  77. package/src/brush/angular/uninstall.js +17 -0
  78. package/src/brush/angular/util/hypothenuse.js +3 -0
  79. package/src/brush/consecutive.js +11 -0
  80. package/src/brush/dimensionsForPoint.js +32 -0
  81. package/src/brush/invertByScale.js +24 -0
  82. package/src/brush/strums/brushReset.js +14 -0
  83. package/src/brush/strums/index.js +19 -0
  84. package/src/brush/strums/install.js +81 -0
  85. package/src/brush/strums/onDrag.js +83 -0
  86. package/src/brush/strums/onDragEnd.js +20 -0
  87. package/src/brush/strums/onDragStart.js +33 -0
  88. package/src/brush/strums/removeStrum.js +10 -0
  89. package/src/brush/strums/selected.js +52 -0
  90. package/src/brush/strums/uninstall.js +16 -0
  91. package/src/index.js +226 -0
  92. package/src/parallel-coordinates.css +61 -0
  93. package/src/state/defaultConfig.js +36 -0
  94. package/src/state/index.js +86 -0
  95. package/src/state/sideEffects.js +89 -0
  96. package/src/util/colorPath.js +71 -0
  97. package/src/util/computeCentroids.js +44 -0
  98. package/src/util/computeClusterCentroids.js +33 -0
  99. package/src/util/computeControlPoints.js +38 -0
  100. package/src/util/dimensionLabels.js +11 -0
  101. package/src/util/flipAxisAndUpdatePCP.js +19 -0
  102. package/src/util/functor.js +3 -0
  103. package/src/util/getRange.js +24 -0
  104. package/src/util/getset.js +23 -0
  105. package/src/util/height.js +3 -0
  106. package/src/util/isBrushed.js +15 -0
  107. package/src/util/renderQueue.js +67 -0
  108. package/src/util/rotateLabels.js +18 -0
  109. package/src/util/width.js +3 -0
@@ -0,0 +1,4132 @@
1
+ import 'requestanimationframe';
2
+ import { select, pointer, selectAll } from 'd3-selection';
3
+ import { brushSelection, brushY } from 'd3-brush';
4
+ import { drag } from 'd3-drag';
5
+ import { arc } from 'd3-shape';
6
+ import { scaleOrdinal, scalePoint, scaleLinear, scaleTime } from 'd3-scale';
7
+ import { extent, min, ascending } from 'd3-array';
8
+ import { axisLeft, axisBottom, axisTop, axisRight } from 'd3-axis';
9
+ import { dispatch } from 'd3-dispatch';
10
+
11
+ var renderQueue = function renderQueue(func) {
12
+ var _queue = [],
13
+ // data to be rendered
14
+ _rate = 1000,
15
+ // number of calls per frame
16
+ _invalidate = function _invalidate() {},
17
+ // invalidate last render queue
18
+ _clear = function _clear() {}; // clearing function
19
+
20
+ var _rq = function rq(data) {
21
+ if (data) _rq.data(data);
22
+ _invalidate();
23
+ _clear();
24
+ _rq.render();
25
+ };
26
+ _rq.render = function () {
27
+ var valid = true;
28
+ _invalidate = _rq.invalidate = function () {
29
+ valid = false;
30
+ };
31
+ function doFrame() {
32
+ if (!valid) return true;
33
+ var chunk = _queue.splice(0, _rate);
34
+ chunk.map(func);
35
+ requestAnimationFrame(doFrame);
36
+ }
37
+ doFrame();
38
+ };
39
+ _rq.data = function (data) {
40
+ _invalidate();
41
+ _queue = data.slice(0); // creates a copy of the data
42
+ return _rq;
43
+ };
44
+ _rq.add = function (data) {
45
+ _queue = _queue.concat(data);
46
+ };
47
+ _rq.rate = function (value) {
48
+ if (!arguments.length) return _rate;
49
+ _rate = value;
50
+ return _rq;
51
+ };
52
+ _rq.remaining = function () {
53
+ return _queue.length;
54
+ };
55
+
56
+ // clear the canvas
57
+ _rq.clear = function (func) {
58
+ if (!arguments.length) {
59
+ _clear();
60
+ return _rq;
61
+ }
62
+ _clear = func;
63
+ return _rq;
64
+ };
65
+ _rq.invalidate = _invalidate;
66
+ return _rq;
67
+ };
68
+
69
+ var w = function w(config) {
70
+ return config.width - config.margin.right - config.margin.left;
71
+ };
72
+
73
+ var invertCategorical = function invertCategorical(selection, scale) {
74
+ if (selection.length === 0) {
75
+ return [];
76
+ }
77
+ var domain = scale.domain();
78
+ var range = scale.range();
79
+ var found = [];
80
+ range.forEach(function (d, i) {
81
+ if (d >= selection[0] && d <= selection[1]) {
82
+ found.push(domain[i]);
83
+ }
84
+ });
85
+ return found;
86
+ };
87
+ var invertByScale = function invertByScale(selection, scale) {
88
+ if (scale === null) return [];
89
+ return typeof scale.invert === 'undefined' ? invertCategorical(selection, scale) : selection.map(function (d) {
90
+ return scale.invert(d);
91
+ });
92
+ };
93
+
94
+ var brushExtents$1 = function brushExtents(state, config, pc) {
95
+ return function (extents) {
96
+ var brushes = state.brushes,
97
+ brushNodes = state.brushNodes;
98
+ if (typeof extents === 'undefined') {
99
+ return Object.keys(config.dimensions).reduce(function (acc, cur) {
100
+ var brush = brushes[cur];
101
+ //todo: brush check
102
+ if (brush !== undefined && brushSelection(brushNodes[cur]) !== null) {
103
+ var raw = brushSelection(brushNodes[cur]);
104
+ var yScale = config.dimensions[cur].yscale;
105
+ var scaled = invertByScale(raw, yScale);
106
+ acc[cur] = {
107
+ extent: brush.extent(),
108
+ selection: {
109
+ raw: raw,
110
+ scaled: scaled
111
+ }
112
+ };
113
+ }
114
+ return acc;
115
+ }, {});
116
+ } else {
117
+ //first get all the brush selections
118
+ var brushSelections = {};
119
+ pc.g().selectAll('.brush').each(function (d) {
120
+ brushSelections[d] = select(this);
121
+ });
122
+
123
+ // loop over each dimension and update appropriately (if it was passed in through extents)
124
+ Object.keys(config.dimensions).forEach(function (d) {
125
+ if (extents[d] === undefined) {
126
+ return;
127
+ }
128
+ var brush = brushes[d];
129
+ if (brush !== undefined) {
130
+ var dim = config.dimensions[d];
131
+ var yExtent = extents[d].map(dim.yscale);
132
+
133
+ //update the extent
134
+ //sets the brushable extent to the specified array of points [[x0, y0], [x1, y1]]
135
+ //we actually don't need this since we are using brush.move below
136
+ //extents set the limits of the brush which means a user will not be able
137
+ //to move or drag the brush beyond the limits set by brush.extent
138
+ //brush.extent([[-15, yExtent[1]], [15, yExtent[0]]]);
139
+
140
+ //redraw the brush
141
+ //https://github.com/d3/d3-brush#brush_move
142
+ // For an x-brush, it must be defined as [x0, x1]; for a y-brush, it must be defined as [y0, y1].
143
+ brushSelections[d].call(brush).call(brush.move, yExtent.reverse());
144
+
145
+ //fire some events
146
+ // brush.event(brushSelections[d]);
147
+ }
148
+ });
149
+
150
+ //redraw the chart
151
+ pc.renderBrushed();
152
+ return pc;
153
+ }
154
+ };
155
+ };
156
+
157
+ var _this$5 = undefined;
158
+ var brushReset$4 = function brushReset(state, config, pc) {
159
+ return function (dimension) {
160
+ var brushes = state.brushes;
161
+ if (dimension === undefined) {
162
+ config.brushed = false;
163
+ if (pc.g() !== undefined && pc.g() !== null) {
164
+ pc.g().selectAll('.brush').each(function (d) {
165
+ if (brushes[d] !== undefined) {
166
+ select(this).call(brushes[d].move, null);
167
+ }
168
+ });
169
+ pc.renderBrushed();
170
+ }
171
+ } else {
172
+ config.brushed = false;
173
+ if (pc.g() !== undefined && pc.g() !== null) {
174
+ pc.g().selectAll('.brush').each(function (d) {
175
+ if (d !== dimension) return;
176
+ select(this).call(brushes[d].move, null);
177
+ if (typeof brushes[d].type === 'function') {
178
+ brushes[d].event(select(this));
179
+ }
180
+ });
181
+ pc.renderBrushed();
182
+ }
183
+ }
184
+ return _this$5;
185
+ };
186
+ };
187
+
188
+ //https://github.com/d3/d3-brush/issues/10
189
+
190
+ // data within extents
191
+ var selected$4 = function selected(state, config, brushGroup) {
192
+ return function () {
193
+ var brushNodes = state.brushNodes;
194
+ var is_brushed = function is_brushed(p) {
195
+ return brushNodes[p] && brushSelection(brushNodes[p]) !== null;
196
+ };
197
+ var actives = Object.keys(config.dimensions).filter(is_brushed);
198
+ var extents = actives.map(function (p) {
199
+ var _brushRange = brushSelection(brushNodes[p]);
200
+ if (typeof config.dimensions[p].yscale.invert === 'function') {
201
+ return [config.dimensions[p].yscale.invert(_brushRange[1]), config.dimensions[p].yscale.invert(_brushRange[0])];
202
+ } else {
203
+ return _brushRange;
204
+ }
205
+ });
206
+ // We don't want to return the full data set when there are no axes brushed.
207
+ // Actually, when there are no axes brushed, by definition, no items are
208
+ // selected. So, let's avoid the filtering and just return false.
209
+ //if (actives.length === 0) return false;
210
+
211
+ // Resolves broken examples for now. They expect to get the full dataset back from empty brushes
212
+ if (actives.length === 0) return config.data;
213
+
214
+ // test if within range
215
+ var within = {
216
+ date: function date(d, p, dimension) {
217
+ if (typeof config.dimensions[p].yscale.bandwidth === 'function') {
218
+ // if it is ordinal
219
+ return extents[dimension][0] <= config.dimensions[p].yscale(d[p]) && config.dimensions[p].yscale(d[p]) <= extents[dimension][1];
220
+ } else {
221
+ return extents[dimension][0] <= d[p] && d[p] <= extents[dimension][1];
222
+ }
223
+ },
224
+ number: function number(d, p, dimension) {
225
+ if (typeof config.dimensions[p].yscale.bandwidth === 'function') {
226
+ // if it is ordinal
227
+ return extents[dimension][0] <= config.dimensions[p].yscale(d[p]) && config.dimensions[p].yscale(d[p]) <= extents[dimension][1];
228
+ } else {
229
+ return extents[dimension][0] <= d[p] && d[p] <= extents[dimension][1];
230
+ }
231
+ },
232
+ string: function string(d, p, dimension) {
233
+ return extents[dimension][0] <= config.dimensions[p].yscale(d[p]) && config.dimensions[p].yscale(d[p]) <= extents[dimension][1];
234
+ }
235
+ };
236
+ return config.data.filter(function (d) {
237
+ switch (brushGroup.predicate) {
238
+ case 'AND':
239
+ return actives.every(function (p, dimension) {
240
+ return within[config.dimensions[p].type](d, p, dimension);
241
+ });
242
+ case 'OR':
243
+ return actives.some(function (p, dimension) {
244
+ return within[config.dimensions[p].type](d, p, dimension);
245
+ });
246
+ default:
247
+ throw new Error('Unknown brush predicate ' + config.brushPredicate);
248
+ }
249
+ });
250
+ };
251
+ };
252
+
253
+ var brushUpdated$1 = function brushUpdated(config, pc, events, args) {
254
+ return function (newSelection) {
255
+ config.brushed = newSelection;
256
+ events.call('brush', pc, config.brushed, args);
257
+ pc.renderBrushed();
258
+ };
259
+ };
260
+ var brushFor$1 = function brushFor(state, config, pc, events, brushGroup) {
261
+ return function (axis, _selector) {
262
+ // handle hidden axes which will not be a property of dimensions
263
+ if (!config.dimensions.hasOwnProperty(axis)) {
264
+ return function () {};
265
+ }
266
+ var brushRangeMax = config.dimensions[axis].type === 'string' ? config.dimensions[axis].yscale.range()[config.dimensions[axis].yscale.range().length - 1] : config.dimensions[axis].yscale.range()[0];
267
+ var _brush = brushY(_selector).extent([[-15, 0], [15, brushRangeMax]]);
268
+ var convertBrushArguments = function convertBrushArguments(args) {
269
+ var args_array = Array.prototype.slice.call(args);
270
+ var axis = args_array[0];
271
+ var raw = brushSelection(args_array[2][0]) || [];
272
+
273
+ // handle hidden axes which will not have a yscale
274
+ var yscale = null;
275
+ if (config.dimensions.hasOwnProperty(axis)) {
276
+ yscale = config.dimensions[axis].yscale;
277
+ }
278
+
279
+ // ordinal scales do not have invert
280
+ var scaled = invertByScale(raw, yscale);
281
+ return {
282
+ axis: args_array[0],
283
+ node: args_array[2][0],
284
+ selection: {
285
+ raw: raw,
286
+ scaled: scaled
287
+ }
288
+ };
289
+ };
290
+ _brush.on('start', function (event) {
291
+ if (event.sourceEvent !== null) {
292
+ events.call('brushstart', pc, config.brushed, convertBrushArguments(arguments));
293
+ if (typeof event.sourceEvent.stopPropagation === 'function') {
294
+ event.sourceEvent.stopPropagation();
295
+ }
296
+ }
297
+ }).on('brush', function () {
298
+ brushUpdated$1(config, pc, events, convertBrushArguments(arguments))(selected$4(state, config, brushGroup)());
299
+ }).on('end', function () {
300
+ brushUpdated$1(config, pc, events)(selected$4(state, config, brushGroup)());
301
+ events.call('brushend', pc, config.brushed, convertBrushArguments(arguments));
302
+ });
303
+ state.brushes[axis] = _brush;
304
+ state.brushNodes[axis] = _selector.node();
305
+ return _brush;
306
+ };
307
+ };
308
+
309
+ var install$3 = function install(state, config, pc, events, brushGroup) {
310
+ return function () {
311
+ if (!pc.g()) {
312
+ pc.createAxes();
313
+ }
314
+
315
+ // Add and store a brush for each axis.
316
+ var brush = pc.g().append('svg:g').attr('class', 'brush').each(function (d) {
317
+ select(this).call(brushFor$1(state, config, pc, events, brushGroup)(d, select(this)));
318
+ });
319
+ brush.selectAll('rect').style('visibility', null).attr('x', -15).attr('width', 30);
320
+ brush.selectAll('rect.background').style('fill', 'transparent');
321
+ brush.selectAll('rect.extent').style('fill', 'rgba(255,255,255,0.25)').style('stroke', 'rgba(0,0,0,0.6)');
322
+ brush.selectAll('.resize rect').style('fill', 'rgba(0,0,0,0.1)');
323
+ pc.brushExtents = brushExtents$1(state, config, pc);
324
+ pc.brushReset = brushReset$4(state, config, pc);
325
+ return pc;
326
+ };
327
+ };
328
+
329
+ var uninstall$3 = function uninstall(state, pc) {
330
+ return function () {
331
+ if (pc.g() !== undefined && pc.g() !== null) pc.g().selectAll('.brush').remove();
332
+ state.brushes = {};
333
+ delete pc.brushExtents;
334
+ delete pc.brushReset;
335
+ };
336
+ };
337
+
338
+ var install1DAxes = function install1DAxes(brushGroup, config, pc, events) {
339
+ var state = {
340
+ brushes: {},
341
+ brushNodes: {}
342
+ };
343
+ brushGroup.modes['1D-axes'] = {
344
+ install: install$3(state, config, pc, events, brushGroup),
345
+ uninstall: uninstall$3(state, pc),
346
+ selected: selected$4(state, config, brushGroup),
347
+ brushState: brushExtents$1(state, config, pc)
348
+ };
349
+ };
350
+
351
+ var drawBrushes = function drawBrushes(brushes, config, pc, axis, selector) {
352
+ var brushSelection = selector.selectAll('.brush').data(brushes, function (d) {
353
+ return d.id;
354
+ });
355
+ brushSelection.enter().insert('g', '.brush').attr('class', 'brush').attr('dimension', axis).attr('id', function (b) {
356
+ return 'brush-' + Object.keys(config.dimensions).indexOf(axis) + '-' + b.id;
357
+ }).each(function (brushObject) {
358
+ brushObject.brush(select(this));
359
+ });
360
+ brushSelection.each(function (brushObject) {
361
+ select(this).attr('class', 'brush').selectAll('.overlay').style('pointer-events', function () {
362
+ var brush = brushObject.brush;
363
+ if (brushObject.id === brushes.length - 1 && brush !== undefined) {
364
+ return 'all';
365
+ } else {
366
+ return 'none';
367
+ }
368
+ });
369
+ });
370
+ brushSelection.exit().remove();
371
+ };
372
+
373
+ function _arrayLikeToArray(r, a) {
374
+ (null == a || a > r.length) && (a = r.length);
375
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
376
+ return n;
377
+ }
378
+ function _arrayWithHoles(r) {
379
+ if (Array.isArray(r)) return r;
380
+ }
381
+ function _createForOfIteratorHelper(r, e) {
382
+ var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
383
+ if (!t) {
384
+ if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) {
385
+ t && (r = t);
386
+ var n = 0,
387
+ F = function () {};
388
+ return {
389
+ s: F,
390
+ n: function () {
391
+ return n >= r.length ? {
392
+ done: true
393
+ } : {
394
+ done: false,
395
+ value: r[n++]
396
+ };
397
+ },
398
+ e: function (r) {
399
+ throw r;
400
+ },
401
+ f: F
402
+ };
403
+ }
404
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
405
+ }
406
+ var o,
407
+ a = true,
408
+ u = false;
409
+ return {
410
+ s: function () {
411
+ t = t.call(r);
412
+ },
413
+ n: function () {
414
+ var r = t.next();
415
+ return a = r.done, r;
416
+ },
417
+ e: function (r) {
418
+ u = true, o = r;
419
+ },
420
+ f: function () {
421
+ try {
422
+ a || null == t.return || t.return();
423
+ } finally {
424
+ if (u) throw o;
425
+ }
426
+ }
427
+ };
428
+ }
429
+ function _defineProperty(e, r, t) {
430
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
431
+ value: t,
432
+ enumerable: true,
433
+ configurable: true,
434
+ writable: true
435
+ }) : e[r] = t, e;
436
+ }
437
+ function _iterableToArrayLimit(r, l) {
438
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
439
+ if (null != t) {
440
+ var e,
441
+ n,
442
+ i,
443
+ u,
444
+ a = [],
445
+ f = true,
446
+ o = false;
447
+ try {
448
+ if (i = (t = t.call(r)).next, 0 === l) {
449
+ if (Object(t) !== t) return;
450
+ f = !1;
451
+ } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
452
+ } catch (r) {
453
+ o = true, n = r;
454
+ } finally {
455
+ try {
456
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
457
+ } finally {
458
+ if (o) throw n;
459
+ }
460
+ }
461
+ return a;
462
+ }
463
+ }
464
+ function _nonIterableRest() {
465
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
466
+ }
467
+ function ownKeys(e, r) {
468
+ var t = Object.keys(e);
469
+ if (Object.getOwnPropertySymbols) {
470
+ var o = Object.getOwnPropertySymbols(e);
471
+ r && (o = o.filter(function (r) {
472
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
473
+ })), t.push.apply(t, o);
474
+ }
475
+ return t;
476
+ }
477
+ function _objectSpread2(e) {
478
+ for (var r = 1; r < arguments.length; r++) {
479
+ var t = null != arguments[r] ? arguments[r] : {};
480
+ r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
481
+ _defineProperty(e, r, t[r]);
482
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
483
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
484
+ });
485
+ }
486
+ return e;
487
+ }
488
+ function _slicedToArray(r, e) {
489
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
490
+ }
491
+ function _toPrimitive(t, r) {
492
+ if ("object" != typeof t || !t) return t;
493
+ var e = t[Symbol.toPrimitive];
494
+ if (void 0 !== e) {
495
+ var i = e.call(t, r);
496
+ if ("object" != typeof i) return i;
497
+ throw new TypeError("@@toPrimitive must return a primitive value.");
498
+ }
499
+ return ("string" === r ? String : Number)(t);
500
+ }
501
+ function _toPropertyKey(t) {
502
+ var i = _toPrimitive(t, "string");
503
+ return "symbol" == typeof i ? i : i + "";
504
+ }
505
+ function _unsupportedIterableToArray(r, a) {
506
+ if (r) {
507
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
508
+ var t = {}.toString.call(r).slice(8, -1);
509
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
510
+ }
511
+ }
512
+
513
+ // data within extents
514
+ var selected$3 = function selected(state, config, pc, events, brushGroup) {
515
+ var brushes = state.brushes;
516
+ var is_brushed = function is_brushed(p, pos) {
517
+ var axisBrushes = brushes[p];
518
+ for (var i = 0; i < axisBrushes.length; i++) {
519
+ var brush = document.getElementById('brush-' + pos + '-' + i);
520
+ if (brush && brushSelection(brush) !== null) {
521
+ return true;
522
+ }
523
+ }
524
+ return false;
525
+ };
526
+ var actives = Object.keys(config.dimensions).filter(is_brushed);
527
+ var extents = actives.map(function (p) {
528
+ var axisBrushes = brushes[p];
529
+ return axisBrushes.filter(function (d) {
530
+ return !pc.hideAxis().includes(d);
531
+ }).map(function (d, i) {
532
+ return brushSelection(document.getElementById('brush-' + Object.keys(config.dimensions).indexOf(p) + '-' + i));
533
+ }).map(function (d, i) {
534
+ if (d === null || d === undefined) {
535
+ return null;
536
+ } else if (typeof config.dimensions[p].yscale.invert === 'function') {
537
+ return [config.dimensions[p].yscale.invert(d[1]), config.dimensions[p].yscale.invert(d[0])];
538
+ } else {
539
+ return d;
540
+ }
541
+ });
542
+ });
543
+
544
+ // We don't want to return the full data set when there are no axes brushed.
545
+ // Actually, when there are no axes brushed, by definition, no items are
546
+ // selected. So, let's avoid the filtering and just return false.
547
+ //if (actives.length === 0) return false;
548
+
549
+ // Resolves broken examples for now. They expect to get the full dataset back from empty brushes
550
+ if (actives.length === 0) return config.data;
551
+
552
+ // test if within range
553
+ var within = {
554
+ date: function date(d, p, i) {
555
+ var dimExt = extents[i];
556
+ if (typeof config.dimensions[p].yscale.bandwidth === 'function') {
557
+ // if it is ordinal
558
+ var _iterator = _createForOfIteratorHelper(dimExt),
559
+ _step;
560
+ try {
561
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
562
+ var e = _step.value;
563
+ if (e === null || e === undefined) {
564
+ continue;
565
+ }
566
+ if (e[0] <= config.dimensions[p].yscale(d[p]) && config.dimensions[p].yscale(d[p]) <= e[1]) {
567
+ return true;
568
+ }
569
+ }
570
+ } catch (err) {
571
+ _iterator.e(err);
572
+ } finally {
573
+ _iterator.f();
574
+ }
575
+ return false;
576
+ } else {
577
+ var _iterator2 = _createForOfIteratorHelper(dimExt),
578
+ _step2;
579
+ try {
580
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
581
+ var _e = _step2.value;
582
+ if (_e === null || _e === undefined) {
583
+ continue;
584
+ }
585
+ if (_e[0] <= d[p] && d[p] <= _e[1]) {
586
+ return true;
587
+ }
588
+ }
589
+ } catch (err) {
590
+ _iterator2.e(err);
591
+ } finally {
592
+ _iterator2.f();
593
+ }
594
+ return false;
595
+ }
596
+ },
597
+ number: function number(d, p, i) {
598
+ var dimExt = extents[i];
599
+ if (typeof config.dimensions[p].yscale.bandwidth === 'function') {
600
+ // if it is ordinal
601
+ var _iterator3 = _createForOfIteratorHelper(dimExt),
602
+ _step3;
603
+ try {
604
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
605
+ var e = _step3.value;
606
+ if (e === null || e === undefined) {
607
+ continue;
608
+ }
609
+ if (e[0] <= config.dimensions[p].yscale(d[p]) && config.dimensions[p].yscale(d[p]) <= e[1]) {
610
+ return true;
611
+ }
612
+ }
613
+ } catch (err) {
614
+ _iterator3.e(err);
615
+ } finally {
616
+ _iterator3.f();
617
+ }
618
+ return false;
619
+ } else {
620
+ var _iterator4 = _createForOfIteratorHelper(dimExt),
621
+ _step4;
622
+ try {
623
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
624
+ var _e2 = _step4.value;
625
+ if (_e2 === null || _e2 === undefined) {
626
+ continue;
627
+ }
628
+ if (_e2[0] <= d[p] && d[p] <= _e2[1]) {
629
+ return true;
630
+ }
631
+ }
632
+ } catch (err) {
633
+ _iterator4.e(err);
634
+ } finally {
635
+ _iterator4.f();
636
+ }
637
+ return false;
638
+ }
639
+ },
640
+ string: function string(d, p, i) {
641
+ var dimExt = extents[i];
642
+ var _iterator5 = _createForOfIteratorHelper(dimExt),
643
+ _step5;
644
+ try {
645
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
646
+ var e = _step5.value;
647
+ if (e === null || e === undefined) {
648
+ continue;
649
+ }
650
+ if (e[0] <= config.dimensions[p].yscale(d[p]) && config.dimensions[p].yscale(d[p]) <= e[1]) {
651
+ return true;
652
+ }
653
+ }
654
+ } catch (err) {
655
+ _iterator5.e(err);
656
+ } finally {
657
+ _iterator5.f();
658
+ }
659
+ return false;
660
+ }
661
+ };
662
+ return config.data.filter(function (d) {
663
+ switch (brushGroup.predicate) {
664
+ case 'AND':
665
+ return actives.every(function (p, i) {
666
+ return within[config.dimensions[p].type](d, p, i);
667
+ });
668
+ case 'OR':
669
+ return actives.some(function (p, i) {
670
+ return within[config.dimensions[p].type](d, p, i);
671
+ });
672
+ default:
673
+ throw new Error('Unknown brush predicate ' + config.brushPredicate);
674
+ }
675
+ });
676
+ };
677
+
678
+ var brushUpdated = function brushUpdated(config, pc, events) {
679
+ return function (newSelection) {
680
+ config.brushed = newSelection;
681
+ events.call('brush', pc, config.brushed);
682
+ pc.renderBrushed();
683
+ };
684
+ };
685
+ var _newBrush = function newBrush(state, config, pc, events, brushGroup) {
686
+ return function (axis, _selector) {
687
+ var brushes = state.brushes,
688
+ brushNodes = state.brushNodes;
689
+ var brushRangeMax = config.dimensions[axis].type === 'string' ? config.dimensions[axis].yscale.range()[config.dimensions[axis].yscale.range().length - 1] : config.dimensions[axis].yscale.range()[0];
690
+ var brush = brushY().extent([[-15, 0], [15, brushRangeMax]]);
691
+ var id = brushes[axis] ? brushes[axis].length : 0;
692
+ var node = 'brush-' + Object.keys(config.dimensions).indexOf(axis) + '-' + id;
693
+ if (brushes[axis]) {
694
+ brushes[axis].push({
695
+ id: id,
696
+ brush: brush,
697
+ node: node
698
+ });
699
+ } else {
700
+ brushes[axis] = [{
701
+ id: id,
702
+ brush: brush,
703
+ node: node
704
+ }];
705
+ }
706
+ if (brushNodes[axis]) {
707
+ brushNodes[axis].push({
708
+ id: id,
709
+ node: node
710
+ });
711
+ } else {
712
+ brushNodes[axis] = [{
713
+ id: id,
714
+ node: node
715
+ }];
716
+ }
717
+ brush.on('start', function (event) {
718
+ if (event.sourceEvent !== null) {
719
+ events.call('brushstart', pc, config.brushed);
720
+ if (typeof event.sourceEvent.stopPropagation === 'function') {
721
+ event.sourceEvent.stopPropagation();
722
+ }
723
+ }
724
+ }).on('brush', function () {
725
+ // record selections
726
+ brushUpdated(config, pc, events)(selected$3(state, config, pc, events, brushGroup));
727
+ }).on('end', function (event) {
728
+ // Figure out if our latest brush has a selection
729
+ var lastBrushID = brushes[axis][brushes[axis].length - 1].id;
730
+ var lastBrush = document.getElementById('brush-' + Object.keys(config.dimensions).indexOf(axis) + '-' + lastBrushID);
731
+ var selection = brushSelection(lastBrush);
732
+ if (selection !== undefined && selection !== null && selection[0] !== selection[1]) {
733
+ _newBrush(state, config, pc, events, brushGroup)(axis, _selector);
734
+ drawBrushes(brushes[axis], config, pc, axis, _selector);
735
+ brushUpdated(config, pc, events)(selected$3(state, config, pc, events, brushGroup));
736
+ } else {
737
+ if (event.sourceEvent && event.sourceEvent.toString() === '[object MouseEvent]' && event.selection === null) {
738
+ pc.brushReset(axis);
739
+ }
740
+ }
741
+ events.call('brushend', pc, config.brushed);
742
+ });
743
+ return brush;
744
+ };
745
+ };
746
+
747
+ /**
748
+ *
749
+ * extents are in format of [[2,6], [3,5]]
750
+ *
751
+ * * @param state
752
+ * @param config
753
+ * @param pc
754
+ * @returns {Function}
755
+ */
756
+ var brushExtents = function brushExtents(state, config, pc, events, brushGroup) {
757
+ return function (extents) {
758
+ var brushes = state.brushes;
759
+ var hiddenAxes = pc.hideAxis();
760
+ if (typeof extents === 'undefined') {
761
+ return Object.keys(config.dimensions).filter(function (d) {
762
+ return !hiddenAxes.includes(d);
763
+ }).reduce(function (acc, cur, pos) {
764
+ var axisBrushes = brushes[cur];
765
+ if (axisBrushes === undefined || axisBrushes === null) {
766
+ acc[cur] = [];
767
+ } else {
768
+ acc[cur] = axisBrushes.reduce(function (d, p, i) {
769
+ var raw = brushSelection(document.getElementById('brush-' + pos + '-' + i));
770
+ if (raw) {
771
+ var yScale = config.dimensions[cur].yscale;
772
+ var scaled = invertByScale(raw, yScale);
773
+ d.push({
774
+ extent: p.brush.extent(),
775
+ selection: {
776
+ raw: raw,
777
+ scaled: scaled
778
+ }
779
+ });
780
+ }
781
+ return d;
782
+ }, []);
783
+ }
784
+ return acc;
785
+ }, {});
786
+ } else {
787
+ // //first get all the brush selections
788
+ // loop over each dimension and update appropriately (if it was passed in through extents)
789
+ Object.keys(config.dimensions).forEach(function (d, pos) {
790
+ if (extents[d] === undefined || extents[d] === null) {
791
+ return;
792
+ }
793
+ var dim = config.dimensions[d];
794
+ var yExtents = extents[d].map(function (e) {
795
+ return e.map(dim.yscale);
796
+ });
797
+ var _bs = yExtents.map(function (e, j) {
798
+ var _brush = _newBrush(state, config, pc, events, brushGroup)(d, select('#brush-group-' + pos));
799
+ //update the extent
800
+ //sets the brushable extent to the specified array of points [[x0, y0], [x1, y1]]
801
+ _brush.extent([[-15, e[1]], [15, e[0]]]);
802
+ return {
803
+ id: j,
804
+ brush: _brush,
805
+ ext: e
806
+ };
807
+ });
808
+ brushes[d] = _bs;
809
+ drawBrushes(_bs, config, pc, d, select('#brush-group-' + pos));
810
+
811
+ //redraw the brush
812
+ //https://github.com/d3/d3-brush#brush_move
813
+ // For an x-brush, it must be defined as [x0, x1]; for a y-brush, it must be defined as [y0, y1].
814
+ _bs.forEach(function (f, k) {
815
+ select('#brush-' + pos + '-' + k).call(f.brush).call(f.brush.move, f.ext.reverse());
816
+ });
817
+ });
818
+
819
+ //redraw the chart
820
+ pc.renderBrushed();
821
+ return pc;
822
+ }
823
+ };
824
+ };
825
+
826
+ var _this$4 = undefined;
827
+ var brushReset$3 = function brushReset(state, config, pc) {
828
+ return function (dimension) {
829
+ var brushes = state.brushes;
830
+ if (dimension === undefined) {
831
+ if (pc.g() !== undefined && pc.g() !== null) {
832
+ Object.keys(config.dimensions).forEach(function (d, pos) {
833
+ var axisBrush = brushes[d];
834
+
835
+ // hidden axes will be undefined
836
+ if (axisBrush) {
837
+ axisBrush.forEach(function (e, i) {
838
+ var brush = document.getElementById('brush-' + pos + '-' + i);
839
+ if (brush && brushSelection(brush) !== null) {
840
+ pc.g().select('#brush-' + pos + '-' + i).call(e.brush.move, null);
841
+ }
842
+ });
843
+ }
844
+ });
845
+ pc.renderBrushed();
846
+ }
847
+ } else {
848
+ if (pc.g() !== undefined && pc.g() !== null) {
849
+ var axisBrush = brushes[dimension];
850
+ var pos = Object.keys(config.dimensions).indexOf(dimension);
851
+ if (axisBrush) {
852
+ axisBrush.forEach(function (e, i) {
853
+ var brush = document.getElementById('brush-' + pos + '-' + i);
854
+ if (brushSelection(brush) !== null) {
855
+ pc.g().select('#brush-' + pos + '-' + i).call(e.brush.move, null);
856
+ if (typeof e.event === 'function') {
857
+ e.event(select('#brush-' + pos + '-' + i));
858
+ }
859
+ }
860
+ });
861
+ }
862
+ pc.renderBrushed();
863
+ }
864
+ }
865
+ return _this$4;
866
+ };
867
+ };
868
+
869
+ var brushFor = function brushFor(state, config, pc, events, brushGroup) {
870
+ return function (axis, _selector) {
871
+ var brushes = state.brushes;
872
+ _newBrush(state, config, pc, events, brushGroup)(axis, _selector);
873
+ drawBrushes(brushes[axis], config, pc, axis, _selector);
874
+ };
875
+ };
876
+
877
+ var install$2 = function install(state, config, pc, events, brushGroup) {
878
+ return function () {
879
+ if (!pc.g()) {
880
+ pc.createAxes();
881
+ }
882
+ var hiddenAxes = pc.hideAxis();
883
+ pc.g().append('svg:g').attr('id', function (d, i) {
884
+ return 'brush-group-' + i;
885
+ }).attr('class', 'brush-group').attr('dimension', function (d) {
886
+ return d;
887
+ }).each(function (d) {
888
+ if (!hiddenAxes.includes(d)) {
889
+ brushFor(state, config, pc, events, brushGroup)(d, select(this));
890
+ }
891
+ });
892
+ pc.brushExtents = brushExtents(state, config, pc, events, brushGroup);
893
+ pc.brushReset = brushReset$3(state, config, pc);
894
+ return pc;
895
+ };
896
+ };
897
+
898
+ var uninstall$2 = function uninstall(state, pc) {
899
+ return function () {
900
+ if (pc.g() !== undefined && pc.g() !== null) pc.g().selectAll('.brush-group').remove();
901
+ state.brushes = {};
902
+ delete pc.brushExtents;
903
+ delete pc.brushReset;
904
+ };
905
+ };
906
+
907
+ var install1DMultiAxes = function install1DMultiAxes(brushGroup, config, pc, events) {
908
+ var state = {
909
+ brushes: {},
910
+ brushNodes: {}
911
+ };
912
+ brushGroup.modes['1D-axes-multi'] = {
913
+ install: install$2(state, config, pc, events, brushGroup),
914
+ uninstall: uninstall$2(state, pc),
915
+ selected: selected$3(state, config, brushGroup),
916
+ brushState: brushExtents(state, config, pc)
917
+ };
918
+ };
919
+
920
+ var uninstall$1 = function uninstall(state, pc) {
921
+ return function () {
922
+ pc.selection.select('svg').select('g#strums').remove();
923
+ pc.selection.select('svg').select('rect#strum-events').remove();
924
+ pc.on('axesreorder.strums', undefined);
925
+ delete pc.brushReset;
926
+ state.strumRect = undefined;
927
+ };
928
+ };
929
+
930
+ // test if point falls between lines
931
+ var containmentTest$1 = function containmentTest(strum, width) {
932
+ return function (p) {
933
+ var p1 = [strum.p1[0] - strum.minX, strum.p1[1] - strum.minX],
934
+ p2 = [strum.p2[0] - strum.minX, strum.p2[1] - strum.minX],
935
+ m1 = 1 - width / p1[0],
936
+ b1 = p1[1] * (1 - m1),
937
+ m2 = 1 - width / p2[0],
938
+ b2 = p2[1] * (1 - m2);
939
+ var x = p[0],
940
+ y = p[1],
941
+ y1 = m1 * x + b1,
942
+ y2 = m2 * x + b2;
943
+ return y > Math.min(y1, y2) && y < Math.max(y1, y2);
944
+ };
945
+ };
946
+ var crossesStrum$1 = function crossesStrum(state, config) {
947
+ return function (d, id) {
948
+ var strum = state.strums[id],
949
+ test = containmentTest$1(strum, state.strums.width(id)),
950
+ d1 = strum.dims.left,
951
+ d2 = strum.dims.right,
952
+ y1 = config.dimensions[d1].yscale,
953
+ y2 = config.dimensions[d2].yscale,
954
+ point = [y1(d[d1]) - strum.minX, y2(d[d2]) - strum.minX];
955
+ return test(point);
956
+ };
957
+ };
958
+ var selected$2 = function selected(brushGroup, state, config) {
959
+ // Get the ids of the currently active strums.
960
+ var ids = Object.getOwnPropertyNames(state.strums).filter(function (d) {
961
+ return !isNaN(d);
962
+ }),
963
+ brushed = config.data;
964
+ if (ids.length === 0) {
965
+ return brushed;
966
+ }
967
+ var crossTest = crossesStrum$1(state, config);
968
+ return brushed.filter(function (d) {
969
+ switch (brushGroup.predicate) {
970
+ case 'AND':
971
+ return ids.every(function (id) {
972
+ return crossTest(d, id);
973
+ });
974
+ case 'OR':
975
+ return ids.some(function (id) {
976
+ return crossTest(d, id);
977
+ });
978
+ default:
979
+ throw new Error('Unknown brush predicate ' + config.brushPredicate);
980
+ }
981
+ });
982
+ };
983
+
984
+ var removeStrum$1 = function removeStrum(state, pc) {
985
+ var strum = state.strums[state.strums.active],
986
+ svg = pc.selection.select('svg').select('g#strums');
987
+ delete state.strums[state.strums.active];
988
+ svg.selectAll('line#strum-' + strum.dims.i).remove();
989
+ svg.selectAll('circle#strum-' + strum.dims.i).remove();
990
+ };
991
+
992
+ var onDragEnd$1 = function onDragEnd(brushGroup, state, config, pc, events) {
993
+ return function () {
994
+ var strum = state.strums[state.strums.active];
995
+
996
+ // Okay, somewhat unexpected, but not totally unsurprising, a mousclick is
997
+ // considered a drag without move. So we have to deal with that case
998
+ if (strum && strum.p1[0] === strum.p2[0] && strum.p1[1] === strum.p2[1]) {
999
+ removeStrum$1(state, pc);
1000
+ }
1001
+ var brushed = selected$2(brushGroup, state, config);
1002
+ state.strums.active = undefined;
1003
+ config.brushed = brushed;
1004
+ pc.renderBrushed();
1005
+ events.call('brushend', pc, config.brushed);
1006
+ };
1007
+ };
1008
+
1009
+ var _drawStrum$1 = function drawStrum(brushGroup, state, config, pc, events, strum, activePoint) {
1010
+ var _svg = pc.selection.select('svg').select('g#strums'),
1011
+ id = strum.dims.i,
1012
+ points = [strum.p1, strum.p2],
1013
+ _line = _svg.selectAll('line#strum-' + id).data([strum]),
1014
+ circles = _svg.selectAll('circle#strum-' + id).data(points),
1015
+ _drag = drag();
1016
+ _line.enter().append('line').attr('id', 'strum-' + id).attr('class', 'strum');
1017
+ _line.attr('x1', function (d) {
1018
+ return d.p1[0];
1019
+ }).attr('y1', function (d) {
1020
+ return d.p1[1];
1021
+ }).attr('x2', function (d) {
1022
+ return d.p2[0];
1023
+ }).attr('y2', function (d) {
1024
+ return d.p2[1];
1025
+ }).attr('stroke', 'black').attr('stroke-width', 2);
1026
+ _drag.on('drag', function (event, d) {
1027
+ var i = points.indexOf(d) + 1;
1028
+ strum['p' + i][0] = Math.min(Math.max(strum.minX + 1, event.x), strum.maxX);
1029
+ strum['p' + i][1] = Math.min(Math.max(strum.minY, event.y), strum.maxY);
1030
+ _drawStrum$1(brushGroup, state, config, pc, events, strum, i - 1);
1031
+ }).on('end', onDragEnd$1(brushGroup, state, config, pc, events));
1032
+ circles.enter().append('circle').attr('id', 'strum-' + id).attr('class', 'strum');
1033
+ circles.attr('cx', function (d) {
1034
+ return d[0];
1035
+ }).attr('cy', function (d) {
1036
+ return d[1];
1037
+ }).attr('r', 5).style('opacity', function (d, i) {
1038
+ return activePoint !== undefined && i === activePoint ? 0.8 : 0;
1039
+ }).on('mouseover', function () {
1040
+ select(this).style('opacity', 0.8);
1041
+ }).on('mouseout', function () {
1042
+ select(this).style('opacity', 0);
1043
+ }).call(_drag);
1044
+ };
1045
+ var onDrag$1 = function onDrag(brushGroup, state, config, pc, events) {
1046
+ return function (event) {
1047
+ var strum = state.strums[state.strums.active];
1048
+
1049
+ // Make sure that the point is within the bounds
1050
+ strum.p2[0] = Math.min(Math.max(strum.minX + 1, event.x - config.margin.left), strum.maxX);
1051
+ strum.p2[1] = Math.min(Math.max(strum.minY, event.y - config.margin.top), strum.maxY);
1052
+ _drawStrum$1(brushGroup, state, config, pc, events, strum, 1);
1053
+ };
1054
+ };
1055
+
1056
+ var h = function h(config) {
1057
+ return config.height - config.margin.top - config.margin.bottom;
1058
+ };
1059
+
1060
+ var dimensionsForPoint = function dimensionsForPoint(config, pc, xscale, p) {
1061
+ var dims = {
1062
+ i: -1,
1063
+ left: undefined,
1064
+ right: undefined
1065
+ };
1066
+ Object.keys(config.dimensions).some(function (dim, i) {
1067
+ if (xscale(dim) < p[0]) {
1068
+ dims.i = i;
1069
+ dims.left = dim;
1070
+ dims.right = Object.keys(config.dimensions)[pc.getOrderedDimensionKeys().indexOf(dim) + 1];
1071
+ return false;
1072
+ }
1073
+ return true;
1074
+ });
1075
+ if (dims.left === undefined) {
1076
+ // Event on the left side of the first axis.
1077
+ dims.i = 0;
1078
+ dims.left = pc.getOrderedDimensionKeys()[0];
1079
+ dims.right = pc.getOrderedDimensionKeys()[1];
1080
+ } else if (dims.right === undefined) {
1081
+ // Event on the right side of the last axis
1082
+ dims.i = Object.keys(config.dimensions).length - 1;
1083
+ dims.right = dims.left;
1084
+ dims.left = pc.getOrderedDimensionKeys()[Object.keys(config.dimensions).length - 2];
1085
+ }
1086
+ return dims;
1087
+ };
1088
+
1089
+ // First we need to determine between which two axes the sturm was started.
1090
+ // This will determine the freedom of movement, because a strum can
1091
+ // logically only happen between two axes, so no movement outside these axes
1092
+ // should be allowed.
1093
+ var onDragStart$1 = function onDragStart(state, config, pc, xscale) {
1094
+ return function (event) {
1095
+ var p = pointer(event, state.strumRect.node());
1096
+ p[0] = p[0] - config.margin.left;
1097
+ p[1] = p[1] - config.margin.top;
1098
+ var dims = dimensionsForPoint(config, pc, xscale, p);
1099
+ var strum = {
1100
+ p1: p,
1101
+ dims: dims,
1102
+ minX: xscale(dims.left),
1103
+ maxX: xscale(dims.right),
1104
+ minY: 0,
1105
+ maxY: h(config)
1106
+ };
1107
+
1108
+ // Make sure that the point is within the bounds
1109
+ strum.p1[0] = Math.min(Math.max(strum.minX, p[0]), strum.maxX);
1110
+ strum.p2 = strum.p1.slice();
1111
+ state.strums[dims.i] = strum;
1112
+ state.strums.active = dims.i;
1113
+ };
1114
+ };
1115
+
1116
+ var brushReset$2 = function brushReset(brushGroup, state, config, pc, events) {
1117
+ return function () {
1118
+ var ids = Object.getOwnPropertyNames(state.strums).filter(function (d) {
1119
+ return !isNaN(d);
1120
+ });
1121
+ ids.forEach(function (d) {
1122
+ state.strums.active = d;
1123
+ removeStrum$1(state, pc);
1124
+ });
1125
+ onDragEnd$1(brushGroup, state, config, pc, events)();
1126
+ };
1127
+ };
1128
+
1129
+ // Checks if the first dimension is directly left of the second dimension.
1130
+ var consecutive = function consecutive(dimensions) {
1131
+ return function (first, second) {
1132
+ var keys = Object.keys(dimensions);
1133
+ return keys.some(function (d, i) {
1134
+ return d === first ? i + i < keys.length && dimensions[i + 1] === second : false;
1135
+ });
1136
+ };
1137
+ };
1138
+
1139
+ var install$1 = function install(brushGroup, state, config, pc, events, xscale) {
1140
+ return function () {
1141
+ if (pc.g() === undefined || pc.g() === null) {
1142
+ pc.createAxes();
1143
+ }
1144
+ var _drag = drag();
1145
+
1146
+ // Map of current strums. Strums are stored per segment of the PC. A segment,
1147
+ // being the area between two axes. The left most area is indexed at 0.
1148
+ state.strums.active = undefined;
1149
+ // Returns the width of the PC segment where currently a strum is being
1150
+ // placed. NOTE: even though they are evenly spaced in our current
1151
+ // implementation, we keep for when non-even spaced segments are supported as
1152
+ // well.
1153
+ state.strums.width = function (id) {
1154
+ return state.strums[id] === undefined ? undefined : state.strums[id].maxX - state.strums[id].minX;
1155
+ };
1156
+ pc.on('axesreorder.strums', function () {
1157
+ var ids = Object.getOwnPropertyNames(state.strums).filter(function (d) {
1158
+ return !isNaN(d);
1159
+ });
1160
+ if (ids.length > 0) {
1161
+ // We have some strums, which might need to be removed.
1162
+ ids.forEach(function (d) {
1163
+ var dims = state.strums[d].dims;
1164
+ state.strums.active = d;
1165
+ // If the two dimensions of the current strum are not next to each other
1166
+ // any more, than we'll need to remove the strum. Otherwise we keep it.
1167
+ if (!consecutive(config.dimensions)(dims.left, dims.right)) {
1168
+ removeStrum$1(state, pc);
1169
+ }
1170
+ });
1171
+ onDragEnd$1(brushGroup, state, config, pc, events)();
1172
+ }
1173
+ });
1174
+
1175
+ // Add a new svg group in which we draw the strums.
1176
+ pc.selection.select('svg').append('g').attr('id', 'strums').attr('transform', 'translate(' + config.margin.left + ',' + config.margin.top + ')');
1177
+
1178
+ // Install the required brushReset function
1179
+ pc.brushReset = brushReset$2(brushGroup, state, config, pc, events);
1180
+ _drag.on('start', onDragStart$1(state, config, pc, xscale)).on('drag', onDrag$1(brushGroup, state, config, pc, events)).on('end', onDragEnd$1(brushGroup, state, config, pc, events));
1181
+
1182
+ // NOTE: The styling needs to be done here and not in the css. This is because
1183
+ // for 1D brushing, the canvas layers should not listen to
1184
+ // pointer-events._.
1185
+ state.strumRect = pc.selection.select('svg').insert('rect', 'g#strums').attr('id', 'strum-events').attr('x', config.margin.left).attr('y', config.margin.top).attr('width', w(config)).attr('height', h(config) + 2).style('opacity', 0).call(_drag);
1186
+ };
1187
+ };
1188
+
1189
+ var install2DStrums = function install2DStrums(brushGroup, config, pc, events, xscale) {
1190
+ var state = {
1191
+ strums: {},
1192
+ strumRect: {}
1193
+ };
1194
+ brushGroup.modes['2D-strums'] = {
1195
+ install: install$1(brushGroup, state, config, pc, events, xscale),
1196
+ uninstall: uninstall$1(state, pc),
1197
+ selected: selected$2(brushGroup, state, config),
1198
+ brushState: function brushState() {
1199
+ return state.strums;
1200
+ }
1201
+ };
1202
+ };
1203
+
1204
+ var uninstall = function uninstall(state, pc) {
1205
+ return function () {
1206
+ pc.selection.select('svg').select('g#arcs').remove();
1207
+ pc.selection.select('svg').select('rect#arc-events').remove();
1208
+ pc.on('axesreorder.arcs', undefined);
1209
+ delete pc.brushReset;
1210
+ state.strumRect = undefined;
1211
+ };
1212
+ };
1213
+
1214
+ var hypothenuse = function hypothenuse(a, b) {
1215
+ return Math.sqrt(a * a + b * b);
1216
+ };
1217
+
1218
+ // [0, 2*PI] -> [-PI/2, PI/2]
1219
+ var signedAngle = function signedAngle(angle) {
1220
+ return angle > Math.PI ? 1.5 * Math.PI - angle : 0.5 * Math.PI - angle;
1221
+ };
1222
+
1223
+ /**
1224
+ * angles are stored in radians from in [0, 2*PI], where 0 in 12 o'clock.
1225
+ * However, one can only select lines from 0 to PI, so we compute the
1226
+ * 'signed' angle, where 0 is the horizontal line (3 o'clock), and +/- PI/2
1227
+ * are 12 and 6 o'clock respectively.
1228
+ */
1229
+ var containmentTest = function containmentTest(arc) {
1230
+ return function (a) {
1231
+ var startAngle = signedAngle(arc.startAngle);
1232
+ var endAngle = signedAngle(arc.endAngle);
1233
+ if (startAngle > endAngle) {
1234
+ var tmp = startAngle;
1235
+ startAngle = endAngle;
1236
+ endAngle = tmp;
1237
+ }
1238
+
1239
+ // test if segment angle is contained in angle interval
1240
+ return a >= startAngle && a <= endAngle;
1241
+ };
1242
+ };
1243
+ var crossesStrum = function crossesStrum(state, config) {
1244
+ return function (d, id) {
1245
+ var arc = state.arcs[id],
1246
+ test = containmentTest(arc),
1247
+ d1 = arc.dims.left,
1248
+ d2 = arc.dims.right,
1249
+ y1 = config.dimensions[d1].yscale,
1250
+ y2 = config.dimensions[d2].yscale,
1251
+ a = state.arcs.width(id),
1252
+ b = y1(d[d1]) - y2(d[d2]),
1253
+ c = hypothenuse(a, b),
1254
+ angle = Math.asin(b / c); // rad in [-PI/2, PI/2]
1255
+ return test(angle);
1256
+ };
1257
+ };
1258
+ var selected$1 = function selected(brushGroup, state, config) {
1259
+ var ids = Object.getOwnPropertyNames(state.arcs).filter(function (d) {
1260
+ return !isNaN(d);
1261
+ });
1262
+ var brushed = config.data;
1263
+ if (ids.length === 0) {
1264
+ return brushed;
1265
+ }
1266
+ var crossTest = crossesStrum(state, config);
1267
+ return brushed.filter(function (d) {
1268
+ switch (brushGroup.predicate) {
1269
+ case 'AND':
1270
+ return ids.every(function (id) {
1271
+ return crossTest(d, id);
1272
+ });
1273
+ case 'OR':
1274
+ return ids.some(function (id) {
1275
+ return crossTest(d, id);
1276
+ });
1277
+ default:
1278
+ throw new Error('Unknown brush predicate ' + config.brushPredicate);
1279
+ }
1280
+ });
1281
+ };
1282
+
1283
+ var removeStrum = function removeStrum(state, pc) {
1284
+ var arc = state.arcs[state.arcs.active],
1285
+ svg = pc.selection.select('svg').select('g#arcs');
1286
+ delete state.arcs[state.arcs.active];
1287
+ state.arcs.active = undefined;
1288
+ svg.selectAll('line#arc-' + arc.dims.i).remove();
1289
+ svg.selectAll('circle#arc-' + arc.dims.i).remove();
1290
+ svg.selectAll('path#arc-' + arc.dims.i).remove();
1291
+ };
1292
+
1293
+ var onDragEnd = function onDragEnd(brushGroup, state, config, pc, events) {
1294
+ return function () {
1295
+ var arc = state.arcs[state.arcs.active];
1296
+
1297
+ // Okay, somewhat unexpected, but not totally unsurprising, a mousclick is
1298
+ // considered a drag without move. So we have to deal with that case
1299
+ if (arc && arc.p1[0] === arc.p2[0] && arc.p1[1] === arc.p2[1]) {
1300
+ removeStrum(state, pc);
1301
+ }
1302
+ if (arc) {
1303
+ var angle = state.arcs.startAngle(state.arcs.active);
1304
+ arc.startAngle = angle;
1305
+ arc.endAngle = angle;
1306
+ arc.arc.outerRadius(state.arcs.length(state.arcs.active)).startAngle(angle).endAngle(angle);
1307
+ }
1308
+ state.arcs.active = undefined;
1309
+ config.brushed = selected$1(brushGroup, state, config);
1310
+ pc.renderBrushed();
1311
+ events.call('brushend', pc, config.brushed);
1312
+ };
1313
+ };
1314
+
1315
+ var _drawStrum = function drawStrum(brushGroup, state, config, pc, events, arc, activePoint) {
1316
+ var svg = pc.selection.select('svg').select('g#arcs'),
1317
+ id = arc.dims.i,
1318
+ points = [arc.p2, arc.p3],
1319
+ _line = svg.selectAll('line#arc-' + id).data([{
1320
+ p1: arc.p1,
1321
+ p2: arc.p2
1322
+ }, {
1323
+ p1: arc.p1,
1324
+ p2: arc.p3
1325
+ }]),
1326
+ circles = svg.selectAll('circle#arc-' + id).data(points),
1327
+ _drag = drag(),
1328
+ _path = svg.selectAll('path#arc-' + id).data([arc]);
1329
+ _path.enter().append('path').attr('id', 'arc-' + id).attr('class', 'arc').style('fill', 'orange').style('opacity', 0.5);
1330
+ _path.attr('d', arc.arc).attr('transform', 'translate(' + arc.p1[0] + ',' + arc.p1[1] + ')');
1331
+ _line.enter().append('line').attr('id', 'arc-' + id).attr('class', 'arc');
1332
+ _line.attr('x1', function (d) {
1333
+ return d.p1[0];
1334
+ }).attr('y1', function (d) {
1335
+ return d.p1[1];
1336
+ }).attr('x2', function (d) {
1337
+ return d.p2[0];
1338
+ }).attr('y2', function (d) {
1339
+ return d.p2[1];
1340
+ }).attr('stroke', 'black').attr('stroke-width', 2);
1341
+ _drag.on('drag', function (event, d) {
1342
+ var i = points.indexOf(d) + 2;
1343
+ arc['p' + i][0] = Math.min(Math.max(arc.minX + 1, event.x), arc.maxX);
1344
+ arc['p' + i][1] = Math.min(Math.max(arc.minY, event.y), arc.maxY);
1345
+ var angle = i === 3 ? state.arcs.startAngle(id) : state.arcs.endAngle(id);
1346
+ if (arc.startAngle < Math.PI && arc.endAngle < Math.PI && angle < Math.PI || arc.startAngle >= Math.PI && arc.endAngle >= Math.PI && angle >= Math.PI) {
1347
+ if (i === 2) {
1348
+ arc.endAngle = angle;
1349
+ arc.arc.endAngle(angle);
1350
+ } else if (i === 3) {
1351
+ arc.startAngle = angle;
1352
+ arc.arc.startAngle(angle);
1353
+ }
1354
+ }
1355
+ _drawStrum(brushGroup, state, config, pc, events, arc, i - 2);
1356
+ }).on('end', onDragEnd(brushGroup, state, config, pc, events));
1357
+ circles.enter().append('circle').attr('id', 'arc-' + id).attr('class', 'arc');
1358
+ circles.attr('cx', function (d) {
1359
+ return d[0];
1360
+ }).attr('cy', function (d) {
1361
+ return d[1];
1362
+ }).attr('r', 5).style('opacity', function (d, i) {
1363
+ return activePoint !== undefined && i === activePoint ? 0.8 : 0;
1364
+ }).on('mouseover', function () {
1365
+ select(this).style('opacity', 0.8);
1366
+ }).on('mouseout', function () {
1367
+ select(this).style('opacity', 0);
1368
+ }).call(_drag);
1369
+ };
1370
+ var onDrag = function onDrag(brushGroup, state, config, pc, events) {
1371
+ return function (event) {
1372
+ var arc = state.arcs[state.arcs.active];
1373
+
1374
+ // Make sure that the point is within the bounds
1375
+ arc.p2[0] = Math.min(Math.max(arc.minX + 1, event.x - config.margin.left), arc.maxX);
1376
+ arc.p2[1] = Math.min(Math.max(arc.minY, event.y - config.margin.top), arc.maxY);
1377
+ arc.p3 = arc.p2.slice();
1378
+ _drawStrum(brushGroup, state, config, pc, events, arc, 1);
1379
+ };
1380
+ };
1381
+
1382
+ // First we need to determine between which two axes the arc was started.
1383
+ // This will determine the freedom of movement, because a arc can
1384
+ // logically only happen between two axes, so no movement outside these axes
1385
+ // should be allowed.
1386
+ var onDragStart = function onDragStart(state, config, pc, xscale) {
1387
+ return function (event) {
1388
+ var p = pointer(event, state.strumRect.node());
1389
+ p[0] = p[0] - config.margin.left;
1390
+ p[1] = p[1] - config.margin.top;
1391
+ var dims = dimensionsForPoint(config, pc, xscale, p);
1392
+ var arc$1 = {
1393
+ p1: p,
1394
+ dims: dims,
1395
+ minX: xscale(dims.left),
1396
+ maxX: xscale(dims.right),
1397
+ minY: 0,
1398
+ maxY: h(config),
1399
+ startAngle: undefined,
1400
+ endAngle: undefined,
1401
+ arc: arc().innerRadius(0)
1402
+ };
1403
+
1404
+ // Make sure that the point is within the bounds
1405
+ arc$1.p1[0] = Math.min(Math.max(arc$1.minX, p[0]), arc$1.maxX);
1406
+ arc$1.p2 = arc$1.p1.slice();
1407
+ arc$1.p3 = arc$1.p1.slice();
1408
+ state.arcs[dims.i] = arc$1;
1409
+ state.arcs.active = dims.i;
1410
+ };
1411
+ };
1412
+
1413
+ var brushReset$1 = function brushReset(brushGroup, state, config, pc, events) {
1414
+ return function () {
1415
+ var ids = Object.getOwnPropertyNames(state.arcs).filter(function (d) {
1416
+ return !isNaN(d);
1417
+ });
1418
+ ids.forEach(function (d) {
1419
+ state.arcs.active = d;
1420
+ removeStrum(state, pc);
1421
+ });
1422
+ onDragEnd(brushGroup, state, config, pc, events)();
1423
+ };
1424
+ };
1425
+
1426
+ // returns angles in [-PI/2, PI/2]
1427
+ var angle = function angle(p1, p2) {
1428
+ var a = p1[0] - p2[0],
1429
+ b = p1[1] - p2[1],
1430
+ c = hypothenuse(a, b);
1431
+ return Math.asin(b / c);
1432
+ };
1433
+ var endAngle = function endAngle(state) {
1434
+ return function (id) {
1435
+ var arc = state.arcs[id];
1436
+ if (arc === undefined) {
1437
+ return undefined;
1438
+ }
1439
+ var sAngle = angle(arc.p1, arc.p2),
1440
+ uAngle = -sAngle + Math.PI / 2;
1441
+ if (arc.p1[0] > arc.p2[0]) {
1442
+ uAngle = 2 * Math.PI - uAngle;
1443
+ }
1444
+ return uAngle;
1445
+ };
1446
+ };
1447
+ var startAngle = function startAngle(state) {
1448
+ return function (id) {
1449
+ var arc = state.arcs[id];
1450
+ if (arc === undefined) {
1451
+ return undefined;
1452
+ }
1453
+ var sAngle = angle(arc.p1, arc.p3),
1454
+ uAngle = -sAngle + Math.PI / 2;
1455
+ if (arc.p1[0] > arc.p3[0]) {
1456
+ uAngle = 2 * Math.PI - uAngle;
1457
+ }
1458
+ return uAngle;
1459
+ };
1460
+ };
1461
+ var length = function length(state) {
1462
+ return function (id) {
1463
+ var arc = state.arcs[id];
1464
+ if (arc === undefined) {
1465
+ return undefined;
1466
+ }
1467
+ var a = arc.p1[0] - arc.p2[0],
1468
+ b = arc.p1[1] - arc.p2[1];
1469
+ return hypothenuse(a, b);
1470
+ };
1471
+ };
1472
+ var install = function install(brushGroup, state, config, pc, events, xscale) {
1473
+ return function () {
1474
+ if (!pc.g()) {
1475
+ pc.createAxes();
1476
+ }
1477
+ var _drag = drag();
1478
+
1479
+ // Map of current arcs. arcs are stored per segment of the PC. A segment,
1480
+ // being the area between two axes. The left most area is indexed at 0.
1481
+ state.arcs.active = undefined;
1482
+ // Returns the width of the PC segment where currently a arc is being
1483
+ // placed. NOTE: even though they are evenly spaced in our current
1484
+ // implementation, we keep for when non-even spaced segments are supported as
1485
+ // well.
1486
+ state.arcs.width = function (id) {
1487
+ var arc = state.arcs[id];
1488
+ return arc === undefined ? undefined : arc.maxX - arc.minX;
1489
+ };
1490
+
1491
+ // returns angles in [0, 2 * PI]
1492
+ state.arcs.endAngle = endAngle(state);
1493
+ state.arcs.startAngle = startAngle(state);
1494
+ state.arcs.length = length(state);
1495
+ pc.on('axesreorder.arcs', function () {
1496
+ var ids = Object.getOwnPropertyNames(arcs).filter(function (d) {
1497
+ return !isNaN(d);
1498
+ });
1499
+ if (ids.length > 0) {
1500
+ // We have some arcs, which might need to be removed.
1501
+ ids.forEach(function (d) {
1502
+ var dims = arcs[d].dims;
1503
+ state.arcs.active = d;
1504
+ // If the two dimensions of the current arc are not next to each other
1505
+ // any more, than we'll need to remove the arc. Otherwise we keep it.
1506
+ if (!consecutive(dims)(dims.left, dims.right)) {
1507
+ removeStrum(state, pc);
1508
+ }
1509
+ });
1510
+ onDragEnd(brushGroup, state, config, pc, events)();
1511
+ }
1512
+ });
1513
+
1514
+ // Add a new svg group in which we draw the arcs.
1515
+ pc.selection.select('svg').append('g').attr('id', 'arcs').attr('transform', 'translate(' + config.margin.left + ',' + config.margin.top + ')');
1516
+
1517
+ // Install the required brushReset function
1518
+ pc.brushReset = brushReset$1(brushGroup, state, config, pc, events);
1519
+ _drag.on('start', onDragStart(state, config, pc, xscale)).on('drag', onDrag(brushGroup, state, config, pc, events)).on('end', onDragEnd(brushGroup, state, config, pc, events));
1520
+
1521
+ // NOTE: The styling needs to be done here and not in the css. This is because
1522
+ // for 1D brushing, the canvas layers should not listen to
1523
+ // pointer-events._.
1524
+ state.strumRect = pc.selection.select('svg').insert('rect', 'g#arcs').attr('id', 'arc-events').attr('x', config.margin.left).attr('y', config.margin.top).attr('width', w(config)).attr('height', h(config) + 2).style('opacity', 0).call(_drag);
1525
+ };
1526
+ };
1527
+
1528
+ var installAngularBrush = function installAngularBrush(brushGroup, config, pc, events, xscale) {
1529
+ var state = {
1530
+ arcs: {},
1531
+ strumRect: {}
1532
+ };
1533
+ brushGroup.modes['angular'] = {
1534
+ install: install(brushGroup, state, config, pc, events, xscale),
1535
+ uninstall: uninstall(state, pc),
1536
+ selected: selected$1(brushGroup, state, config),
1537
+ brushState: function brushState() {
1538
+ return state.arcs;
1539
+ }
1540
+ };
1541
+ };
1542
+
1543
+ // calculate 2d intersection of line a->b with line c->d
1544
+ // points are objects with x and y properties
1545
+ var intersection = function intersection(a, b, c, d) {
1546
+ return {
1547
+ x: ((a.x * b.y - a.y * b.x) * (c.x - d.x) - (a.x - b.x) * (c.x * d.y - c.y * d.x)) / ((a.x - b.x) * (c.y - d.y) - (a.y - b.y) * (c.x - d.x)),
1548
+ y: ((a.x * b.y - a.y * b.x) * (c.y - d.y) - (a.y - b.y) * (c.x * d.y - c.y * d.x)) / ((a.x - b.x) * (c.y - d.y) - (a.y - b.y) * (c.x - d.x))
1549
+ };
1550
+ };
1551
+
1552
+ // Merges the canvases and SVG elements into one canvas element which is then passed into the callback
1553
+ // (so you can choose to save it to disk, etc.)
1554
+ var mergeParcoords = function mergeParcoords(pc) {
1555
+ return function (callback) {
1556
+ // Retina display, etc.
1557
+ var devicePixelRatio = window.devicePixelRatio || 1;
1558
+
1559
+ // Create a canvas element to store the merged canvases
1560
+ var mergedCanvas = document.createElement('canvas');
1561
+ var foregroundCanvas = pc.canvas.foreground;
1562
+ // We will need to adjust for canvas margins to align the svg and canvas
1563
+ var canvasMarginLeft = Number(foregroundCanvas.style.marginLeft.replace('px', ''));
1564
+ var textTopAdjust = 15;
1565
+ var canvasMarginTop = Number(foregroundCanvas.style.marginTop.replace('px', '')) + textTopAdjust;
1566
+ var width = (foregroundCanvas.clientWidth + canvasMarginLeft) * devicePixelRatio;
1567
+ var height = (foregroundCanvas.clientHeight + canvasMarginTop) * devicePixelRatio;
1568
+ mergedCanvas.width = width + 50; // pad so that svg labels at right will not get cut off
1569
+ mergedCanvas.height = height + 30; // pad so that svg labels at bottom will not get cut off
1570
+ mergedCanvas.style.width = mergedCanvas.width / devicePixelRatio + 'px';
1571
+ mergedCanvas.style.height = mergedCanvas.height / devicePixelRatio + 'px';
1572
+
1573
+ // Give the canvas a white background
1574
+ var context = mergedCanvas.getContext('2d');
1575
+ context.fillStyle = '#ffffff';
1576
+ context.fillRect(0, 0, mergedCanvas.width, mergedCanvas.height);
1577
+
1578
+ // Merge all the canvases
1579
+ for (var key in pc.canvas) {
1580
+ context.drawImage(pc.canvas[key], canvasMarginLeft * devicePixelRatio, canvasMarginTop * devicePixelRatio, width - canvasMarginLeft * devicePixelRatio, height - canvasMarginTop * devicePixelRatio);
1581
+ }
1582
+ var serializer = new XMLSerializer();
1583
+ // axis labels are translated (0,-5) so we will clone the svg
1584
+ // and translate down so the labels are drawn on the canvas
1585
+ var svgNodeCopy = pc.selection.select('svg').node().cloneNode(true);
1586
+ svgNodeCopy.setAttribute('transform', 'translate(0,' + textTopAdjust + ')');
1587
+ svgNodeCopy.setAttribute('height', svgNodeCopy.getAttribute('height') + textTopAdjust);
1588
+ // text will need fill attribute since css styles will not get picked up
1589
+ // this is not sophisticated since it doesn't look up css styles
1590
+ // if the user changes
1591
+ select(svgNodeCopy).selectAll('text').attr('fill', 'black');
1592
+ var svgStr = serializer.serializeToString(svgNodeCopy);
1593
+
1594
+ // Create a Data URI.
1595
+ var src = 'data:image/svg+xml;base64,' + window.btoa(svgStr);
1596
+ var img = new Image();
1597
+ img.onload = function () {
1598
+ context.drawImage(img, 0, 0, img.width * devicePixelRatio, img.height * devicePixelRatio);
1599
+ if (typeof callback === 'function') {
1600
+ callback(mergedCanvas);
1601
+ }
1602
+ };
1603
+ img.src = src;
1604
+ };
1605
+ };
1606
+
1607
+ var selected = function selected(config, pc) {
1608
+ return function () {
1609
+ var actives = [];
1610
+ var extents = [];
1611
+ var ranges = {};
1612
+ //get brush selections from each node, convert to actual values
1613
+ //invert order of values in array to comply with the parcoords architecture
1614
+ if (config.brushes.length === 0) {
1615
+ var nodes = pc.g().selectAll('.brush').nodes();
1616
+ for (var k = 0; k < nodes.length; k++) {
1617
+ if (brushSelection(nodes[k]) !== null) {
1618
+ actives.push(nodes[k].__data__);
1619
+ var values = [];
1620
+ var ranger = brushSelection(nodes[k]);
1621
+ if (typeof config.dimensions[nodes[k].__data__].yscale.domain()[0] === 'number') {
1622
+ for (var i = 0; i < ranger.length; i++) {
1623
+ if (actives.includes(nodes[k].__data__) && config.flipAxes.includes(nodes[k].__data__)) {
1624
+ values.push(config.dimensions[nodes[k].__data__].yscale.invert(ranger[i]));
1625
+ } else if (config.dimensions[nodes[k].__data__].yscale() !== 1) {
1626
+ values.unshift(config.dimensions[nodes[k].__data__].yscale.invert(ranger[i]));
1627
+ }
1628
+ }
1629
+ extents.push(values);
1630
+ for (var ii = 0; ii < extents.length; ii++) {
1631
+ if (extents[ii].length === 0) {
1632
+ extents[ii] = [1, 1];
1633
+ }
1634
+ }
1635
+ } else {
1636
+ ranges[nodes[k].__data__] = brushSelection(nodes[k]);
1637
+ var dimRange = config.dimensions[nodes[k].__data__].yscale.range();
1638
+ config.dimensions[nodes[k].__data__].yscale.domain();
1639
+ for (var j = 0; j < dimRange.length; j++) {
1640
+ if (dimRange[j] >= ranger[0] && dimRange[j] <= ranger[1] && actives.includes(nodes[k].__data__) && config.flipAxes.includes(nodes[k].__data__)) {
1641
+ values.push(dimRange[j]);
1642
+ } else if (dimRange[j] >= ranger[0] && dimRange[j] <= ranger[1]) {
1643
+ values.unshift(dimRange[j]);
1644
+ }
1645
+ }
1646
+ extents.push(values);
1647
+ for (var _ii = 0; _ii < extents.length; _ii++) {
1648
+ if (extents[_ii].length === 0) {
1649
+ extents[_ii] = [1, 1];
1650
+ }
1651
+ }
1652
+ }
1653
+ }
1654
+ }
1655
+ // test if within range
1656
+ var within = {
1657
+ date: function date(d, p, dimension) {
1658
+ var category = d[p];
1659
+ var categoryIndex = config.dimensions[p].yscale.domain().indexOf(category);
1660
+ var categoryRangeValue = config.dimensions[p].yscale.range()[categoryIndex];
1661
+ return categoryRangeValue >= ranges[p][0] && categoryRangeValue <= ranges[p][1];
1662
+ },
1663
+ number: function number(d, p, dimension) {
1664
+ return extents[dimension][0] <= d[p] && d[p] <= extents[dimension][1];
1665
+ },
1666
+ string: function string(d, p, dimension) {
1667
+ var category = d[p];
1668
+ var categoryIndex = config.dimensions[p].yscale.domain().indexOf(category);
1669
+ var categoryRangeValue = config.dimensions[p].yscale.range()[categoryIndex];
1670
+ return categoryRangeValue >= ranges[p][0] && categoryRangeValue <= ranges[p][1];
1671
+ }
1672
+ };
1673
+ return config.data.filter(function (d) {
1674
+ return actives.every(function (p, dimension) {
1675
+ return within[config.dimensions[p].type](d, p, dimension);
1676
+ });
1677
+ });
1678
+ } else {
1679
+ // need to get data from each brush instead of each axis
1680
+ // first must find active axes by iterating through all brushes
1681
+ // then go through similiar process as above.
1682
+ var multiBrushData = [];
1683
+ var _loop = function _loop(idx) {
1684
+ var brush = config.brushes[idx];
1685
+ var values = [];
1686
+ var ranger = brush.extent;
1687
+ var actives = [brush.data];
1688
+ if (typeof config.dimensions[brush.data].yscale.domain()[0] === 'number') {
1689
+ for (var _i = 0; _i < ranger.length; _i++) {
1690
+ if (actives.includes(brush.data) && config.flipAxes.includes(brush.data)) {
1691
+ values.push(config.dimensions[brush.data].yscale.invert(ranger[_i]));
1692
+ } else if (config.dimensions[brush.data].yscale() !== 1) {
1693
+ values.unshift(config.dimensions[brush.data].yscale.invert(ranger[_i]));
1694
+ }
1695
+ }
1696
+ extents.push(values);
1697
+ for (var _ii2 = 0; _ii2 < extents.length; _ii2++) {
1698
+ if (extents[_ii2].length === 0) {
1699
+ extents[_ii2] = [1, 1];
1700
+ }
1701
+ }
1702
+ } else {
1703
+ ranges[brush.data] = brush.extent;
1704
+ var _dimRange = config.dimensions[brush.data].yscale.range();
1705
+ config.dimensions[brush.data].yscale.domain();
1706
+ for (var _j = 0; _j < _dimRange.length; _j++) {
1707
+ if (_dimRange[_j] >= ranger[0] && _dimRange[_j] <= ranger[1] && actives.includes(brush.data) && config.flipAxes.includes(brush.data)) {
1708
+ values.push(_dimRange[_j]);
1709
+ } else if (_dimRange[_j] >= ranger[0] && _dimRange[_j] <= ranger[1]) {
1710
+ values.unshift(_dimRange[_j]);
1711
+ }
1712
+ }
1713
+ extents.push(values);
1714
+ for (var _ii3 = 0; _ii3 < extents.length; _ii3++) {
1715
+ if (extents[_ii3].length === 0) {
1716
+ extents[_ii3] = [1, 1];
1717
+ }
1718
+ }
1719
+ }
1720
+ var within = {
1721
+ date: function date(d, p, dimension) {
1722
+ var category = d[p];
1723
+ var categoryIndex = config.dimensions[p].yscale.domain().indexOf(category);
1724
+ var categoryRangeValue = config.dimensions[p].yscale.range()[categoryIndex];
1725
+ return categoryRangeValue >= ranges[p][0] && categoryRangeValue <= ranges[p][1];
1726
+ },
1727
+ number: function number(d, p, dimension) {
1728
+ return extents[idx][0] <= d[p] && d[p] <= extents[idx][1];
1729
+ },
1730
+ string: function string(d, p, dimension) {
1731
+ var category = d[p];
1732
+ var categoryIndex = config.dimensions[p].yscale.domain().indexOf(category);
1733
+ var categoryRangeValue = config.dimensions[p].yscale.range()[categoryIndex];
1734
+ return categoryRangeValue >= ranges[p][0] && categoryRangeValue <= ranges[p][1];
1735
+ }
1736
+ };
1737
+
1738
+ // filter data, but instead of returning it now,
1739
+ // put it into multiBrush data which is returned after
1740
+ // all brushes are iterated through.
1741
+ var filtered = config.data.filter(function (d) {
1742
+ return actives.every(function (p, dimension) {
1743
+ return within[config.dimensions[p].type](d, p, dimension);
1744
+ });
1745
+ });
1746
+ for (var z = 0; z < filtered.length; z++) {
1747
+ multiBrushData.push(filtered[z]);
1748
+ }
1749
+ actives = [];
1750
+ ranges = {};
1751
+ };
1752
+ for (var idx = 0; idx < config.brushes.length; idx++) {
1753
+ _loop(idx);
1754
+ }
1755
+ return multiBrushData;
1756
+ }
1757
+ };
1758
+ };
1759
+
1760
+ var brushPredicate = function brushPredicate(brushGroup, config, pc) {
1761
+ return function () {
1762
+ var predicate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
1763
+ if (predicate === null) {
1764
+ return brushGroup.predicate;
1765
+ }
1766
+ predicate = String(predicate).toUpperCase();
1767
+ if (predicate !== 'AND' && predicate !== 'OR') {
1768
+ throw new Error('Invalid predicate ' + predicate);
1769
+ }
1770
+ brushGroup.predicate = predicate;
1771
+ config.brushed = brushGroup.currentMode().selected();
1772
+ pc.renderBrushed();
1773
+ return pc;
1774
+ };
1775
+ };
1776
+ var brushMode = function brushMode(brushGroup, config, pc) {
1777
+ return function () {
1778
+ var mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
1779
+ if (mode === null) {
1780
+ return brushGroup.mode;
1781
+ }
1782
+ if (pc.brushModes().indexOf(mode) === -1) {
1783
+ throw new Error('pc.brushmode: Unsupported brush mode: ' + mode);
1784
+ }
1785
+
1786
+ // Make sure that we don't trigger unnecessary events by checking if the mode
1787
+ // actually changes.
1788
+ if (mode !== brushGroup.mode) {
1789
+ // When changing brush modes, the first thing we need to do is clearing any
1790
+ // brushes from the current mode, if any.
1791
+ if (brushGroup.mode !== 'None') {
1792
+ pc.brushReset();
1793
+ }
1794
+
1795
+ // Next, we need to 'uninstall' the current brushMode.
1796
+ brushGroup.modes[brushGroup.mode].uninstall(pc);
1797
+ // Finally, we can install the requested one.
1798
+ brushGroup.mode = mode;
1799
+ brushGroup.modes[brushGroup.mode].install();
1800
+ if (mode === 'None') {
1801
+ delete pc.brushPredicate;
1802
+ } else {
1803
+ pc.brushPredicate = brushPredicate(brushGroup, config, pc);
1804
+ }
1805
+ }
1806
+ return pc;
1807
+ };
1808
+ };
1809
+
1810
+ /**
1811
+ * dimension display names
1812
+ *
1813
+ * @param config
1814
+ * @param d
1815
+ * @returns {*}
1816
+ */
1817
+ var dimensionLabels = function dimensionLabels(config) {
1818
+ return function (d) {
1819
+ return config.dimensions[d].title ? config.dimensions[d].title : d;
1820
+ };
1821
+ };
1822
+
1823
+ var flipAxisAndUpdatePCP = function flipAxisAndUpdatePCP(config, pc, axis) {
1824
+ return function (dimension) {
1825
+ pc.flip(dimension);
1826
+ pc.brushReset(dimension);
1827
+
1828
+ // select(this.parentElement)
1829
+ pc.selection.select('svg').selectAll('g.axis').filter(function (d) {
1830
+ return d === dimension;
1831
+ }).transition().duration(config.animationTime).call(axis.scale(config.dimensions[dimension].yscale));
1832
+ pc.render();
1833
+ };
1834
+ };
1835
+
1836
+ var rotateLabels = function rotateLabels(config, pc) {
1837
+ return function (event) {
1838
+ if (!config.rotateLabels) return;
1839
+ var delta = event.deltaY;
1840
+ delta = delta < 0 ? -5 : delta;
1841
+ delta = delta > 0 ? 5 : delta;
1842
+ config.dimensionTitleRotation += delta;
1843
+ pc.svg.selectAll('text.label').attr('transform', 'translate(0,-5) rotate(' + config.dimensionTitleRotation + ')');
1844
+ event.preventDefault();
1845
+ };
1846
+ };
1847
+
1848
+ var _this$3 = undefined;
1849
+ var updateAxes = function updateAxes(config, pc, position, axis, flags) {
1850
+ return function () {
1851
+ var animationTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
1852
+ if (animationTime === null) {
1853
+ animationTime = config.animationTime;
1854
+ }
1855
+ var g_data = pc.svg.selectAll('.dimension').data(pc.getOrderedDimensionKeys());
1856
+ // Enter
1857
+ g_data.enter().append('svg:g').attr('class', 'dimension').attr('transform', function (p) {
1858
+ return 'translate(' + position(p) + ')';
1859
+ }).style('opacity', 0).append('svg:g').attr('class', 'axis').attr('transform', 'translate(0,0)').each(function (d) {
1860
+ var axisElement = select(this).call(pc.applyAxisConfig(axis, config.dimensions[d]));
1861
+ axisElement.selectAll('path').style('fill', 'none').style('stroke', '#222').style('shape-rendering', 'crispEdges');
1862
+ axisElement.selectAll('line').style('fill', 'none').style('stroke', '#222').style('shape-rendering', 'crispEdges');
1863
+ }).append('svg:text').attr('text-anchor', 'middle').attr('class', 'label').attr('x', 0).attr('y', 0).attr('transform', 'translate(0,-5) rotate(' + config.dimensionTitleRotation + ')').text(dimensionLabels(config)).on('dblclick', flipAxisAndUpdatePCP(config, pc, axis)).on('wheel', rotateLabels(config, pc));
1864
+
1865
+ // Update
1866
+ g_data.attr('opacity', 0);
1867
+ g_data.select('.axis').transition().duration(animationTime).each(function (d) {
1868
+ select(this).call(pc.applyAxisConfig(axis, config.dimensions[d]));
1869
+ });
1870
+ g_data.select('.label').transition().duration(animationTime).text(dimensionLabels(config)).attr('transform', 'translate(0,-5) rotate(' + config.dimensionTitleRotation + ')');
1871
+
1872
+ // Exit
1873
+ g_data.exit().remove();
1874
+ var g = pc.svg.selectAll('.dimension');
1875
+ g.transition().duration(animationTime).attr('transform', function (p) {
1876
+ return 'translate(' + position(p) + ')';
1877
+ }).style('opacity', 1);
1878
+ pc.svg.selectAll('.axis').transition().duration(animationTime).each(function (d) {
1879
+ select(this).call(pc.applyAxisConfig(axis, config.dimensions[d]));
1880
+ });
1881
+ if (flags.brushable) pc.brushable();
1882
+ if (flags.reorderable) pc.reorderable();
1883
+ if (pc.brushMode() !== 'None') {
1884
+ var mode = pc.brushMode();
1885
+ pc.brushMode('None');
1886
+ pc.brushMode(mode);
1887
+ }
1888
+ return _this$3;
1889
+ };
1890
+ };
1891
+
1892
+ /** adjusts an axis' default range [h()+1, 1] if a NullValueSeparator is set */
1893
+ var getRange = function getRange(config) {
1894
+ var h = config.height - config.margin.top - config.margin.bottom;
1895
+ if (config.nullValueSeparator == 'bottom') {
1896
+ return [h + 1 - config.nullValueSeparatorPadding.bottom - config.nullValueSeparatorPadding.top, 1];
1897
+ } else if (config.nullValueSeparator == 'top') {
1898
+ return [h + 1, 1 + config.nullValueSeparatorPadding.bottom + config.nullValueSeparatorPadding.top];
1899
+ }
1900
+ return [h + 1, 1];
1901
+ };
1902
+
1903
+ var autoscale = function autoscale(config, pc, xscale, ctx) {
1904
+ return function () {
1905
+ // yscale
1906
+ var defaultScales = {
1907
+ date: function date(k) {
1908
+ var _extent = extent(config.data, function (d) {
1909
+ return d[k] ? d[k].getTime() : null;
1910
+ });
1911
+ // special case if single value
1912
+ if (_extent[0] === _extent[1]) {
1913
+ return scalePoint().domain(_extent).range(getRange(config));
1914
+ }
1915
+ if (config.flipAxes.includes(k)) {
1916
+ _extent = _extent.map(function (val) {
1917
+ return tempDate.unshift(val);
1918
+ });
1919
+ }
1920
+ return scaleTime().domain(_extent).range(getRange(config));
1921
+ },
1922
+ number: function number(k) {
1923
+ var _extent = extent(config.data, function (d) {
1924
+ return +d[k];
1925
+ });
1926
+ // special case if single value
1927
+ if (_extent[0] === _extent[1]) {
1928
+ return scalePoint().domain(_extent).range(getRange(config));
1929
+ }
1930
+ if (config.flipAxes.includes(k)) {
1931
+ _extent = _extent.map(function (val) {
1932
+ return tempDate.unshift(val);
1933
+ });
1934
+ }
1935
+ return scaleLinear().domain(_extent).range(getRange(config));
1936
+ },
1937
+ string: function string(k) {
1938
+ var counts = {},
1939
+ domain = [];
1940
+ // Let's get the count for each value so that we can sort the domain based
1941
+ // on the number of items for each value.
1942
+ config.data.map(function (p) {
1943
+ if (p[k] === undefined && config.nullValueSeparator !== 'undefined') {
1944
+ return null; // null values will be drawn beyond the horizontal null value separator!
1945
+ }
1946
+ if (counts[p[k]] === undefined) {
1947
+ counts[p[k]] = 1;
1948
+ } else {
1949
+ counts[p[k]] = counts[p[k]] + 1;
1950
+ }
1951
+ });
1952
+ if (config.flipAxes.includes(k)) {
1953
+ domain = Object.getOwnPropertyNames(counts).sort();
1954
+ } else {
1955
+ var tempArr = Object.getOwnPropertyNames(counts).sort();
1956
+ for (var i = 0; i < Object.getOwnPropertyNames(counts).length; i++) {
1957
+ domain.push(tempArr.pop());
1958
+ }
1959
+ }
1960
+
1961
+ //need to create an ordinal scale for categorical data
1962
+ var categoricalRange = [];
1963
+ if (domain.length === 1) {
1964
+ //edge case
1965
+ domain = [' ', domain[0], ' '];
1966
+ }
1967
+ var addBy = getRange(config)[0] / (domain.length - 1);
1968
+ for (var j = 0; j < domain.length; j++) {
1969
+ if (categoricalRange.length === 0) {
1970
+ categoricalRange.push(0);
1971
+ continue;
1972
+ }
1973
+ categoricalRange.push(categoricalRange[j - 1] + addBy);
1974
+ }
1975
+ return scaleOrdinal().domain(domain).range(categoricalRange);
1976
+ }
1977
+ };
1978
+ Object.keys(config.dimensions).forEach(function (k) {
1979
+ if (config.dimensions[k].yscale === undefined || config.dimensions[k].yscale === null) {
1980
+ config.dimensions[k].yscale = defaultScales[config.dimensions[k].type](k);
1981
+ }
1982
+ });
1983
+
1984
+ // xscale
1985
+ // add padding for d3 >= v4 default 0.2
1986
+ xscale.range([0, w(config)]).padding(0.2);
1987
+
1988
+ // Retina display, etc.
1989
+ var devicePixelRatio = window.devicePixelRatio || 1;
1990
+
1991
+ // canvas sizes
1992
+ pc.selection.selectAll('canvas').style('margin-top', config.margin.top + 'px').style('margin-left', config.margin.left + 'px').style('width', w(config) + 2 + 'px').style('height', h(config) + 2 + 'px').attr('width', (w(config) + 2) * devicePixelRatio).attr('height', (h(config) + 2) * devicePixelRatio);
1993
+ // default styles, needs to be set when canvas width changes
1994
+ ctx.foreground.strokeStyle = config.color;
1995
+ ctx.foreground.lineWidth = config.lineWidth;
1996
+ ctx.foreground.globalCompositeOperation = config.composite;
1997
+ ctx.foreground.globalAlpha = config.alpha;
1998
+ ctx.foreground.scale(devicePixelRatio, devicePixelRatio);
1999
+ ctx.brushed.strokeStyle = config.brushedColor;
2000
+ ctx.brushed.lineWidth = config.lineWidth;
2001
+ ctx.brushed.globalCompositeOperation = config.composite;
2002
+ ctx.brushed.globalAlpha = config.alpha;
2003
+ ctx.brushed.scale(devicePixelRatio, devicePixelRatio);
2004
+ ctx.highlight.lineWidth = config.highlightedLineWidth;
2005
+ ctx.highlight.scale(devicePixelRatio, devicePixelRatio);
2006
+ ctx.marked.lineWidth = config.markedLineWidth;
2007
+ ctx.marked.shadowColor = config.markedShadowColor;
2008
+ ctx.marked.shadowBlur = config.markedShadowBlur;
2009
+ ctx.marked.scale(devicePixelRatio, devicePixelRatio);
2010
+ return this;
2011
+ };
2012
+ };
2013
+
2014
+ var brushable = function brushable(config, pc, flags) {
2015
+ return function () {
2016
+ if (!pc.g()) {
2017
+ pc.createAxes();
2018
+ }
2019
+ var g = pc.g();
2020
+
2021
+ // Add and store a brush for each axis.
2022
+ g.append('svg:g').attr('class', 'brush').each(function (d) {
2023
+ if (config.dimensions[d] !== undefined) {
2024
+ config.dimensions[d]['brush'] = brushY(select(this)).extent([[-15, 0], [15, config.dimensions[d].yscale.range()[0]]]);
2025
+ select(this).call(config.dimensions[d]['brush'].on('start', function (event) {
2026
+ if (event.sourceEvent !== null && !event.sourceEvent.ctrlKey) {
2027
+ pc.brushReset();
2028
+ }
2029
+ }).on('brush', function (event) {
2030
+ if (!event.sourceEvent.ctrlKey) {
2031
+ pc.brush();
2032
+ }
2033
+ }).on('end', function (event) {
2034
+ // save brush selection is ctrl key is held
2035
+ // store important brush information and
2036
+ // the html element of the selection,
2037
+ // to make a dummy selection element
2038
+ if (event.sourceEvent.ctrlKey) {
2039
+ var html = select(this).select('.selection').nodes()[0].outerHTML;
2040
+ html = html.replace('class="selection"', 'class="selection dummy' + ' selection-' + config.brushes.length + '"');
2041
+ var dat = select(this).nodes()[0].__data__;
2042
+ var brush = {
2043
+ id: config.brushes.length,
2044
+ extent: brushSelection(this),
2045
+ html: html,
2046
+ data: dat
2047
+ };
2048
+ config.brushes.push(brush);
2049
+ select(select(this).nodes()[0].parentNode).select('.axis').nodes()[0].outerHTML += html;
2050
+ pc.brush();
2051
+ config.dimensions[d].brush.move(select(this, null));
2052
+ select(this).select('.selection').attr('style', 'display:none');
2053
+ pc.brushable();
2054
+ } else {
2055
+ pc.brush();
2056
+ }
2057
+ }));
2058
+ select(this).on('dblclick', function () {
2059
+ pc.brushReset(d);
2060
+ });
2061
+ }
2062
+ });
2063
+ flags.brushable = true;
2064
+ return this;
2065
+ };
2066
+ };
2067
+
2068
+ var commonScale = function commonScale(config, pc) {
2069
+ return function (global, type) {
2070
+ var t = type || 'number';
2071
+ if (typeof global === 'undefined') {
2072
+ global = true;
2073
+ }
2074
+
2075
+ // try to autodetect dimensions and create scales
2076
+ if (!Object.keys(config.dimensions).length) {
2077
+ pc.detectDimensions();
2078
+ }
2079
+ pc.autoscale();
2080
+
2081
+ // scales of the same type
2082
+ var scales = Object.keys(config.dimensions).filter(function (p) {
2083
+ return config.dimensions[p].type == t;
2084
+ });
2085
+ if (global) {
2086
+ var _extent = extent(scales.map(function (d) {
2087
+ return config.dimensions[d].yscale.domain();
2088
+ }).reduce(function (cur, acc) {
2089
+ return cur.concat(acc);
2090
+ }));
2091
+ scales.forEach(function (d) {
2092
+ config.dimensions[d].yscale.domain(_extent);
2093
+ });
2094
+ } else {
2095
+ scales.forEach(function (d) {
2096
+ config.dimensions[d].yscale.domain(extent(config.data, function (d) {
2097
+ return +d[k];
2098
+ }));
2099
+ });
2100
+ }
2101
+
2102
+ // update centroids
2103
+ if (config.bundleDimension !== null) {
2104
+ pc.bundleDimension(config.bundleDimension);
2105
+ }
2106
+ return this;
2107
+ };
2108
+ };
2109
+
2110
+ var computeRealCentroids = function computeRealCentroids(config, position) {
2111
+ return function (row) {
2112
+ return Object.keys(config.dimensions).map(function (d) {
2113
+ var x = position(d);
2114
+ var y = config.dimensions[d].yscale(row[d]);
2115
+ return [x, y];
2116
+ });
2117
+ };
2118
+ };
2119
+
2120
+ var isValid = function isValid(d) {
2121
+ return d !== null && d !== undefined;
2122
+ };
2123
+ var applyDimensionDefaults = function applyDimensionDefaults(config, pc) {
2124
+ return function (dims) {
2125
+ var types = pc.detectDimensionTypes(config.data);
2126
+ dims = dims ? dims : Object.keys(types);
2127
+ return dims.reduce(function (acc, cur, i) {
2128
+ var k = config.dimensions[cur] ? config.dimensions[cur] : {};
2129
+ acc[cur] = _objectSpread2(_objectSpread2({}, k), {}, {
2130
+ orient: isValid(k.orient) ? k.orient : 'left',
2131
+ ticks: isValid(k.ticks) ? k.ticks : 5,
2132
+ innerTickSize: isValid(k.innerTickSize) ? k.innerTickSize : 6,
2133
+ outerTickSize: isValid(k.outerTickSize) ? k.outerTickSize : 0,
2134
+ tickPadding: isValid(k.tickPadding) ? k.tickPadding : 3,
2135
+ type: isValid(k.type) ? k.type : types[cur],
2136
+ index: isValid(k.index) ? k.index : i
2137
+ });
2138
+ return acc;
2139
+ }, {});
2140
+ };
2141
+ };
2142
+
2143
+ /**
2144
+ * Create static SVG axes with dimension names, ticks, and labels.
2145
+ *
2146
+ * @param config
2147
+ * @param pc
2148
+ * @param xscale
2149
+ * @param flags
2150
+ * @param axis
2151
+ * @returns {Function}
2152
+ */
2153
+ var createAxes = function createAxes(config, pc, xscale, flags, axis) {
2154
+ return function () {
2155
+ if (pc.g() !== undefined) {
2156
+ pc.removeAxes();
2157
+ }
2158
+ // Add a group element for each dimension.
2159
+ pc._g = pc.svg.selectAll('.dimension').data(pc.getOrderedDimensionKeys(), function (d) {
2160
+ return d;
2161
+ }).enter().append('svg:g').attr('class', 'dimension').attr('transform', function (d) {
2162
+ return 'translate(' + xscale(d) + ')';
2163
+ });
2164
+ // Add an axis and title.
2165
+ pc._g.append('svg:g').attr('class', 'axis').attr('transform', 'translate(0,0)').each(function (d) {
2166
+ var axisElement = select(this).call(pc.applyAxisConfig(axis, config.dimensions[d]));
2167
+ axisElement.selectAll('path').style('fill', 'none').style('stroke', '#222').style('shape-rendering', 'crispEdges');
2168
+ axisElement.selectAll('line').style('fill', 'none').style('stroke', '#222').style('shape-rendering', 'crispEdges');
2169
+ }).append('svg:text').attr('text-anchor', 'middle').attr('y', 0).attr('transform', 'translate(0,-5) rotate(' + config.dimensionTitleRotation + ')').attr('x', 0).attr('class', 'label').text(dimensionLabels(config)).on('dblclick', flipAxisAndUpdatePCP(config, pc, axis)).on('wheel', rotateLabels(config, pc));
2170
+ if (config.nullValueSeparator === 'top') {
2171
+ pc.svg.append('line').attr('x1', 0).attr('y1', 1 + config.nullValueSeparatorPadding.top).attr('x2', w(config)).attr('y2', 1 + config.nullValueSeparatorPadding.top).attr('stroke-width', 1).attr('stroke', '#777').attr('fill', 'none').attr('shape-rendering', 'crispEdges');
2172
+ } else if (config.nullValueSeparator === 'bottom') {
2173
+ pc.svg.append('line').attr('x1', 0).attr('y1', h(config) + 1 - config.nullValueSeparatorPadding.bottom).attr('x2', w(config)).attr('y2', h(config) + 1 - config.nullValueSeparatorPadding.bottom).attr('stroke-width', 1).attr('stroke', '#777').attr('fill', 'none').attr('shape-rendering', 'crispEdges');
2174
+ }
2175
+ flags.axes = true;
2176
+ return this;
2177
+ };
2178
+ };
2179
+
2180
+ var _this$2 = undefined;
2181
+
2182
+ //draw dots with radius r on the axis line where data intersects
2183
+ var axisDots = function axisDots(config, pc, position) {
2184
+ return function (_r) {
2185
+ var r = _r || 0.1;
2186
+ var ctx = pc.ctx.dots;
2187
+ var startAngle = 0;
2188
+ var endAngle = 2 * Math.PI;
2189
+ ctx.globalAlpha = min([1 / Math.pow(config.data.length, 1 / 2), 1]);
2190
+ config.data.forEach(function (d) {
2191
+ Object.entries(config.dimensions).forEach(function (_ref, i) {
2192
+ var _ref2 = _slicedToArray(_ref, 1),
2193
+ key = _ref2[0];
2194
+ ctx.beginPath();
2195
+ ctx.arc(position(key), config.dimensions[key].yscale(d[key]), r, startAngle, endAngle);
2196
+ ctx.stroke();
2197
+ ctx.fill();
2198
+ });
2199
+ });
2200
+ return _this$2;
2201
+ };
2202
+ };
2203
+
2204
+ var applyAxisConfig = function applyAxisConfig(axis, dimension) {
2205
+ var axisCfg;
2206
+ switch (dimension.orient) {
2207
+ case 'left':
2208
+ axisCfg = axisLeft(dimension.yscale);
2209
+ break;
2210
+ case 'right':
2211
+ axisCfg = axisRight(dimension.yscale);
2212
+ break;
2213
+ case 'top':
2214
+ axisCfg = axisTop(dimension.yscale);
2215
+ break;
2216
+ case 'bottom':
2217
+ axisCfg = axisBottom(dimension.yscale);
2218
+ break;
2219
+ default:
2220
+ axisCfg = axisLeft(dimension.yscale);
2221
+ break;
2222
+ }
2223
+ axisCfg.ticks(dimension.ticks).tickValues(dimension.tickValues).tickSizeInner(dimension.innerTickSize).tickSizeOuter(dimension.outerTickSize).tickPadding(dimension.tickPadding).tickFormat(dimension.tickFormat);
2224
+ return axisCfg;
2225
+ };
2226
+
2227
+ // Jason Davies, http://bl.ocks.org/1341281
2228
+ var reorderable = function reorderable(config, pc, xscale, position, dragging, flags) {
2229
+ return function () {
2230
+ if (pc.g() === undefined) pc.createAxes();
2231
+ var g = pc.g();
2232
+ g.style('cursor', 'move').call(drag().on('start', function (event, d) {
2233
+ dragging[d] = this.__origin__ = xscale(d);
2234
+ }).on('drag', function (event, d) {
2235
+ dragging[d] = Math.min(w(config), Math.max(0, this.__origin__ += event.dx));
2236
+ pc.sortDimensions();
2237
+ xscale.domain(pc.getOrderedDimensionKeys());
2238
+ pc.render();
2239
+ g.attr('transform', function (d) {
2240
+ return 'translate(' + position(d) + ')';
2241
+ });
2242
+ }).on('end', function (event, d) {
2243
+ delete this.__origin__;
2244
+ delete dragging[d];
2245
+ select(this).transition().attr('transform', 'translate(' + xscale(d) + ')');
2246
+ pc.render();
2247
+ pc.renderMarked();
2248
+ }));
2249
+ flags.reorderable = true;
2250
+ return this;
2251
+ };
2252
+ };
2253
+
2254
+ // rescale for height, width and margins
2255
+ // TODO currently assumes chart is brushable, and destroys old brushes
2256
+ var resize = function resize(config, pc, flags, events) {
2257
+ return function () {
2258
+ // selection size
2259
+ pc.selection.select('svg').attr('width', config.width).attr('height', config.height);
2260
+ pc.svg.attr('transform', 'translate(' + config.margin.left + ',' + config.margin.top + ')');
2261
+
2262
+ // FIXME: the current brush state should pass through
2263
+ if (flags.brushable) pc.brushReset();
2264
+
2265
+ // scales
2266
+ pc.autoscale();
2267
+
2268
+ // axes, destroys old brushes.
2269
+ if (pc.g()) pc.createAxes();
2270
+ if (flags.brushable) pc.brushable();
2271
+ if (flags.reorderable) pc.reorderable();
2272
+ events.call('resize', this, {
2273
+ width: config.width,
2274
+ height: config.height,
2275
+ margin: config.margin
2276
+ });
2277
+ return this;
2278
+ };
2279
+ };
2280
+
2281
+ // Reorder dimensions, such that the highest value (visually) is on the left and
2282
+ // the lowest on the right. Visual values are determined by the data values in
2283
+ // the given row.
2284
+ var reorder = function reorder(config, pc, xscale) {
2285
+ return function (rowdata) {
2286
+ var firstDim = pc.getOrderedDimensionKeys()[0];
2287
+ pc.sortDimensionsByRowData(rowdata);
2288
+ // NOTE: this is relatively cheap given that:
2289
+ // number of dimensions < number of data items
2290
+ // Thus we check equality of order to prevent rerendering when this is the case.
2291
+ var reordered = firstDim !== pc.getOrderedDimensionKeys()[0];
2292
+ if (reordered) {
2293
+ xscale.domain(pc.getOrderedDimensionKeys());
2294
+ var highlighted = config.highlighted.slice(0);
2295
+ pc.unhighlight();
2296
+ var marked = config.marked.slice(0);
2297
+ pc.unmark();
2298
+ var g = pc.g();
2299
+ g.transition().duration(1500).attr('transform', function (d) {
2300
+ return 'translate(' + xscale(d) + ')';
2301
+ });
2302
+ pc.render();
2303
+
2304
+ // pc.highlight() does not check whether highlighted is length zero, so we do that here.
2305
+ if (highlighted.length !== 0) {
2306
+ pc.highlight(highlighted);
2307
+ }
2308
+ if (marked.length !== 0) {
2309
+ pc.mark(marked);
2310
+ }
2311
+ }
2312
+ };
2313
+ };
2314
+
2315
+ var sortDimensions = function sortDimensions(config, position) {
2316
+ return function () {
2317
+ var copy = Object.assign({}, config.dimensions);
2318
+ var positionSortedKeys = Object.keys(config.dimensions).sort(function (a, b) {
2319
+ return position(a) - position(b) === 0 ? 1 : position(a) - position(b);
2320
+ });
2321
+ config.dimensions = {};
2322
+ positionSortedKeys.forEach(function (p, i) {
2323
+ config.dimensions[p] = copy[p];
2324
+ config.dimensions[p].index = i;
2325
+ });
2326
+ };
2327
+ };
2328
+
2329
+ var sortDimensionsByRowData = function sortDimensionsByRowData(config) {
2330
+ return function (rowdata) {
2331
+ var copy = Object.assign({}, config.dimensions);
2332
+ var positionSortedKeys = Object.keys(config.dimensions).sort(function (a, b) {
2333
+ var pixelDifference = config.dimensions[a].yscale(rowdata[a]) - config.dimensions[b].yscale(rowdata[b]);
2334
+
2335
+ // Array.sort is not necessarily stable, this means that if pixelDifference is zero
2336
+ // the ordering of dimensions might change unexpectedly. This is solved by sorting on
2337
+ // variable name in that case.
2338
+ return pixelDifference === 0 ? a.localeCompare(b) : pixelDifference;
2339
+ });
2340
+ config.dimensions = {};
2341
+ positionSortedKeys.forEach(function (p, i) {
2342
+ config.dimensions[p] = copy[p];
2343
+ config.dimensions[p].index = i;
2344
+ });
2345
+ };
2346
+ };
2347
+
2348
+ var isBrushed = function isBrushed(config, brushGroup) {
2349
+ if (config.brushed && config.brushed.length !== config.data.length) return true;
2350
+ var object = brushGroup.currentMode().brushState();
2351
+ for (var key in object) {
2352
+ if (object.hasOwnProperty(key)) {
2353
+ return true;
2354
+ }
2355
+ }
2356
+ return false;
2357
+ };
2358
+
2359
+ var clear = function clear(config, pc, ctx, brushGroup) {
2360
+ return function (layer) {
2361
+ ctx[layer].clearRect(0, 0, w(config) + 2, h(config) + 2);
2362
+
2363
+ // This will make sure that the foreground items are transparent
2364
+ // without the need for changing the opacity style of the foreground canvas
2365
+ // as this would stop the css styling from working
2366
+ if (layer === 'brushed' && isBrushed(config, brushGroup)) {
2367
+ ctx.brushed.fillStyle = pc.selection.style('background-color');
2368
+ ctx.brushed.globalAlpha = 1 - config.alphaOnBrushed;
2369
+ ctx.brushed.fillRect(0, 0, w(config) + 2, h(config) + 2);
2370
+ ctx.brushed.globalAlpha = config.alpha;
2371
+ }
2372
+ return this;
2373
+ };
2374
+ };
2375
+
2376
+ const PRECISION = 1e-6;
2377
+
2378
+ class Matrix {
2379
+ constructor(elements) {
2380
+ this.setElements(elements);
2381
+ }
2382
+ e(i, j) {
2383
+ if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) {
2384
+ return null;
2385
+ }
2386
+ return this.elements[i - 1][j - 1];
2387
+ }
2388
+ row(i) {
2389
+ if (i > this.elements.length) {
2390
+ return null;
2391
+ }
2392
+ return new Vector(this.elements[i - 1]);
2393
+ }
2394
+ col(j) {
2395
+ if (this.elements.length === 0) {
2396
+ return null;
2397
+ }
2398
+ if (j > this.elements[0].length) {
2399
+ return null;
2400
+ }
2401
+ var col = [],
2402
+ n = this.elements.length;
2403
+ for (var i = 0; i < n; i++) {
2404
+ col.push(this.elements[i][j - 1]);
2405
+ }
2406
+ return new Vector(col);
2407
+ }
2408
+ dimensions() {
2409
+ var cols = this.elements.length === 0 ? 0 : this.elements[0].length;
2410
+ return {
2411
+ rows: this.elements.length,
2412
+ cols: cols
2413
+ };
2414
+ }
2415
+ rows() {
2416
+ return this.elements.length;
2417
+ }
2418
+ cols() {
2419
+ if (this.elements.length === 0) {
2420
+ return 0;
2421
+ }
2422
+ return this.elements[0].length;
2423
+ }
2424
+ eql(matrix) {
2425
+ var M = matrix.elements || matrix;
2426
+ if (!M[0] || typeof M[0][0] === 'undefined') {
2427
+ M = new Matrix(M).elements;
2428
+ }
2429
+ if (this.elements.length === 0 || M.length === 0) {
2430
+ return this.elements.length === M.length;
2431
+ }
2432
+ if (this.elements.length !== M.length) {
2433
+ return false;
2434
+ }
2435
+ if (this.elements[0].length !== M[0].length) {
2436
+ return false;
2437
+ }
2438
+ var i = this.elements.length,
2439
+ nj = this.elements[0].length,
2440
+ j;
2441
+ while (i--) {
2442
+ j = nj;
2443
+ while (j--) {
2444
+ if (Math.abs(this.elements[i][j] - M[i][j]) > PRECISION) {
2445
+ return false;
2446
+ }
2447
+ }
2448
+ }
2449
+ return true;
2450
+ }
2451
+ dup() {
2452
+ return new Matrix(this.elements);
2453
+ }
2454
+ map(fn, context) {
2455
+ if (this.elements.length === 0) {
2456
+ return new Matrix([]);
2457
+ }
2458
+ var els = [],
2459
+ i = this.elements.length,
2460
+ nj = this.elements[0].length,
2461
+ j;
2462
+ while (i--) {
2463
+ j = nj;
2464
+ els[i] = [];
2465
+ while (j--) {
2466
+ els[i][j] = fn.call(context, this.elements[i][j], i + 1, j + 1);
2467
+ }
2468
+ }
2469
+ return new Matrix(els);
2470
+ }
2471
+ isSameSizeAs(matrix) {
2472
+ var M = matrix.elements || matrix;
2473
+ if (typeof M[0][0] === 'undefined') {
2474
+ M = new Matrix(M).elements;
2475
+ }
2476
+ if (this.elements.length === 0) {
2477
+ return M.length === 0;
2478
+ }
2479
+ return this.elements.length === M.length && this.elements[0].length === M[0].length;
2480
+ }
2481
+ add(matrix) {
2482
+ if (this.elements.length === 0) {
2483
+ return this.map(function (x) {
2484
+ return x;
2485
+ });
2486
+ }
2487
+ var M = matrix.elements || matrix;
2488
+ if (typeof M[0][0] === 'undefined') {
2489
+ M = new Matrix(M).elements;
2490
+ }
2491
+ if (!this.isSameSizeAs(M)) {
2492
+ return null;
2493
+ }
2494
+ return this.map(function (x, i, j) {
2495
+ return x + M[i - 1][j - 1];
2496
+ });
2497
+ }
2498
+ subtract(matrix) {
2499
+ if (this.elements.length === 0) {
2500
+ return this.map(function (x) {
2501
+ return x;
2502
+ });
2503
+ }
2504
+ var M = matrix.elements || matrix;
2505
+ if (typeof M[0][0] === 'undefined') {
2506
+ M = new Matrix(M).elements;
2507
+ }
2508
+ if (!this.isSameSizeAs(M)) {
2509
+ return null;
2510
+ }
2511
+ return this.map(function (x, i, j) {
2512
+ return x - M[i - 1][j - 1];
2513
+ });
2514
+ }
2515
+ canMultiplyFromLeft(matrix) {
2516
+ if (this.elements.length === 0) {
2517
+ return false;
2518
+ }
2519
+ var M = matrix.elements || matrix;
2520
+ if (typeof M[0][0] === 'undefined') {
2521
+ M = new Matrix(M).elements;
2522
+ }
2523
+ // this.columns should equal matrix.rows
2524
+ return this.elements[0].length === M.length;
2525
+ }
2526
+ multiply(matrix) {
2527
+ if (this.elements.length === 0) {
2528
+ return null;
2529
+ }
2530
+ if (!matrix.elements) {
2531
+ return this.map(function (x) {
2532
+ return x * matrix;
2533
+ });
2534
+ }
2535
+ var returnVector = matrix.modulus ? true : false;
2536
+ var M = matrix.elements || matrix;
2537
+ if (typeof M[0][0] === 'undefined') {
2538
+ M = new Matrix(M).elements;
2539
+ }
2540
+ if (!this.canMultiplyFromLeft(M)) {
2541
+ return null;
2542
+ }
2543
+ var i = this.elements.length,
2544
+ nj = M[0].length,
2545
+ j;
2546
+ var cols = this.elements[0].length,
2547
+ c,
2548
+ elements = [],
2549
+ sum;
2550
+ while (i--) {
2551
+ j = nj;
2552
+ elements[i] = [];
2553
+ while (j--) {
2554
+ c = cols;
2555
+ sum = 0;
2556
+ while (c--) {
2557
+ sum += this.elements[i][c] * M[c][j];
2558
+ }
2559
+ elements[i][j] = sum;
2560
+ }
2561
+ }
2562
+ var M = new Matrix(elements);
2563
+ return returnVector ? M.col(1) : M;
2564
+ }
2565
+ minor(a, b, c, d) {
2566
+ if (this.elements.length === 0) {
2567
+ return null;
2568
+ }
2569
+ var elements = [],
2570
+ ni = c,
2571
+ i,
2572
+ nj,
2573
+ j;
2574
+ var rows = this.elements.length,
2575
+ cols = this.elements[0].length;
2576
+ while (ni--) {
2577
+ i = c - ni - 1;
2578
+ elements[i] = [];
2579
+ nj = d;
2580
+ while (nj--) {
2581
+ j = d - nj - 1;
2582
+ elements[i][j] = this.elements[(a + i - 1) % rows][(b + j - 1) % cols];
2583
+ }
2584
+ }
2585
+ return new Matrix(elements);
2586
+ }
2587
+ transpose() {
2588
+ if (this.elements.length === 0) {
2589
+ return new Matrix([]);
2590
+ }
2591
+ var rows = this.elements.length,
2592
+ i,
2593
+ cols = this.elements[0].length,
2594
+ j;
2595
+ var elements = [],
2596
+ i = cols;
2597
+ while (i--) {
2598
+ j = rows;
2599
+ elements[i] = [];
2600
+ while (j--) {
2601
+ elements[i][j] = this.elements[j][i];
2602
+ }
2603
+ }
2604
+ return new Matrix(elements);
2605
+ }
2606
+ isSquare() {
2607
+ var cols = this.elements.length === 0 ? 0 : this.elements[0].length;
2608
+ return this.elements.length === cols;
2609
+ }
2610
+ max() {
2611
+ if (this.elements.length === 0) {
2612
+ return null;
2613
+ }
2614
+ var m = 0,
2615
+ i = this.elements.length,
2616
+ nj = this.elements[0].length,
2617
+ j;
2618
+ while (i--) {
2619
+ j = nj;
2620
+ while (j--) {
2621
+ if (Math.abs(this.elements[i][j]) > Math.abs(m)) {
2622
+ m = this.elements[i][j];
2623
+ }
2624
+ }
2625
+ }
2626
+ return m;
2627
+ }
2628
+ indexOf(x) {
2629
+ if (this.elements.length === 0) {
2630
+ return null;
2631
+ }
2632
+ var ni = this.elements.length,
2633
+ i,
2634
+ nj = this.elements[0].length,
2635
+ j;
2636
+ for (i = 0; i < ni; i++) {
2637
+ for (j = 0; j < nj; j++) {
2638
+ if (this.elements[i][j] === x) {
2639
+ return {
2640
+ i: i + 1,
2641
+ j: j + 1
2642
+ };
2643
+ }
2644
+ }
2645
+ }
2646
+ return null;
2647
+ }
2648
+ diagonal() {
2649
+ if (!this.isSquare) {
2650
+ return null;
2651
+ }
2652
+ var els = [],
2653
+ n = this.elements.length;
2654
+ for (var i = 0; i < n; i++) {
2655
+ els.push(this.elements[i][i]);
2656
+ }
2657
+ return new Vector(els);
2658
+ }
2659
+ toRightTriangular() {
2660
+ if (this.elements.length === 0) {
2661
+ return new Matrix([]);
2662
+ }
2663
+ var M = this.dup(),
2664
+ els;
2665
+ var n = this.elements.length,
2666
+ i,
2667
+ j,
2668
+ np = this.elements[0].length,
2669
+ p;
2670
+ for (i = 0; i < n; i++) {
2671
+ if (M.elements[i][i] === 0) {
2672
+ for (j = i + 1; j < n; j++) {
2673
+ if (M.elements[j][i] !== 0) {
2674
+ els = [];
2675
+ for (p = 0; p < np; p++) {
2676
+ els.push(M.elements[i][p] + M.elements[j][p]);
2677
+ }
2678
+ M.elements[i] = els;
2679
+ break;
2680
+ }
2681
+ }
2682
+ }
2683
+ if (M.elements[i][i] !== 0) {
2684
+ for (j = i + 1; j < n; j++) {
2685
+ var multiplier = M.elements[j][i] / M.elements[i][i];
2686
+ els = [];
2687
+ for (p = 0; p < np; p++) {
2688
+ // Elements with column numbers up to an including the number of the
2689
+ // row that we're subtracting can safely be set straight to zero,
2690
+ // since that's the point of this routine and it avoids having to
2691
+ // loop over and correct rounding errors later
2692
+ els.push(p <= i ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier);
2693
+ }
2694
+ M.elements[j] = els;
2695
+ }
2696
+ }
2697
+ }
2698
+ return M;
2699
+ }
2700
+ determinant() {
2701
+ if (this.elements.length === 0) {
2702
+ return 1;
2703
+ }
2704
+ if (!this.isSquare()) {
2705
+ return null;
2706
+ }
2707
+ var M = this.toRightTriangular();
2708
+ var det = M.elements[0][0],
2709
+ n = M.elements.length;
2710
+ for (var i = 1; i < n; i++) {
2711
+ det = det * M.elements[i][i];
2712
+ }
2713
+ return det;
2714
+ }
2715
+ isSingular() {
2716
+ return this.isSquare() && this.determinant() === 0;
2717
+ }
2718
+ trace() {
2719
+ if (this.elements.length === 0) {
2720
+ return 0;
2721
+ }
2722
+ if (!this.isSquare()) {
2723
+ return null;
2724
+ }
2725
+ var tr = this.elements[0][0],
2726
+ n = this.elements.length;
2727
+ for (var i = 1; i < n; i++) {
2728
+ tr += this.elements[i][i];
2729
+ }
2730
+ return tr;
2731
+ }
2732
+ rank() {
2733
+ if (this.elements.length === 0) {
2734
+ return 0;
2735
+ }
2736
+ var M = this.toRightTriangular(),
2737
+ rank = 0;
2738
+ var i = this.elements.length,
2739
+ nj = this.elements[0].length,
2740
+ j;
2741
+ while (i--) {
2742
+ j = nj;
2743
+ while (j--) {
2744
+ if (Math.abs(M.elements[i][j]) > PRECISION) {
2745
+ rank++;
2746
+ break;
2747
+ }
2748
+ }
2749
+ }
2750
+ return rank;
2751
+ }
2752
+ augment(matrix) {
2753
+ if (this.elements.length === 0) {
2754
+ return this.dup();
2755
+ }
2756
+ var M = matrix.elements || matrix;
2757
+ if (typeof M[0][0] === 'undefined') {
2758
+ M = new Matrix(M).elements;
2759
+ }
2760
+ var T = this.dup(),
2761
+ cols = T.elements[0].length;
2762
+ var i = T.elements.length,
2763
+ nj = M[0].length,
2764
+ j;
2765
+ if (i !== M.length) {
2766
+ return null;
2767
+ }
2768
+ while (i--) {
2769
+ j = nj;
2770
+ while (j--) {
2771
+ T.elements[i][cols + j] = M[i][j];
2772
+ }
2773
+ }
2774
+ return T;
2775
+ }
2776
+ inverse() {
2777
+ if (this.elements.length === 0) {
2778
+ return null;
2779
+ }
2780
+ if (!this.isSquare() || this.isSingular()) {
2781
+ return null;
2782
+ }
2783
+ var n = this.elements.length,
2784
+ i = n,
2785
+ j;
2786
+ var M = this.augment(Matrix.I(n)).toRightTriangular();
2787
+ var np = M.elements[0].length,
2788
+ p,
2789
+ els,
2790
+ divisor;
2791
+ var inverse_elements = [],
2792
+ new_element;
2793
+ // Matrix is non-singular so there will be no zeros on the
2794
+ // diagonal. Cycle through rows from last to first.
2795
+ while (i--) {
2796
+ // First, normalise diagonal elements to 1
2797
+ els = [];
2798
+ inverse_elements[i] = [];
2799
+ divisor = M.elements[i][i];
2800
+ for (p = 0; p < np; p++) {
2801
+ new_element = M.elements[i][p] / divisor;
2802
+ els.push(new_element);
2803
+ // Shuffle off the current row of the right hand side into the results
2804
+ // array as it will not be modified by later runs through this loop
2805
+ if (p >= n) {
2806
+ inverse_elements[i].push(new_element);
2807
+ }
2808
+ }
2809
+ M.elements[i] = els;
2810
+ // Then, subtract this row from those above it to give the identity matrix
2811
+ // on the left hand side
2812
+ j = i;
2813
+ while (j--) {
2814
+ els = [];
2815
+ for (p = 0; p < np; p++) {
2816
+ els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]);
2817
+ }
2818
+ M.elements[j] = els;
2819
+ }
2820
+ }
2821
+ return new Matrix(inverse_elements);
2822
+ }
2823
+ round() {
2824
+ return this.map(function (x) {
2825
+ return Math.round(x);
2826
+ });
2827
+ }
2828
+ snapTo(x) {
2829
+ return this.map(function (p) {
2830
+ return Math.abs(p - x) <= PRECISION ? x : p;
2831
+ });
2832
+ }
2833
+ inspect() {
2834
+ var matrix_rows = [];
2835
+ var n = this.elements.length;
2836
+ if (n === 0) return '[]';
2837
+ for (var i = 0; i < n; i++) {
2838
+ matrix_rows.push(new Vector(this.elements[i]).inspect());
2839
+ }
2840
+ return matrix_rows.join('\n');
2841
+ }
2842
+ setElements(els) {
2843
+ var i,
2844
+ j,
2845
+ elements = els.elements || els;
2846
+ if (elements[0] && typeof elements[0][0] !== 'undefined') {
2847
+ i = elements.length;
2848
+ this.elements = [];
2849
+ while (i--) {
2850
+ j = elements[i].length;
2851
+ this.elements[i] = [];
2852
+ while (j--) {
2853
+ this.elements[i][j] = elements[i][j];
2854
+ }
2855
+ }
2856
+ return this;
2857
+ }
2858
+ var n = elements.length;
2859
+ this.elements = [];
2860
+ for (i = 0; i < n; i++) {
2861
+ this.elements.push([elements[i]]);
2862
+ }
2863
+ return this;
2864
+ }
2865
+
2866
+ //From glUtils.js
2867
+ flatten() {
2868
+ var result = [];
2869
+ if (this.elements.length == 0) {
2870
+ return [];
2871
+ }
2872
+ for (var j = 0; j < this.elements[0].length; j++) {
2873
+ for (var i = 0; i < this.elements.length; i++) {
2874
+ result.push(this.elements[i][j]);
2875
+ }
2876
+ }
2877
+ return result;
2878
+ }
2879
+
2880
+ //From glUtils.js
2881
+ ensure4x4() {
2882
+ if (this.elements.length == 4 && this.elements[0].length == 4) {
2883
+ return this;
2884
+ }
2885
+ if (this.elements.length > 4 || this.elements[0].length > 4) {
2886
+ return null;
2887
+ }
2888
+ for (var i = 0; i < this.elements.length; i++) {
2889
+ for (var j = this.elements[i].length; j < 4; j++) {
2890
+ if (i == j) {
2891
+ this.elements[i].push(1);
2892
+ } else {
2893
+ this.elements[i].push(0);
2894
+ }
2895
+ }
2896
+ }
2897
+ for (var i = this.elements.length; i < 4; i++) {
2898
+ if (i == 0) {
2899
+ this.elements.push([1, 0, 0, 0]);
2900
+ } else if (i == 1) {
2901
+ this.elements.push([0, 1, 0, 0]);
2902
+ } else if (i == 2) {
2903
+ this.elements.push([0, 0, 1, 0]);
2904
+ } else if (i == 3) {
2905
+ this.elements.push([0, 0, 0, 1]);
2906
+ }
2907
+ }
2908
+ return this;
2909
+ }
2910
+
2911
+ //From glUtils.js
2912
+ make3x3() {
2913
+ if (this.elements.length != 4 || this.elements[0].length != 4) {
2914
+ return null;
2915
+ }
2916
+ return new Matrix([[this.elements[0][0], this.elements[0][1], this.elements[0][2]], [this.elements[1][0], this.elements[1][1], this.elements[1][2]], [this.elements[2][0], this.elements[2][1], this.elements[2][2]]]);
2917
+ }
2918
+ }
2919
+ Matrix.I = function (n) {
2920
+ var els = [],
2921
+ i = n,
2922
+ j;
2923
+ while (i--) {
2924
+ j = n;
2925
+ els[i] = [];
2926
+ while (j--) {
2927
+ els[i][j] = i === j ? 1 : 0;
2928
+ }
2929
+ }
2930
+ return new Matrix(els);
2931
+ };
2932
+ Matrix.Diagonal = function (elements) {
2933
+ var i = elements.length;
2934
+ var M = Matrix.I(i);
2935
+ while (i--) {
2936
+ M.elements[i][i] = elements[i];
2937
+ }
2938
+ return M;
2939
+ };
2940
+ Matrix.Rotation = function (theta, a) {
2941
+ if (!a) {
2942
+ return new Matrix([[Math.cos(theta), -Math.sin(theta)], [Math.sin(theta), Math.cos(theta)]]);
2943
+ }
2944
+ var axis = a.dup();
2945
+ if (axis.elements.length !== 3) {
2946
+ return null;
2947
+ }
2948
+ var mod = axis.modulus();
2949
+ var x = axis.elements[0] / mod,
2950
+ y = axis.elements[1] / mod,
2951
+ z = axis.elements[2] / mod;
2952
+ var s = Math.sin(theta),
2953
+ c = Math.cos(theta),
2954
+ t = 1 - c;
2955
+ // Formula derived here: http://www.gamedev.net/reference/articles/article1199.asp
2956
+ // That proof rotates the co-ordinate system so theta becomes -theta and sin
2957
+ // becomes -sin here.
2958
+ return new Matrix([[t * x * x + c, t * x * y - s * z, t * x * z + s * y], [t * x * y + s * z, t * y * y + c, t * y * z - s * x], [t * x * z - s * y, t * y * z + s * x, t * z * z + c]]);
2959
+ };
2960
+ Matrix.RotationX = function (t) {
2961
+ var c = Math.cos(t),
2962
+ s = Math.sin(t);
2963
+ return new Matrix([[1, 0, 0], [0, c, -s], [0, s, c]]);
2964
+ };
2965
+ Matrix.RotationY = function (t) {
2966
+ var c = Math.cos(t),
2967
+ s = Math.sin(t);
2968
+ return new Matrix([[c, 0, s], [0, 1, 0], [-s, 0, c]]);
2969
+ };
2970
+ Matrix.RotationZ = function (t) {
2971
+ var c = Math.cos(t),
2972
+ s = Math.sin(t);
2973
+ return new Matrix([[c, -s, 0], [s, c, 0], [0, 0, 1]]);
2974
+ };
2975
+ Matrix.Random = function (n, m) {
2976
+ return Matrix.Zero(n, m).map(function () {
2977
+ return Math.random();
2978
+ });
2979
+ };
2980
+
2981
+ //From glUtils.js
2982
+ Matrix.Translation = function (v) {
2983
+ if (v.elements.length == 2) {
2984
+ var r = Matrix.I(3);
2985
+ r.elements[2][0] = v.elements[0];
2986
+ r.elements[2][1] = v.elements[1];
2987
+ return r;
2988
+ }
2989
+ if (v.elements.length == 3) {
2990
+ var r = Matrix.I(4);
2991
+ r.elements[0][3] = v.elements[0];
2992
+ r.elements[1][3] = v.elements[1];
2993
+ r.elements[2][3] = v.elements[2];
2994
+ return r;
2995
+ }
2996
+ throw "Invalid length for Translation";
2997
+ };
2998
+ Matrix.Zero = function (n, m) {
2999
+ var els = [],
3000
+ i = n,
3001
+ j;
3002
+ while (i--) {
3003
+ j = m;
3004
+ els[i] = [];
3005
+ while (j--) {
3006
+ els[i][j] = 0;
3007
+ }
3008
+ }
3009
+ return new Matrix(els);
3010
+ };
3011
+ Matrix.prototype.toUpperTriangular = Matrix.prototype.toRightTriangular;
3012
+ Matrix.prototype.det = Matrix.prototype.determinant;
3013
+ Matrix.prototype.tr = Matrix.prototype.trace;
3014
+ Matrix.prototype.rk = Matrix.prototype.rank;
3015
+ Matrix.prototype.inv = Matrix.prototype.inverse;
3016
+ Matrix.prototype.x = Matrix.prototype.multiply;
3017
+
3018
+ class Vector {
3019
+ constructor(elements) {
3020
+ this.setElements(elements);
3021
+ }
3022
+ e(i) {
3023
+ return i < 1 || i > this.elements.length ? null : this.elements[i - 1];
3024
+ }
3025
+ dimensions() {
3026
+ return this.elements.length;
3027
+ }
3028
+ modulus() {
3029
+ return Math.sqrt(this.dot(this));
3030
+ }
3031
+ eql(vector) {
3032
+ var n = this.elements.length;
3033
+ var V = vector.elements || vector;
3034
+ if (n !== V.length) {
3035
+ return false;
3036
+ }
3037
+ while (n--) {
3038
+ if (Math.abs(this.elements[n] - V[n]) > PRECISION) {
3039
+ return false;
3040
+ }
3041
+ }
3042
+ return true;
3043
+ }
3044
+ dup() {
3045
+ return new Vector(this.elements);
3046
+ }
3047
+ map(fn, context) {
3048
+ var elements = [];
3049
+ this.each(function (x, i) {
3050
+ elements.push(fn.call(context, x, i));
3051
+ });
3052
+ return new Vector(elements);
3053
+ }
3054
+ forEach(fn, context) {
3055
+ var n = this.elements.length;
3056
+ for (var i = 0; i < n; i++) {
3057
+ fn.call(context, this.elements[i], i + 1);
3058
+ }
3059
+ }
3060
+ toUnitVector() {
3061
+ var r = this.modulus();
3062
+ if (r === 0) {
3063
+ return this.dup();
3064
+ }
3065
+ return this.map(function (x) {
3066
+ return x / r;
3067
+ });
3068
+ }
3069
+ angleFrom(vector) {
3070
+ var V = vector.elements || vector;
3071
+ var n = this.elements.length;
3072
+ if (n !== V.length) {
3073
+ return null;
3074
+ }
3075
+ var dot = 0,
3076
+ mod1 = 0,
3077
+ mod2 = 0;
3078
+ // Work things out in parallel to save time
3079
+ this.each(function (x, i) {
3080
+ dot += x * V[i - 1];
3081
+ mod1 += x * x;
3082
+ mod2 += V[i - 1] * V[i - 1];
3083
+ });
3084
+ mod1 = Math.sqrt(mod1);
3085
+ mod2 = Math.sqrt(mod2);
3086
+ if (mod1 * mod2 === 0) {
3087
+ return null;
3088
+ }
3089
+ var theta = dot / (mod1 * mod2);
3090
+ if (theta < -1) {
3091
+ theta = -1;
3092
+ }
3093
+ if (theta > 1) {
3094
+ theta = 1;
3095
+ }
3096
+ return Math.acos(theta);
3097
+ }
3098
+ isParallelTo(vector) {
3099
+ var angle = this.angleFrom(vector);
3100
+ return angle === null ? null : angle <= PRECISION;
3101
+ }
3102
+ isAntiparallelTo(vector) {
3103
+ var angle = this.angleFrom(vector);
3104
+ return angle === null ? null : Math.abs(angle - Math.PI) <= PRECISION;
3105
+ }
3106
+ isPerpendicularTo(vector) {
3107
+ var dot = this.dot(vector);
3108
+ return dot === null ? null : Math.abs(dot) <= PRECISION;
3109
+ }
3110
+ add(vector) {
3111
+ var V = vector.elements || vector;
3112
+ if (this.elements.length !== V.length) {
3113
+ return null;
3114
+ }
3115
+ return this.map(function (x, i) {
3116
+ return x + V[i - 1];
3117
+ });
3118
+ }
3119
+ subtract(vector) {
3120
+ var V = vector.elements || vector;
3121
+ if (this.elements.length !== V.length) {
3122
+ return null;
3123
+ }
3124
+ return this.map(function (x, i) {
3125
+ return x - V[i - 1];
3126
+ });
3127
+ }
3128
+ multiply(k) {
3129
+ return this.map(function (x) {
3130
+ return x * k;
3131
+ });
3132
+ }
3133
+ dot(vector) {
3134
+ var V = vector.elements || vector;
3135
+ var product = 0,
3136
+ n = this.elements.length;
3137
+ if (n !== V.length) {
3138
+ return null;
3139
+ }
3140
+ while (n--) {
3141
+ product += this.elements[n] * V[n];
3142
+ }
3143
+ return product;
3144
+ }
3145
+ cross(vector) {
3146
+ var B = vector.elements || vector;
3147
+ if (this.elements.length !== 3 || B.length !== 3) {
3148
+ return null;
3149
+ }
3150
+ var A = this.elements;
3151
+ return new Vector([A[1] * B[2] - A[2] * B[1], A[2] * B[0] - A[0] * B[2], A[0] * B[1] - A[1] * B[0]]);
3152
+ }
3153
+ max() {
3154
+ var m = 0,
3155
+ i = this.elements.length;
3156
+ while (i--) {
3157
+ if (Math.abs(this.elements[i]) > Math.abs(m)) {
3158
+ m = this.elements[i];
3159
+ }
3160
+ }
3161
+ return m;
3162
+ }
3163
+ indexOf(x) {
3164
+ var index = null,
3165
+ n = this.elements.length;
3166
+ for (var i = 0; i < n; i++) {
3167
+ if (index === null && this.elements[i] === x) {
3168
+ index = i + 1;
3169
+ }
3170
+ }
3171
+ return index;
3172
+ }
3173
+ toDiagonalMatrix() {
3174
+ return Matrix.Diagonal(this.elements);
3175
+ }
3176
+ round() {
3177
+ return this.map(function (x) {
3178
+ return Math.round(x);
3179
+ });
3180
+ }
3181
+ snapTo(x) {
3182
+ return this.map(function (y) {
3183
+ return Math.abs(y - x) <= PRECISION ? x : y;
3184
+ });
3185
+ }
3186
+ distanceFrom(obj) {
3187
+ if (obj.anchor || obj.start && obj.end) {
3188
+ return obj.distanceFrom(this);
3189
+ }
3190
+ var V = obj.elements || obj;
3191
+ if (V.length !== this.elements.length) {
3192
+ return null;
3193
+ }
3194
+ var sum = 0,
3195
+ part;
3196
+ this.each(function (x, i) {
3197
+ part = x - V[i - 1];
3198
+ sum += part * part;
3199
+ });
3200
+ return Math.sqrt(sum);
3201
+ }
3202
+ liesOn(line) {
3203
+ return line.contains(this);
3204
+ }
3205
+ liesIn(plane) {
3206
+ return plane.contains(this);
3207
+ }
3208
+ rotate(t, obj) {
3209
+ var V,
3210
+ R = null,
3211
+ x,
3212
+ y,
3213
+ z;
3214
+ if (t.determinant) {
3215
+ R = t.elements;
3216
+ }
3217
+ switch (this.elements.length) {
3218
+ case 2:
3219
+ {
3220
+ V = obj.elements || obj;
3221
+ if (V.length !== 2) {
3222
+ return null;
3223
+ }
3224
+ if (!R) {
3225
+ R = Matrix.Rotation(t).elements;
3226
+ }
3227
+ x = this.elements[0] - V[0];
3228
+ y = this.elements[1] - V[1];
3229
+ return new Vector([V[0] + R[0][0] * x + R[0][1] * y, V[1] + R[1][0] * x + R[1][1] * y]);
3230
+ }
3231
+ case 3:
3232
+ {
3233
+ if (!obj.direction) {
3234
+ return null;
3235
+ }
3236
+ var C = obj.pointClosestTo(this).elements;
3237
+ if (!R) {
3238
+ R = Matrix.Rotation(t, obj.direction).elements;
3239
+ }
3240
+ x = this.elements[0] - C[0];
3241
+ y = this.elements[1] - C[1];
3242
+ z = this.elements[2] - C[2];
3243
+ return new Vector([C[0] + R[0][0] * x + R[0][1] * y + R[0][2] * z, C[1] + R[1][0] * x + R[1][1] * y + R[1][2] * z, C[2] + R[2][0] * x + R[2][1] * y + R[2][2] * z]);
3244
+ }
3245
+ default:
3246
+ {
3247
+ return null;
3248
+ }
3249
+ }
3250
+ }
3251
+ reflectionIn(obj) {
3252
+ if (obj.anchor) {
3253
+ // obj is a plane or line
3254
+ var P = this.elements.slice();
3255
+ var C = obj.pointClosestTo(P).elements;
3256
+ return new Vector([C[0] + (C[0] - P[0]), C[1] + (C[1] - P[1]), C[2] + (C[2] - (P[2] || 0))]);
3257
+ } else {
3258
+ // obj is a point
3259
+ var Q = obj.elements || obj;
3260
+ if (this.elements.length !== Q.length) {
3261
+ return null;
3262
+ }
3263
+ return this.map(function (x, i) {
3264
+ return Q[i - 1] + (Q[i - 1] - x);
3265
+ });
3266
+ }
3267
+ }
3268
+ to3D() {
3269
+ var V = this.dup();
3270
+ switch (V.elements.length) {
3271
+ case 3:
3272
+ {
3273
+ break;
3274
+ }
3275
+ case 2:
3276
+ {
3277
+ V.elements.push(0);
3278
+ break;
3279
+ }
3280
+ default:
3281
+ {
3282
+ return null;
3283
+ }
3284
+ }
3285
+ return V;
3286
+ }
3287
+ inspect() {
3288
+ return '[' + this.elements.join(', ') + ']';
3289
+ }
3290
+ setElements(els) {
3291
+ this.elements = (els.elements || els).slice();
3292
+ return this;
3293
+ }
3294
+
3295
+ //From glUtils.js
3296
+ flatten() {
3297
+ return this.elements;
3298
+ }
3299
+ }
3300
+ Vector.Random = function (n) {
3301
+ var elements = [];
3302
+ while (n--) {
3303
+ elements.push(Math.random());
3304
+ }
3305
+ return new Vector(elements);
3306
+ };
3307
+ Vector.Zero = function (n) {
3308
+ var elements = [];
3309
+ while (n--) {
3310
+ elements.push(0);
3311
+ }
3312
+ return new Vector(elements);
3313
+ };
3314
+ Vector.prototype.x = Vector.prototype.multiply;
3315
+ Vector.prototype.each = Vector.prototype.forEach;
3316
+ Vector.i = new Vector([1, 0, 0]);
3317
+ Vector.j = new Vector([0, 1, 0]);
3318
+ Vector.k = new Vector([0, 0, 1]);
3319
+
3320
+ var computeCentroids = function computeCentroids(config, position, row) {
3321
+ var centroids = [];
3322
+ var p = Object.keys(config.dimensions);
3323
+ var cols = p.length;
3324
+ var a = 0.5; // center between axes
3325
+ for (var i = 0; i < cols; ++i) {
3326
+ // centroids on 'real' axes
3327
+ var x = position(p[i]);
3328
+ var y = config.dimensions[p[i]].yscale(row[p[i]]);
3329
+ centroids.push(new Vector([x, y]));
3330
+
3331
+ // centroids on 'virtual' axes
3332
+ if (i < cols - 1) {
3333
+ var cx = x + a * (position(p[i + 1]) - x);
3334
+ var cy = y + a * (config.dimensions[p[i + 1]].yscale(row[p[i + 1]]) - y);
3335
+ if (config.bundleDimension !== null) {
3336
+ var leftCentroid = config.clusterCentroids.get(config.dimensions[config.bundleDimension].yscale(row[config.bundleDimension])).get(p[i]);
3337
+ var rightCentroid = config.clusterCentroids.get(config.dimensions[config.bundleDimension].yscale(row[config.bundleDimension])).get(p[i + 1]);
3338
+ var centroid = 0.5 * (leftCentroid + rightCentroid);
3339
+ cy = centroid + (1 - config.bundlingStrength) * (cy - centroid);
3340
+ }
3341
+ centroids.push(new Vector([cx, cy]));
3342
+ }
3343
+ }
3344
+ return centroids;
3345
+ };
3346
+
3347
+ var computeControlPoints = function computeControlPoints(smoothness, centroids) {
3348
+ var cols = centroids.length;
3349
+ var a = smoothness;
3350
+ var cps = [];
3351
+ cps.push(centroids[0]);
3352
+ cps.push(new Vector([centroids[0].e(1) + a * 2 * (centroids[1].e(1) - centroids[0].e(1)), centroids[0].e(2)]));
3353
+ for (var col = 1; col < cols - 1; ++col) {
3354
+ var mid = centroids[col];
3355
+ var left = centroids[col - 1];
3356
+ var right = centroids[col + 1];
3357
+ var diff = left.subtract(right);
3358
+ cps.push(mid.add(diff.x(a)));
3359
+ cps.push(mid);
3360
+ cps.push(mid.subtract(diff.x(a)));
3361
+ }
3362
+ cps.push(new Vector([centroids[cols - 1].e(1) + a * 2 * (centroids[cols - 2].e(1) - centroids[cols - 1].e(1)), centroids[cols - 1].e(2)]));
3363
+ cps.push(centroids[cols - 1]);
3364
+ return cps;
3365
+ };
3366
+
3367
+ // draw single cubic bezier curve
3368
+ var singleCurve = function singleCurve(config, position, d, ctx) {
3369
+ var centroids = computeCentroids(config, position, d);
3370
+ var cps = computeControlPoints(config.smoothness, centroids);
3371
+ ctx.moveTo(cps[0].e(1), cps[0].e(2));
3372
+ for (var i = 1; i < cps.length; i += 3) {
3373
+ if (config.showControlPoints) {
3374
+ for (var j = 0; j < 3; j++) {
3375
+ ctx.fillRect(cps[i + j].e(1), cps[i + j].e(2), 2, 2);
3376
+ }
3377
+ }
3378
+ ctx.bezierCurveTo(cps[i].e(1), cps[i].e(2), cps[i + 1].e(1), cps[i + 1].e(2), cps[i + 2].e(1), cps[i + 2].e(2));
3379
+ }
3380
+ };
3381
+
3382
+ // returns the y-position just beyond the separating null value line
3383
+ var getNullPosition = function getNullPosition(config) {
3384
+ if (config.nullValueSeparator === 'bottom') {
3385
+ return h(config) + 1;
3386
+ } else if (config.nullValueSeparator === 'top') {
3387
+ return 1;
3388
+ } else {
3389
+ console.log("A value is NULL, but nullValueSeparator is not set; set it to 'bottom' or 'top'.");
3390
+ }
3391
+ return h(config) + 1;
3392
+ };
3393
+ var singlePath = function singlePath(config, position, d, ctx) {
3394
+ Object.keys(config.dimensions).map(function (p) {
3395
+ return [position(p), d[p] === undefined ? getNullPosition(config) : config.dimensions[p].yscale(d[p])];
3396
+ }).sort(function (a, b) {
3397
+ return a[0] - b[0];
3398
+ }).forEach(function (p, i) {
3399
+ i === 0 ? ctx.moveTo(p[0], p[1]) : ctx.lineTo(p[0], p[1]);
3400
+ });
3401
+ };
3402
+
3403
+ // draw single polyline
3404
+ var colorPath = function colorPath(config, position, d, ctx) {
3405
+ ctx.beginPath();
3406
+ if (config.bundleDimension !== null && config.bundlingStrength > 0 || config.smoothness > 0) {
3407
+ singleCurve(config, position, d, ctx);
3408
+ } else {
3409
+ singlePath(config, position, d, ctx);
3410
+ }
3411
+ ctx.stroke();
3412
+ };
3413
+
3414
+ var _functor = function _functor(v) {
3415
+ return typeof v === 'function' ? v : function () {
3416
+ return v;
3417
+ };
3418
+ };
3419
+
3420
+ var pathMark = function pathMark(config, ctx, position) {
3421
+ return function (d, i) {
3422
+ ctx.marked.strokeStyle = _functor(config.color)(d, i);
3423
+ return colorPath(config, position, d, ctx.marked);
3424
+ };
3425
+ };
3426
+ var renderMarkedDefault = function renderMarkedDefault(config, pc, ctx, position) {
3427
+ return function () {
3428
+ pc.clear('marked');
3429
+ if (config.marked.length) {
3430
+ config.marked.forEach(pathMark(config, ctx, position));
3431
+ }
3432
+ };
3433
+ };
3434
+ var renderMarkedQueue = function renderMarkedQueue(config, markedQueue) {
3435
+ return function () {
3436
+ if (config.marked) {
3437
+ markedQueue(config.marked);
3438
+ } else {
3439
+ markedQueue([]); // This is needed to clear the currently marked items
3440
+ }
3441
+ };
3442
+ };
3443
+ var renderMarked = function renderMarked(config, pc, events) {
3444
+ return function () {
3445
+ if (!Object.keys(config.dimensions).length) pc.detectDimensions();
3446
+ pc.renderMarked[config.mode]();
3447
+ events.call('render', this);
3448
+ return this;
3449
+ };
3450
+ };
3451
+
3452
+ var pathBrushed = function pathBrushed(config, ctx, position) {
3453
+ return function (d, i) {
3454
+ if (config.brushedColor !== null) {
3455
+ ctx.brushed.strokeStyle = _functor(config.brushedColor)(d, i);
3456
+ } else {
3457
+ ctx.brushed.strokeStyle = _functor(config.color)(d, i);
3458
+ }
3459
+ return colorPath(config, position, d, ctx.brushed);
3460
+ };
3461
+ };
3462
+ var renderBrushedDefault = function renderBrushedDefault(config, ctx, position, pc, brushGroup) {
3463
+ return function () {
3464
+ pc.clear('brushed');
3465
+ if (isBrushed(config, brushGroup) && config.brushed !== false) {
3466
+ config.brushed.forEach(pathBrushed(config, ctx, position));
3467
+ }
3468
+ };
3469
+ };
3470
+ var renderBrushedQueue = function renderBrushedQueue(config, brushGroup, brushedQueue) {
3471
+ return function () {
3472
+ if (isBrushed(config, brushGroup)) {
3473
+ brushedQueue(config.brushed);
3474
+ } else {
3475
+ brushedQueue([]); // This is needed to clear the currently brushed items
3476
+ }
3477
+ };
3478
+ };
3479
+ var renderBrushed = function renderBrushed(config, pc, events) {
3480
+ return function () {
3481
+ if (!Object.keys(config.dimensions).length) pc.detectDimensions();
3482
+ pc.renderBrushed[config.mode]();
3483
+ events.call('render', this);
3484
+ return this;
3485
+ };
3486
+ };
3487
+
3488
+ var brushReset = function brushReset(config, pc) {
3489
+ return function (dimension) {
3490
+ var brushesToKeep = [];
3491
+ for (var j = 0; j < config.brushes.length; j++) {
3492
+ if (config.brushes[j].data !== dimension) {
3493
+ brushesToKeep.push(config.brushes[j]);
3494
+ }
3495
+ }
3496
+ config.brushes = brushesToKeep;
3497
+ config.brushed = false;
3498
+ if (pc.g() !== undefined) {
3499
+ var nodes = pc.g().selectAll('.brush').nodes();
3500
+ for (var i = 0; i < nodes.length; i++) {
3501
+ if (nodes[i].__data__ === dimension) {
3502
+ // remove all dummy brushes for this axis or the real brush
3503
+ select(select(nodes[i]).nodes()[0].parentNode).selectAll('.dummy').remove();
3504
+ config.dimensions[dimension].brush.move(select(nodes[i], null));
3505
+ }
3506
+ }
3507
+ }
3508
+ return this;
3509
+ };
3510
+ };
3511
+
3512
+ // a better "typeof" from this post: http://stackoverflow.com/questions/7390426/better-way-to-get-type-of-a-javascript-variable
3513
+ var toType = function toType(v) {
3514
+ return {}.toString.call(v).match(/\s([a-zA-Z]+)/)[1].toLowerCase();
3515
+ };
3516
+
3517
+ // this descriptive text should live with other introspective methods
3518
+ var toString = function toString(config) {
3519
+ return function () {
3520
+ return 'Parallel Coordinates: ' + Object.keys(config.dimensions).length + ' dimensions (' + Object.keys(config.data[0]).length + ' total) , ' + config.data.length + ' rows';
3521
+ };
3522
+ };
3523
+
3524
+ // pairs of adjacent dimensions
3525
+ var adjacentPairs = function adjacentPairs(arr) {
3526
+ var ret = [];
3527
+ for (var i = 0; i < arr.length - 1; i++) {
3528
+ ret.push([arr[i], arr[i + 1]]);
3529
+ }
3530
+ return ret;
3531
+ };
3532
+
3533
+ var pathHighlight = function pathHighlight(config, ctx, position) {
3534
+ return function (d, i) {
3535
+ ctx.highlight.strokeStyle = _functor(config.color)(d, i);
3536
+ return colorPath(config, position, d, ctx.highlight);
3537
+ };
3538
+ };
3539
+
3540
+ // highlight an array of data
3541
+ var highlight = function highlight(config, pc, canvas, events, ctx, position) {
3542
+ return function () {
3543
+ var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
3544
+ if (data === null) {
3545
+ return config.highlighted;
3546
+ }
3547
+ config.highlighted = data;
3548
+ pc.clear('highlight');
3549
+ selectAll([canvas.foreground, canvas.brushed]).classed('faded', true);
3550
+ data.forEach(pathHighlight(config, ctx, position));
3551
+ events.call('highlight', this, data);
3552
+ return this;
3553
+ };
3554
+ };
3555
+
3556
+ // clear highlighting
3557
+ var unhighlight = function unhighlight(config, pc, canvas) {
3558
+ return function () {
3559
+ config.highlighted = [];
3560
+ pc.clear('highlight');
3561
+ selectAll([canvas.foreground, canvas.brushed]).classed('faded', false);
3562
+ return this;
3563
+ };
3564
+ };
3565
+
3566
+ // mark an array of data
3567
+ var mark = function mark(config, pc, canvas, events, ctx, position) {
3568
+ return function () {
3569
+ var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
3570
+ if (data === null) {
3571
+ return config.marked;
3572
+ }
3573
+
3574
+ // add array to already marked data
3575
+ config.marked = config.marked.concat(data);
3576
+ selectAll([canvas.foreground, canvas.brushed]).classed('dimmed', true);
3577
+ data.forEach(pathMark(config, ctx, position));
3578
+ events.call('mark', this, data);
3579
+ return this;
3580
+ };
3581
+ };
3582
+
3583
+ // clear marked data arrays
3584
+ var unmark = function unmark(config, pc, canvas) {
3585
+ return function () {
3586
+ config.marked = [];
3587
+ pc.clear('marked');
3588
+ selectAll([canvas.foreground, canvas.brushed]).classed('dimmed', false);
3589
+ return this;
3590
+ };
3591
+ };
3592
+
3593
+ var removeAxes = function removeAxes(pc) {
3594
+ return function () {
3595
+ pc._g.remove();
3596
+ delete pc._g;
3597
+ return this;
3598
+ };
3599
+ };
3600
+
3601
+ /**
3602
+ * Renders the polylines.
3603
+ * If no dimensions have been specified, it will attempt to detect quantitative
3604
+ * dimensions based on the first data entry. If scales haven't been set, it will
3605
+ * autoscale based on the extent for each dimension.
3606
+ *
3607
+ * @param config
3608
+ * @param pc
3609
+ * @param events
3610
+ * @returns {Function}
3611
+ */
3612
+ var render = function render(config, pc, events) {
3613
+ return function () {
3614
+ // try to autodetect dimensions and create scales
3615
+ if (!Object.keys(config.dimensions).length) {
3616
+ pc.detectDimensions();
3617
+ }
3618
+ pc.autoscale();
3619
+ pc.render[config.mode]();
3620
+ events.call('render', this);
3621
+ return this;
3622
+ };
3623
+ };
3624
+
3625
+ var pathForeground = function pathForeground(config, ctx, position) {
3626
+ return function (d, i) {
3627
+ ctx.foreground.strokeStyle = _functor(config.color)(d, i);
3628
+ return colorPath(config, position, d, ctx.foreground);
3629
+ };
3630
+ };
3631
+ var renderDefault = function renderDefault(config, pc, ctx, position) {
3632
+ return function () {
3633
+ pc.clear('foreground');
3634
+ pc.clear('highlight');
3635
+ pc.renderBrushed["default"]();
3636
+ pc.renderMarked["default"]();
3637
+ config.data.forEach(pathForeground(config, ctx, position));
3638
+ };
3639
+ };
3640
+ var renderDefaultQueue = function renderDefaultQueue(config, pc, foregroundQueue) {
3641
+ return function () {
3642
+ pc.renderBrushed.queue();
3643
+ pc.renderMarked.queue();
3644
+ foregroundQueue(config.data);
3645
+ };
3646
+ };
3647
+
3648
+ // try to coerce to number before returning type
3649
+ var toTypeCoerceNumbers = function toTypeCoerceNumbers(v) {
3650
+ return parseFloat(v) == v && v !== null ? 'number' : toType(v);
3651
+ };
3652
+
3653
+ // attempt to determine types of each dimension based on first row of data
3654
+ var detectDimensionTypes = function detectDimensionTypes(data) {
3655
+ return Object.keys(data[0]).reduce(function (acc, cur) {
3656
+ var key = isNaN(Number(cur)) ? cur : parseInt(cur);
3657
+ acc[key] = toTypeCoerceNumbers(data[0][cur]);
3658
+ return acc;
3659
+ }, {});
3660
+ };
3661
+
3662
+ var getOrderedDimensionKeys = function getOrderedDimensionKeys(config) {
3663
+ return function () {
3664
+ return Object.keys(config.dimensions).sort(function (x, y) {
3665
+ return ascending(config.dimensions[x].index, config.dimensions[y].index);
3666
+ });
3667
+ };
3668
+ };
3669
+
3670
+ var interactive = function interactive(flags) {
3671
+ return function () {
3672
+ flags.interactive = true;
3673
+ return this;
3674
+ };
3675
+ };
3676
+
3677
+ var shadows = function shadows(flags, pc) {
3678
+ return function () {
3679
+ flags.shadows = true;
3680
+ pc.alphaOnBrushed(0.1);
3681
+ pc.render();
3682
+ return this;
3683
+ };
3684
+ };
3685
+
3686
+ /**
3687
+ * Setup a new parallel coordinates chart.
3688
+ *
3689
+ * @param config
3690
+ * @param canvas
3691
+ * @param ctx
3692
+ * @returns {pc} a parcoords closure
3693
+ */
3694
+ var init = function init(config, canvas, ctx) {
3695
+ /**
3696
+ * Create the chart within a container. The selector can also be a d3 selection.
3697
+ *
3698
+ * @param selection a d3 selection
3699
+ * @returns {pc} instance for chained api
3700
+ */
3701
+ var _pc = function pc(selection) {
3702
+ selection = _pc.selection = select(selection);
3703
+ config.width = selection.node().clientWidth;
3704
+ config.height = selection.node().clientHeight;
3705
+ // canvas data layers
3706
+ ['dots', 'foreground', 'brushed', 'marked', 'highlight'].forEach(function (layer) {
3707
+ canvas[layer] = selection.append('canvas').attr('class', layer).node();
3708
+ ctx[layer] = canvas[layer].getContext('2d');
3709
+ });
3710
+
3711
+ // svg tick and brush layers
3712
+ _pc.svg = selection.append('svg').attr('width', config.width).attr('height', config.height).style('font', '14px sans-serif').style('position', 'absolute').append('svg:g').attr('transform', 'translate(' + config.margin.left + ',' + config.margin.top + ')');
3713
+ // for chained api
3714
+ return _pc;
3715
+ };
3716
+
3717
+ // for partial-application style programming
3718
+ return _pc;
3719
+ };
3720
+
3721
+ var flip = function flip(config) {
3722
+ return function (d) {
3723
+ //__.dimensions[d].yscale.domain().reverse(); // does not work
3724
+ config.dimensions[d].yscale.domain(config.dimensions[d].yscale.domain().reverse()); // works
3725
+
3726
+ return this;
3727
+ };
3728
+ };
3729
+
3730
+ var detectDimensions = function detectDimensions(pc) {
3731
+ return function () {
3732
+ pc.dimensions(pc.applyDimensionDefaults());
3733
+ return this;
3734
+ };
3735
+ };
3736
+
3737
+ var scale = function scale(config, pc) {
3738
+ return function (d, domain) {
3739
+ config.dimensions[d].yscale.domain(domain);
3740
+ pc.render["default"]();
3741
+ pc.updateAxes();
3742
+ return this;
3743
+ };
3744
+ };
3745
+
3746
+ const version = "3.0.0";
3747
+
3748
+ var DefaultConfig = {
3749
+ data: [],
3750
+ highlighted: [],
3751
+ marked: [],
3752
+ dimensions: {},
3753
+ dimensionTitleRotation: 0,
3754
+ brushes: [],
3755
+ brushed: false,
3756
+ brushedColor: null,
3757
+ alphaOnBrushed: 0.0,
3758
+ lineWidth: 1.4,
3759
+ highlightedLineWidth: 3,
3760
+ mode: 'default',
3761
+ markedLineWidth: 3,
3762
+ markedShadowColor: '#ffffff',
3763
+ markedShadowBlur: 10,
3764
+ rate: 20,
3765
+ width: 600,
3766
+ height: 300,
3767
+ margin: {
3768
+ top: 24,
3769
+ right: 20,
3770
+ bottom: 12,
3771
+ left: 20
3772
+ },
3773
+ nullValueSeparator: 'undefined',
3774
+ // set to "top" or "bottom"
3775
+ nullValueSeparatorPadding: {
3776
+ top: 8,
3777
+ right: 0,
3778
+ bottom: 8,
3779
+ left: 0
3780
+ },
3781
+ color: '#069',
3782
+ composite: 'source-over',
3783
+ alpha: 0.7,
3784
+ bundlingStrength: 0.5,
3785
+ bundleDimension: null,
3786
+ smoothness: 0.0,
3787
+ showControlPoints: false,
3788
+ hideAxis: [],
3789
+ flipAxes: [],
3790
+ animationTime: 1100,
3791
+ // How long it takes to flip the axis when you double click
3792
+ rotateLabels: false
3793
+ };
3794
+
3795
+ var _this$1 = undefined;
3796
+ var initState = function initState(userConfig) {
3797
+ var config = Object.assign({}, DefaultConfig, userConfig);
3798
+ if (userConfig && userConfig.dimensionTitles) {
3799
+ console.warn('dimensionTitles passed in userConfig is deprecated. Add title to dimension object.');
3800
+ Object.entries(userConfig.dimensionTitles).forEach(function (_ref) {
3801
+ var _ref2 = _slicedToArray(_ref, 2),
3802
+ key = _ref2[0],
3803
+ value = _ref2[1];
3804
+ if (config.dimensions[key]) {
3805
+ config.dimensions[key].title = config.dimensions[key].title ? config.dimensions[key].title : value;
3806
+ } else {
3807
+ config.dimensions[key] = {
3808
+ title: value
3809
+ };
3810
+ }
3811
+ });
3812
+ }
3813
+ var eventTypes = ['render', 'resize', 'highlight', 'mark', 'brush', 'brushend', 'brushstart', 'axesreorder'].concat(Object.keys(config));
3814
+ var events = dispatch.apply(_this$1, eventTypes),
3815
+ flags = {
3816
+ brushable: false,
3817
+ reorderable: false,
3818
+ axes: false,
3819
+ interactive: false,
3820
+ debug: false
3821
+ },
3822
+ xscale = scalePoint(),
3823
+ dragging = {},
3824
+ axis = axisLeft().ticks(5),
3825
+ ctx = {},
3826
+ canvas = {};
3827
+ var brush = {
3828
+ modes: {
3829
+ None: {
3830
+ install: function install(pc) {},
3831
+ // Nothing to be done.
3832
+ uninstall: function uninstall(pc) {},
3833
+ // Nothing to be done.
3834
+ selected: function selected() {
3835
+ return [];
3836
+ },
3837
+ // Nothing to return
3838
+ brushState: function brushState() {
3839
+ return {};
3840
+ }
3841
+ }
3842
+ },
3843
+ mode: 'None',
3844
+ predicate: 'AND',
3845
+ currentMode: function currentMode() {
3846
+ return this.modes[this.mode];
3847
+ }
3848
+ };
3849
+ return {
3850
+ config: config,
3851
+ events: events,
3852
+ eventTypes: eventTypes,
3853
+ flags: flags,
3854
+ xscale: xscale,
3855
+ dragging: dragging,
3856
+ axis: axis,
3857
+ ctx: ctx,
3858
+ canvas: canvas,
3859
+ brush: brush
3860
+ };
3861
+ };
3862
+
3863
+ var computeClusterCentroids = function computeClusterCentroids(config, d) {
3864
+ var clusterCentroids = new Map();
3865
+ var clusterCounts = new Map();
3866
+ // determine clusterCounts
3867
+ config.data.forEach(function (row) {
3868
+ var scaled = config.dimensions[d].yscale(row[d]);
3869
+ if (!clusterCounts.has(scaled)) {
3870
+ clusterCounts.set(scaled, 0);
3871
+ }
3872
+ var count = clusterCounts.get(scaled);
3873
+ clusterCounts.set(scaled, count + 1);
3874
+ });
3875
+ config.data.forEach(function (row) {
3876
+ Object.keys(config.dimensions).map(function (p) {
3877
+ var scaled = config.dimensions[d].yscale(row[d]);
3878
+ if (!clusterCentroids.has(scaled)) {
3879
+ var _map = new Map();
3880
+ clusterCentroids.set(scaled, _map);
3881
+ }
3882
+ if (!clusterCentroids.get(scaled).has(p)) {
3883
+ clusterCentroids.get(scaled).set(p, 0);
3884
+ }
3885
+ var value = clusterCentroids.get(scaled).get(p);
3886
+ value += config.dimensions[p].yscale(row[p]) / clusterCounts.get(scaled);
3887
+ clusterCentroids.get(scaled).set(p, value);
3888
+ });
3889
+ });
3890
+ return clusterCentroids;
3891
+ };
3892
+
3893
+ var _this = undefined;
3894
+ var without = function without(arr, items) {
3895
+ items.forEach(function (el) {
3896
+ delete arr[el];
3897
+ });
3898
+ return arr;
3899
+ };
3900
+ var sideEffects = function sideEffects(config, ctx, pc, xscale, axis, flags, brushedQueue, markedQueue, foregroundQueue) {
3901
+ return dispatch.apply(_this, Object.keys(config)).on('composite', function (d) {
3902
+ ctx.foreground.globalCompositeOperation = d.value;
3903
+ ctx.brushed.globalCompositeOperation = d.value;
3904
+ }).on('alpha', function (d) {
3905
+ ctx.foreground.globalAlpha = d.value;
3906
+ ctx.brushed.globalAlpha = d.value;
3907
+ }).on('brushedColor', function (d) {
3908
+ ctx.brushed.strokeStyle = d.value;
3909
+ }).on('width', function (d) {
3910
+ return pc.resize();
3911
+ }).on('height', function (d) {
3912
+ return pc.resize();
3913
+ }).on('margin', function (d) {
3914
+ return pc.resize();
3915
+ }).on('rate', function (d) {
3916
+ brushedQueue.rate(d.value);
3917
+ markedQueue.rate(d.value);
3918
+ foregroundQueue.rate(d.value);
3919
+ }).on('dimensions', function (d) {
3920
+ config.dimensions = pc.applyDimensionDefaults(Object.keys(d.value));
3921
+ xscale.domain(pc.getOrderedDimensionKeys());
3922
+ pc.sortDimensions();
3923
+ if (flags.interactive) {
3924
+ pc.render().updateAxes();
3925
+ }
3926
+ }).on('bundleDimension', function (d) {
3927
+ if (!Object.keys(config.dimensions).length) pc.detectDimensions();
3928
+ pc.autoscale();
3929
+ if (typeof d.value === 'number') {
3930
+ if (d.value < Object.keys(config.dimensions).length) {
3931
+ config.bundleDimension = config.dimensions[d.value];
3932
+ } else if (d.value < config.hideAxis.length) {
3933
+ config.bundleDimension = config.hideAxis[d.value];
3934
+ }
3935
+ } else {
3936
+ config.bundleDimension = d.value;
3937
+ }
3938
+ config.clusterCentroids = computeClusterCentroids(config, config.bundleDimension);
3939
+ if (flags.interactive) {
3940
+ pc.render();
3941
+ }
3942
+ }).on('hideAxis', function (d) {
3943
+ pc.brushReset();
3944
+ pc.dimensions(pc.applyDimensionDefaults());
3945
+ pc.dimensions(without(config.dimensions, d.value));
3946
+ pc.render();
3947
+ }).on('flipAxes', function (d) {
3948
+ if (d.value && d.value.length) {
3949
+ d.value.forEach(function (dimension) {
3950
+ flipAxisAndUpdatePCP(config, pc, axis)(dimension);
3951
+ });
3952
+ pc.updateAxes(0);
3953
+ }
3954
+ });
3955
+ };
3956
+
3957
+ var getset = function getset(obj, state, events, side_effects) {
3958
+ Object.keys(state).forEach(function (key) {
3959
+ obj[key] = function (x) {
3960
+ if (!arguments.length) {
3961
+ return state[key];
3962
+ }
3963
+ if (key === 'dimensions' && Object.prototype.toString.call(x) === '[object Array]') {
3964
+ console.warn('pc.dimensions([]) is deprecated, use pc.dimensions({})');
3965
+ x = obj.applyDimensionDefaults(x);
3966
+ }
3967
+ var old = state[key];
3968
+ state[key] = x;
3969
+ side_effects.call(key, obj, {
3970
+ value: x,
3971
+ previous: old
3972
+ });
3973
+ events.call(key, obj, {
3974
+ value: x,
3975
+ previous: old
3976
+ });
3977
+ return obj;
3978
+ };
3979
+ });
3980
+ };
3981
+
3982
+ // side effects for setters
3983
+ var d3_rebind = function d3_rebind(target, source, method) {
3984
+ return function () {
3985
+ var value = method.apply(source, arguments);
3986
+ return value === source ? target : value;
3987
+ };
3988
+ };
3989
+ var _rebind = function _rebind(target, source, method) {
3990
+ target[method] = d3_rebind(target, source, source[method]);
3991
+ return target;
3992
+ };
3993
+ var bindEvents = function bindEvents(__, ctx, pc, xscale, flags, brushedQueue, markedQueue, foregroundQueue, events, axis) {
3994
+ var side_effects = sideEffects(__, ctx, pc, xscale, axis, flags, brushedQueue, markedQueue, foregroundQueue);
3995
+
3996
+ // create getter/setters
3997
+ getset(pc, __, events, side_effects);
3998
+
3999
+ // expose events
4000
+ // getter/setter with event firing
4001
+ _rebind(pc, events, 'on');
4002
+ _rebind(pc, axis, 'ticks');
4003
+ };
4004
+
4005
+ // misc
4006
+ var ParCoords = function ParCoords(userConfig) {
4007
+ var state = initState(userConfig);
4008
+ var config = state.config,
4009
+ events = state.events,
4010
+ flags = state.flags,
4011
+ xscale = state.xscale,
4012
+ dragging = state.dragging,
4013
+ axis = state.axis,
4014
+ ctx = state.ctx,
4015
+ canvas = state.canvas,
4016
+ brush = state.brush;
4017
+ var pc = init(config, canvas, ctx);
4018
+ var position = function position(d) {
4019
+ if (xscale.range().length === 0) {
4020
+ xscale.range([0, w(config)], 1);
4021
+ }
4022
+ return dragging[d] == null ? xscale(d) : dragging[d];
4023
+ };
4024
+ var brushedQueue = renderQueue(pathBrushed(config, ctx, position)).rate(50).clear(function () {
4025
+ return pc.clear('brushed');
4026
+ });
4027
+ var markedQueue = renderQueue(pathMark(config, ctx, position)).rate(50).clear(function () {
4028
+ return pc.clear('marked');
4029
+ });
4030
+ var foregroundQueue = renderQueue(pathForeground(config, ctx, position)).rate(50).clear(function () {
4031
+ pc.clear('foreground');
4032
+ pc.clear('highlight');
4033
+ });
4034
+ bindEvents(config, ctx, pc, xscale, flags, brushedQueue, markedQueue, foregroundQueue, events, axis);
4035
+
4036
+ // expose the state of the chart
4037
+ pc.state = config;
4038
+ pc.flags = flags;
4039
+ pc.autoscale = autoscale(config, pc, xscale, ctx);
4040
+ pc.scale = scale(config, pc);
4041
+ pc.flip = flip(config);
4042
+ pc.commonScale = commonScale(config, pc);
4043
+ pc.detectDimensions = detectDimensions(pc);
4044
+ // attempt to determine types of each dimension based on first row of data
4045
+ pc.detectDimensionTypes = detectDimensionTypes;
4046
+ pc.applyDimensionDefaults = applyDimensionDefaults(config, pc);
4047
+ pc.getOrderedDimensionKeys = getOrderedDimensionKeys(config);
4048
+
4049
+ //Renders the polylines.
4050
+ pc.render = render(config, pc, events);
4051
+ pc.renderBrushed = renderBrushed(config, pc, events);
4052
+ pc.renderMarked = renderMarked(config, pc, events);
4053
+ pc.render["default"] = renderDefault(config, pc, ctx, position);
4054
+ pc.render.queue = renderDefaultQueue(config, pc, foregroundQueue);
4055
+ pc.renderBrushed["default"] = renderBrushedDefault(config, ctx, position, pc, brush);
4056
+ pc.renderBrushed.queue = renderBrushedQueue(config, brush, brushedQueue);
4057
+ pc.renderMarked["default"] = renderMarkedDefault(config, pc, ctx, position);
4058
+ pc.renderMarked.queue = renderMarkedQueue(config, markedQueue);
4059
+ pc.compute_real_centroids = computeRealCentroids(config, position);
4060
+ pc.shadows = shadows(flags, pc);
4061
+ pc.axisDots = axisDots(config, pc, position);
4062
+ pc.clear = clear(config, pc, ctx, brush);
4063
+ pc.createAxes = createAxes(config, pc, xscale, flags, axis);
4064
+ pc.removeAxes = removeAxes(pc);
4065
+ pc.updateAxes = updateAxes(config, pc, position, axis, flags);
4066
+ pc.applyAxisConfig = applyAxisConfig;
4067
+ pc.brushable = brushable(config, pc, flags);
4068
+ pc.brushReset = brushReset(config, pc);
4069
+ pc.selected = selected(config, pc);
4070
+ pc.reorderable = reorderable(config, pc, xscale, position, dragging, flags);
4071
+
4072
+ // Reorder dimensions, such that the highest value (visually) is on the left and
4073
+ // the lowest on the right. Visual values are determined by the data values in
4074
+ // the given row.
4075
+ pc.reorder = reorder(config, pc, xscale);
4076
+ pc.sortDimensionsByRowData = sortDimensionsByRowData(config);
4077
+ pc.sortDimensions = sortDimensions(config, position);
4078
+
4079
+ // pairs of adjacent dimensions
4080
+ pc.adjacent_pairs = adjacentPairs;
4081
+ pc.interactive = interactive(flags);
4082
+
4083
+ // expose internal state
4084
+ pc.xscale = xscale;
4085
+ pc.ctx = ctx;
4086
+ pc.canvas = canvas;
4087
+ pc.g = function () {
4088
+ return pc._g;
4089
+ };
4090
+
4091
+ // rescale for height, width and margins
4092
+ // TODO currently assumes chart is brushable, and destroys old brushes
4093
+ pc.resize = resize(config, pc, flags, events);
4094
+
4095
+ // highlight an array of data
4096
+ pc.highlight = highlight(config, pc, canvas, events, ctx, position);
4097
+ // clear highlighting
4098
+ pc.unhighlight = unhighlight(config, pc, canvas);
4099
+
4100
+ // mark an array of data
4101
+ pc.mark = mark(config, pc, canvas, events, ctx, position);
4102
+ // clear marked data
4103
+ pc.unmark = unmark(config, pc, canvas);
4104
+
4105
+ // calculate 2d intersection of line a->b with line c->d
4106
+ // points are objects with x and y properties
4107
+ pc.intersection = intersection;
4108
+
4109
+ // Merges the canvases and SVG elements into one canvas element which is then passed into the callback
4110
+ // (so you can choose to save it to disk, etc.)
4111
+ pc.mergeParcoords = mergeParcoords(pc);
4112
+ pc.brushModes = function () {
4113
+ return Object.getOwnPropertyNames(brush.modes);
4114
+ };
4115
+ pc.brushMode = brushMode(brush, config, pc);
4116
+
4117
+ // install brushes
4118
+ install1DAxes(brush, config, pc, events);
4119
+ install2DStrums(brush, config, pc, events, xscale);
4120
+ installAngularBrush(brush, config, pc, events, xscale);
4121
+ install1DMultiAxes(brush, config, pc, events);
4122
+ pc.version = version;
4123
+ // this descriptive text should live with other introspective methods
4124
+ pc.toString = toString(config);
4125
+ pc.toType = toType;
4126
+ // try to coerce to number before returning type
4127
+ pc.toTypeCoerceNumbers = toTypeCoerceNumbers;
4128
+ return pc;
4129
+ };
4130
+
4131
+ export { ParCoords as default };
4132
+ //# sourceMappingURL=parcoords.esm.js.map