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