@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.
- package/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/parcoords.css +61 -0
- package/dist/parcoords.esm.js +4132 -0
- package/dist/parcoords.esm.js.map +1 -0
- package/dist/parcoords.js +4130 -0
- package/dist/parcoords.js.map +1 -0
- package/dist/parcoords.standalone.js +11233 -0
- package/dist/parcoords.standalone.js.map +1 -0
- package/package.json +89 -0
- package/src/api/adjacentPairs.js +10 -0
- package/src/api/applyAxisConfig.js +35 -0
- package/src/api/applyDimensionDefaults.js +25 -0
- package/src/api/autoscale.js +133 -0
- package/src/api/axisDots.js +27 -0
- package/src/api/brushMode.js +50 -0
- package/src/api/brushReset.js +34 -0
- package/src/api/brushable.js +81 -0
- package/src/api/clear.js +21 -0
- package/src/api/commonScale.js +45 -0
- package/src/api/computeRealCentroids.js +8 -0
- package/src/api/createAxes.js +101 -0
- package/src/api/detectDimensionTypes.js +12 -0
- package/src/api/detectDimensions.js +7 -0
- package/src/api/flip.js +11 -0
- package/src/api/getOrderedDimensionKeys.js +8 -0
- package/src/api/highlight.js +26 -0
- package/src/api/init.js +53 -0
- package/src/api/interactive.js +7 -0
- package/src/api/intersection.js +16 -0
- package/src/api/mark.js +20 -0
- package/src/api/mergeParcoords.js +86 -0
- package/src/api/removeAxes.js +9 -0
- package/src/api/render.js +26 -0
- package/src/api/renderBrushed.js +39 -0
- package/src/api/renderDefault.js +27 -0
- package/src/api/renderMarked.js +34 -0
- package/src/api/reorder.js +37 -0
- package/src/api/reorderable.js +41 -0
- package/src/api/resize.js +36 -0
- package/src/api/scale.js +10 -0
- package/src/api/selected.js +209 -0
- package/src/api/shadows.js +9 -0
- package/src/api/sortDimensions.js +13 -0
- package/src/api/sortDimensionsByRowData.js +20 -0
- package/src/api/toString.js +11 -0
- package/src/api/toType.js +9 -0
- package/src/api/toTypeCoerceNumbers.js +7 -0
- package/src/api/unhighlight.js +12 -0
- package/src/api/unmark.js +12 -0
- package/src/api/updateAxes.js +103 -0
- package/src/bindEvents.js +60 -0
- package/src/brush/1d/brushExtents.js +73 -0
- package/src/brush/1d/brushFor.js +92 -0
- package/src/brush/1d/brushReset.js +36 -0
- package/src/brush/1d/index.js +20 -0
- package/src/brush/1d/install.js +41 -0
- package/src/brush/1d/selected.js +79 -0
- package/src/brush/1d/uninstall.js +12 -0
- package/src/brush/1d-multi/brushExtents.js +101 -0
- package/src/brush/1d-multi/brushFor.js +13 -0
- package/src/brush/1d-multi/brushReset.js +53 -0
- package/src/brush/1d-multi/drawBrushes.js +36 -0
- package/src/brush/1d-multi/index.js +20 -0
- package/src/brush/1d-multi/install.js +29 -0
- package/src/brush/1d-multi/newBrush.js +102 -0
- package/src/brush/1d-multi/selected.js +160 -0
- package/src/brush/1d-multi/uninstall.js +12 -0
- package/src/brush/angular/brushReset.js +14 -0
- package/src/brush/angular/index.js +19 -0
- package/src/brush/angular/install.js +141 -0
- package/src/brush/angular/onDrag.js +109 -0
- package/src/brush/angular/onDragEnd.js +30 -0
- package/src/brush/angular/onDragStart.js +38 -0
- package/src/brush/angular/removeStrum.js +12 -0
- package/src/brush/angular/selected.js +63 -0
- package/src/brush/angular/uninstall.js +17 -0
- package/src/brush/angular/util/hypothenuse.js +3 -0
- package/src/brush/consecutive.js +11 -0
- package/src/brush/dimensionsForPoint.js +32 -0
- package/src/brush/invertByScale.js +24 -0
- package/src/brush/strums/brushReset.js +14 -0
- package/src/brush/strums/index.js +19 -0
- package/src/brush/strums/install.js +81 -0
- package/src/brush/strums/onDrag.js +83 -0
- package/src/brush/strums/onDragEnd.js +20 -0
- package/src/brush/strums/onDragStart.js +33 -0
- package/src/brush/strums/removeStrum.js +10 -0
- package/src/brush/strums/selected.js +52 -0
- package/src/brush/strums/uninstall.js +16 -0
- package/src/index.js +226 -0
- package/src/parallel-coordinates.css +61 -0
- package/src/state/defaultConfig.js +36 -0
- package/src/state/index.js +86 -0
- package/src/state/sideEffects.js +89 -0
- package/src/util/colorPath.js +71 -0
- package/src/util/computeCentroids.js +44 -0
- package/src/util/computeClusterCentroids.js +33 -0
- package/src/util/computeControlPoints.js +38 -0
- package/src/util/dimensionLabels.js +11 -0
- package/src/util/flipAxisAndUpdatePCP.js +19 -0
- package/src/util/functor.js +3 -0
- package/src/util/getRange.js +24 -0
- package/src/util/getset.js +23 -0
- package/src/util/height.js +3 -0
- package/src/util/isBrushed.js +15 -0
- package/src/util/renderQueue.js +67 -0
- package/src/util/rotateLabels.js +18 -0
- package/src/util/width.js +3 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { select } from 'd3-selection';
|
|
2
|
+
import { drag } from 'd3-drag';
|
|
3
|
+
import onDragEnd from './onDragEnd';
|
|
4
|
+
|
|
5
|
+
const drawStrum = (
|
|
6
|
+
brushGroup,
|
|
7
|
+
state,
|
|
8
|
+
config,
|
|
9
|
+
pc,
|
|
10
|
+
events,
|
|
11
|
+
strum,
|
|
12
|
+
activePoint
|
|
13
|
+
) => {
|
|
14
|
+
let _svg = pc.selection.select('svg').select('g#strums'),
|
|
15
|
+
id = strum.dims.i,
|
|
16
|
+
points = [strum.p1, strum.p2],
|
|
17
|
+
_line = _svg.selectAll('line#strum-' + id).data([strum]),
|
|
18
|
+
circles = _svg.selectAll('circle#strum-' + id).data(points),
|
|
19
|
+
_drag = drag();
|
|
20
|
+
|
|
21
|
+
_line
|
|
22
|
+
.enter()
|
|
23
|
+
.append('line')
|
|
24
|
+
.attr('id', 'strum-' + id)
|
|
25
|
+
.attr('class', 'strum');
|
|
26
|
+
|
|
27
|
+
_line
|
|
28
|
+
.attr('x1', d => d.p1[0])
|
|
29
|
+
.attr('y1', d => d.p1[1])
|
|
30
|
+
.attr('x2', d => d.p2[0])
|
|
31
|
+
.attr('y2', d => d.p2[1])
|
|
32
|
+
.attr('stroke', 'black')
|
|
33
|
+
.attr('stroke-width', 2);
|
|
34
|
+
|
|
35
|
+
_drag
|
|
36
|
+
.on('drag', function(event, d) {
|
|
37
|
+
const i = points.indexOf(d) + 1;
|
|
38
|
+
strum['p' + i][0] = Math.min(Math.max(strum.minX + 1, event.x), strum.maxX);
|
|
39
|
+
strum['p' + i][1] = Math.min(Math.max(strum.minY, event.y), strum.maxY);
|
|
40
|
+
drawStrum(brushGroup, state, config, pc, events, strum, i - 1);
|
|
41
|
+
})
|
|
42
|
+
.on('end', onDragEnd(brushGroup, state, config, pc, events));
|
|
43
|
+
|
|
44
|
+
circles
|
|
45
|
+
.enter()
|
|
46
|
+
.append('circle')
|
|
47
|
+
.attr('id', 'strum-' + id)
|
|
48
|
+
.attr('class', 'strum');
|
|
49
|
+
|
|
50
|
+
circles
|
|
51
|
+
.attr('cx', d => d[0])
|
|
52
|
+
.attr('cy', d => d[1])
|
|
53
|
+
.attr('r', 5)
|
|
54
|
+
.style(
|
|
55
|
+
'opacity',
|
|
56
|
+
(d, i) => (activePoint !== undefined && i === activePoint ? 0.8 : 0)
|
|
57
|
+
)
|
|
58
|
+
.on('mouseover', function() {
|
|
59
|
+
select(this).style('opacity', 0.8);
|
|
60
|
+
})
|
|
61
|
+
.on('mouseout', function() {
|
|
62
|
+
select(this).style('opacity', 0);
|
|
63
|
+
})
|
|
64
|
+
.call(_drag);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const onDrag = (brushGroup, state, config, pc, events) => (event) => {
|
|
68
|
+
const strum = state.strums[state.strums.active];
|
|
69
|
+
|
|
70
|
+
// Make sure that the point is within the bounds
|
|
71
|
+
strum.p2[0] = Math.min(
|
|
72
|
+
Math.max(strum.minX + 1, event.x - config.margin.left),
|
|
73
|
+
strum.maxX
|
|
74
|
+
);
|
|
75
|
+
strum.p2[1] = Math.min(
|
|
76
|
+
Math.max(strum.minY, event.y - config.margin.top),
|
|
77
|
+
strum.maxY
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
drawStrum(brushGroup, state, config, pc, events, strum, 1);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default onDrag;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import selected from './selected';
|
|
2
|
+
import removeStrum from './removeStrum';
|
|
3
|
+
|
|
4
|
+
const onDragEnd = (brushGroup, state, config, pc, events) => () => {
|
|
5
|
+
const strum = state.strums[state.strums.active];
|
|
6
|
+
|
|
7
|
+
// Okay, somewhat unexpected, but not totally unsurprising, a mousclick is
|
|
8
|
+
// considered a drag without move. So we have to deal with that case
|
|
9
|
+
if (strum && strum.p1[0] === strum.p2[0] && strum.p1[1] === strum.p2[1]) {
|
|
10
|
+
removeStrum(state, pc);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const brushed = selected(brushGroup, state, config);
|
|
14
|
+
state.strums.active = undefined;
|
|
15
|
+
config.brushed = brushed;
|
|
16
|
+
pc.renderBrushed();
|
|
17
|
+
events.call('brushend', pc, config.brushed);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default onDragEnd;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { pointer } from 'd3-selection';
|
|
2
|
+
import h from '../../util/height';
|
|
3
|
+
import dimensionsForPoint from '../dimensionsForPoint';
|
|
4
|
+
|
|
5
|
+
// First we need to determine between which two axes the sturm was started.
|
|
6
|
+
// This will determine the freedom of movement, because a strum can
|
|
7
|
+
// logically only happen between two axes, so no movement outside these axes
|
|
8
|
+
// should be allowed.
|
|
9
|
+
const onDragStart = (state, config, pc, xscale) => (event) => {
|
|
10
|
+
let p = pointer(event, state.strumRect.node());
|
|
11
|
+
|
|
12
|
+
p[0] = p[0] - config.margin.left;
|
|
13
|
+
p[1] = p[1] - config.margin.top;
|
|
14
|
+
|
|
15
|
+
const dims = dimensionsForPoint(config, pc, xscale, p);
|
|
16
|
+
const strum = {
|
|
17
|
+
p1: p,
|
|
18
|
+
dims: dims,
|
|
19
|
+
minX: xscale(dims.left),
|
|
20
|
+
maxX: xscale(dims.right),
|
|
21
|
+
minY: 0,
|
|
22
|
+
maxY: h(config),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Make sure that the point is within the bounds
|
|
26
|
+
strum.p1[0] = Math.min(Math.max(strum.minX, p[0]), strum.maxX);
|
|
27
|
+
strum.p2 = strum.p1.slice();
|
|
28
|
+
|
|
29
|
+
state.strums[dims.i] = strum;
|
|
30
|
+
state.strums.active = dims.i;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default onDragStart;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const removeStrum = (state, pc) => {
|
|
2
|
+
const strum = state.strums[state.strums.active],
|
|
3
|
+
svg = pc.selection.select('svg').select('g#strums');
|
|
4
|
+
|
|
5
|
+
delete state.strums[state.strums.active];
|
|
6
|
+
svg.selectAll('line#strum-' + strum.dims.i).remove();
|
|
7
|
+
svg.selectAll('circle#strum-' + strum.dims.i).remove();
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default removeStrum;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// test if point falls between lines
|
|
2
|
+
const containmentTest = (strum, width) => p => {
|
|
3
|
+
const p1 = [strum.p1[0] - strum.minX, strum.p1[1] - strum.minX],
|
|
4
|
+
p2 = [strum.p2[0] - strum.minX, strum.p2[1] - strum.minX],
|
|
5
|
+
m1 = 1 - width / p1[0],
|
|
6
|
+
b1 = p1[1] * (1 - m1),
|
|
7
|
+
m2 = 1 - width / p2[0],
|
|
8
|
+
b2 = p2[1] * (1 - m2);
|
|
9
|
+
|
|
10
|
+
const x = p[0],
|
|
11
|
+
y = p[1],
|
|
12
|
+
y1 = m1 * x + b1,
|
|
13
|
+
y2 = m2 * x + b2;
|
|
14
|
+
|
|
15
|
+
return y > Math.min(y1, y2) && y < Math.max(y1, y2);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const crossesStrum = (state, config) => (d, id) => {
|
|
19
|
+
let strum = state.strums[id],
|
|
20
|
+
test = containmentTest(strum, state.strums.width(id)),
|
|
21
|
+
d1 = strum.dims.left,
|
|
22
|
+
d2 = strum.dims.right,
|
|
23
|
+
y1 = config.dimensions[d1].yscale,
|
|
24
|
+
y2 = config.dimensions[d2].yscale,
|
|
25
|
+
point = [y1(d[d1]) - strum.minX, y2(d[d2]) - strum.minX];
|
|
26
|
+
return test(point);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const selected = (brushGroup, state, config) => {
|
|
30
|
+
// Get the ids of the currently active strums.
|
|
31
|
+
const ids = Object.getOwnPropertyNames(state.strums).filter(d => !isNaN(d)),
|
|
32
|
+
brushed = config.data;
|
|
33
|
+
|
|
34
|
+
if (ids.length === 0) {
|
|
35
|
+
return brushed;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const crossTest = crossesStrum(state, config);
|
|
39
|
+
|
|
40
|
+
return brushed.filter(d => {
|
|
41
|
+
switch (brushGroup.predicate) {
|
|
42
|
+
case 'AND':
|
|
43
|
+
return ids.every(id => crossTest(d, id));
|
|
44
|
+
case 'OR':
|
|
45
|
+
return ids.some(id => crossTest(d, id));
|
|
46
|
+
default:
|
|
47
|
+
throw new Error('Unknown brush predicate ' + config.brushPredicate);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default selected;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const uninstall = (state, pc) => () => {
|
|
2
|
+
pc.selection
|
|
3
|
+
.select('svg')
|
|
4
|
+
.select('g#strums')
|
|
5
|
+
.remove();
|
|
6
|
+
pc.selection
|
|
7
|
+
.select('svg')
|
|
8
|
+
.select('rect#strum-events')
|
|
9
|
+
.remove();
|
|
10
|
+
pc.on('axesreorder.strums', undefined);
|
|
11
|
+
delete pc.brushReset;
|
|
12
|
+
|
|
13
|
+
state.strumRect = undefined;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default uninstall;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// misc
|
|
2
|
+
import renderQueue from './util/renderQueue';
|
|
3
|
+
import w from './util/width';
|
|
4
|
+
|
|
5
|
+
// brush
|
|
6
|
+
import install1DAxes from './brush/1d';
|
|
7
|
+
import install1DAxesMulti from './brush/1d-multi';
|
|
8
|
+
import install2DStrums from './brush/strums';
|
|
9
|
+
import installAngularBrush from './brush/angular';
|
|
10
|
+
|
|
11
|
+
// api
|
|
12
|
+
import intersection from './api/intersection';
|
|
13
|
+
import mergeParcoords from './api/mergeParcoords';
|
|
14
|
+
import selected from './api/selected';
|
|
15
|
+
import brushMode from './api/brushMode';
|
|
16
|
+
import updateAxes from './api/updateAxes';
|
|
17
|
+
import autoscale from './api/autoscale';
|
|
18
|
+
import brushable from './api/brushable';
|
|
19
|
+
import commonScale from './api/commonScale';
|
|
20
|
+
import computeRealCentroids from './api/computeRealCentroids';
|
|
21
|
+
import applyDimensionDefaults from './api/applyDimensionDefaults';
|
|
22
|
+
import createAxes from './api/createAxes';
|
|
23
|
+
import axisDots from './api/axisDots';
|
|
24
|
+
import applyAxisConfig from './api/applyAxisConfig';
|
|
25
|
+
import reorderable from './api/reorderable';
|
|
26
|
+
import resize from './api/resize';
|
|
27
|
+
import reorder from './api/reorder';
|
|
28
|
+
import sortDimensions from './api/sortDimensions';
|
|
29
|
+
import sortDimensionsByRowData from './api/sortDimensionsByRowData';
|
|
30
|
+
import clear from './api/clear';
|
|
31
|
+
import {
|
|
32
|
+
pathMark,
|
|
33
|
+
renderMarked,
|
|
34
|
+
renderMarkedDefault,
|
|
35
|
+
renderMarkedQueue,
|
|
36
|
+
} from './api/renderMarked';
|
|
37
|
+
import {
|
|
38
|
+
pathBrushed,
|
|
39
|
+
renderBrushed,
|
|
40
|
+
renderBrushedDefault,
|
|
41
|
+
renderBrushedQueue,
|
|
42
|
+
} from './api/renderBrushed';
|
|
43
|
+
import brushReset from './api/brushReset';
|
|
44
|
+
import toType from './api/toType';
|
|
45
|
+
import toString from './api/toString';
|
|
46
|
+
import adjacentPairs from './api/adjacentPairs';
|
|
47
|
+
import highlight from './api/highlight';
|
|
48
|
+
import unhighlight from './api/unhighlight';
|
|
49
|
+
import mark from './api/mark';
|
|
50
|
+
import unmark from './api/unmark';
|
|
51
|
+
import removeAxes from './api/removeAxes';
|
|
52
|
+
import render from './api/render';
|
|
53
|
+
import renderDefault, {
|
|
54
|
+
pathForeground,
|
|
55
|
+
renderDefaultQueue,
|
|
56
|
+
} from './api/renderDefault';
|
|
57
|
+
import toTypeCoerceNumbers from './api/toTypeCoerceNumbers';
|
|
58
|
+
import detectDimensionTypes from './api/detectDimensionTypes';
|
|
59
|
+
import getOrderedDimensionKeys from './api/getOrderedDimensionKeys';
|
|
60
|
+
import interactive from './api/interactive';
|
|
61
|
+
import shadows from './api/shadows';
|
|
62
|
+
import init from './api/init';
|
|
63
|
+
import flip from './api/flip';
|
|
64
|
+
import detectDimensions from './api/detectDimensions';
|
|
65
|
+
import scale from './api/scale';
|
|
66
|
+
|
|
67
|
+
import { version } from '../package.json';
|
|
68
|
+
import initState from './state';
|
|
69
|
+
import bindEvents from './bindEvents';
|
|
70
|
+
|
|
71
|
+
//css
|
|
72
|
+
import './parallel-coordinates.css';
|
|
73
|
+
|
|
74
|
+
const ParCoords = userConfig => {
|
|
75
|
+
const state = initState(userConfig);
|
|
76
|
+
const {
|
|
77
|
+
config,
|
|
78
|
+
events,
|
|
79
|
+
flags,
|
|
80
|
+
xscale,
|
|
81
|
+
dragging,
|
|
82
|
+
axis,
|
|
83
|
+
ctx,
|
|
84
|
+
canvas,
|
|
85
|
+
brush,
|
|
86
|
+
} = state;
|
|
87
|
+
|
|
88
|
+
const pc = init(config, canvas, ctx);
|
|
89
|
+
|
|
90
|
+
const position = d => {
|
|
91
|
+
if (xscale.range().length === 0) {
|
|
92
|
+
xscale.range([0, w(config)], 1);
|
|
93
|
+
}
|
|
94
|
+
return dragging[d] == null ? xscale(d) : dragging[d];
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const brushedQueue = renderQueue(pathBrushed(config, ctx, position))
|
|
98
|
+
.rate(50)
|
|
99
|
+
.clear(() => pc.clear('brushed'));
|
|
100
|
+
|
|
101
|
+
const markedQueue = renderQueue(pathMark(config, ctx, position))
|
|
102
|
+
.rate(50)
|
|
103
|
+
.clear(() => pc.clear('marked'));
|
|
104
|
+
|
|
105
|
+
const foregroundQueue = renderQueue(pathForeground(config, ctx, position))
|
|
106
|
+
.rate(50)
|
|
107
|
+
.clear(function() {
|
|
108
|
+
pc.clear('foreground');
|
|
109
|
+
pc.clear('highlight');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
bindEvents(
|
|
113
|
+
config,
|
|
114
|
+
ctx,
|
|
115
|
+
pc,
|
|
116
|
+
xscale,
|
|
117
|
+
flags,
|
|
118
|
+
brushedQueue,
|
|
119
|
+
markedQueue,
|
|
120
|
+
foregroundQueue,
|
|
121
|
+
events,
|
|
122
|
+
axis
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// expose the state of the chart
|
|
126
|
+
pc.state = config;
|
|
127
|
+
pc.flags = flags;
|
|
128
|
+
|
|
129
|
+
pc.autoscale = autoscale(config, pc, xscale, ctx);
|
|
130
|
+
pc.scale = scale(config, pc);
|
|
131
|
+
pc.flip = flip(config);
|
|
132
|
+
pc.commonScale = commonScale(config, pc);
|
|
133
|
+
pc.detectDimensions = detectDimensions(pc);
|
|
134
|
+
// attempt to determine types of each dimension based on first row of data
|
|
135
|
+
pc.detectDimensionTypes = detectDimensionTypes;
|
|
136
|
+
pc.applyDimensionDefaults = applyDimensionDefaults(config, pc);
|
|
137
|
+
pc.getOrderedDimensionKeys = getOrderedDimensionKeys(config);
|
|
138
|
+
|
|
139
|
+
//Renders the polylines.
|
|
140
|
+
pc.render = render(config, pc, events);
|
|
141
|
+
pc.renderBrushed = renderBrushed(config, pc, events);
|
|
142
|
+
pc.renderMarked = renderMarked(config, pc, events);
|
|
143
|
+
pc.render.default = renderDefault(config, pc, ctx, position);
|
|
144
|
+
pc.render.queue = renderDefaultQueue(config, pc, foregroundQueue);
|
|
145
|
+
pc.renderBrushed.default = renderBrushedDefault(
|
|
146
|
+
config,
|
|
147
|
+
ctx,
|
|
148
|
+
position,
|
|
149
|
+
pc,
|
|
150
|
+
brush
|
|
151
|
+
);
|
|
152
|
+
pc.renderBrushed.queue = renderBrushedQueue(config, brush, brushedQueue);
|
|
153
|
+
pc.renderMarked.default = renderMarkedDefault(config, pc, ctx, position);
|
|
154
|
+
pc.renderMarked.queue = renderMarkedQueue(config, markedQueue);
|
|
155
|
+
|
|
156
|
+
pc.compute_real_centroids = computeRealCentroids(config, position);
|
|
157
|
+
pc.shadows = shadows(flags, pc);
|
|
158
|
+
pc.axisDots = axisDots(config, pc, position);
|
|
159
|
+
pc.clear = clear(config, pc, ctx, brush);
|
|
160
|
+
pc.createAxes = createAxes(config, pc, xscale, flags, axis);
|
|
161
|
+
pc.removeAxes = removeAxes(pc);
|
|
162
|
+
pc.updateAxes = updateAxes(config, pc, position, axis, flags);
|
|
163
|
+
pc.applyAxisConfig = applyAxisConfig;
|
|
164
|
+
pc.brushable = brushable(config, pc, flags);
|
|
165
|
+
pc.brushReset = brushReset(config, pc);
|
|
166
|
+
pc.selected = selected(config, pc);
|
|
167
|
+
pc.reorderable = reorderable(config, pc, xscale, position, dragging, flags);
|
|
168
|
+
|
|
169
|
+
// Reorder dimensions, such that the highest value (visually) is on the left and
|
|
170
|
+
// the lowest on the right. Visual values are determined by the data values in
|
|
171
|
+
// the given row.
|
|
172
|
+
pc.reorder = reorder(config, pc, xscale);
|
|
173
|
+
pc.sortDimensionsByRowData = sortDimensionsByRowData(config);
|
|
174
|
+
pc.sortDimensions = sortDimensions(config, position);
|
|
175
|
+
|
|
176
|
+
// pairs of adjacent dimensions
|
|
177
|
+
pc.adjacent_pairs = adjacentPairs;
|
|
178
|
+
pc.interactive = interactive(flags);
|
|
179
|
+
|
|
180
|
+
// expose internal state
|
|
181
|
+
pc.xscale = xscale;
|
|
182
|
+
pc.ctx = ctx;
|
|
183
|
+
pc.canvas = canvas;
|
|
184
|
+
pc.g = () => pc._g;
|
|
185
|
+
|
|
186
|
+
// rescale for height, width and margins
|
|
187
|
+
// TODO currently assumes chart is brushable, and destroys old brushes
|
|
188
|
+
pc.resize = resize(config, pc, flags, events);
|
|
189
|
+
|
|
190
|
+
// highlight an array of data
|
|
191
|
+
pc.highlight = highlight(config, pc, canvas, events, ctx, position);
|
|
192
|
+
// clear highlighting
|
|
193
|
+
pc.unhighlight = unhighlight(config, pc, canvas);
|
|
194
|
+
|
|
195
|
+
// mark an array of data
|
|
196
|
+
pc.mark = mark(config, pc, canvas, events, ctx, position);
|
|
197
|
+
// clear marked data
|
|
198
|
+
pc.unmark = unmark(config, pc, canvas);
|
|
199
|
+
|
|
200
|
+
// calculate 2d intersection of line a->b with line c->d
|
|
201
|
+
// points are objects with x and y properties
|
|
202
|
+
pc.intersection = intersection;
|
|
203
|
+
|
|
204
|
+
// Merges the canvases and SVG elements into one canvas element which is then passed into the callback
|
|
205
|
+
// (so you can choose to save it to disk, etc.)
|
|
206
|
+
pc.mergeParcoords = mergeParcoords(pc);
|
|
207
|
+
pc.brushModes = () => Object.getOwnPropertyNames(brush.modes);
|
|
208
|
+
pc.brushMode = brushMode(brush, config, pc);
|
|
209
|
+
|
|
210
|
+
// install brushes
|
|
211
|
+
install1DAxes(brush, config, pc, events);
|
|
212
|
+
install2DStrums(brush, config, pc, events, xscale);
|
|
213
|
+
installAngularBrush(brush, config, pc, events, xscale);
|
|
214
|
+
install1DAxesMulti(brush, config, pc, events);
|
|
215
|
+
|
|
216
|
+
pc.version = version;
|
|
217
|
+
// this descriptive text should live with other introspective methods
|
|
218
|
+
pc.toString = toString(config);
|
|
219
|
+
pc.toType = toType;
|
|
220
|
+
// try to coerce to number before returning type
|
|
221
|
+
pc.toTypeCoerceNumbers = toTypeCoerceNumbers;
|
|
222
|
+
|
|
223
|
+
return pc;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
export default ParCoords;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
.parcoords > svg, .parcoords > canvas {
|
|
2
|
+
font: 14px sans-serif;
|
|
3
|
+
position: absolute;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.parcoords > canvas {
|
|
7
|
+
pointer-events: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.parcoords text.label {
|
|
11
|
+
cursor: default;
|
|
12
|
+
fill: black;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.parcoords rect.background {
|
|
16
|
+
fill: transparent;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.parcoords rect.background:hover {
|
|
20
|
+
fill: rgba(120, 120, 120, 0.2);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.parcoords .resize rect {
|
|
24
|
+
fill: rgba(0, 0, 0, 0.1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.parcoords rect.extent {
|
|
28
|
+
fill: rgba(255, 255, 255, 0.25);
|
|
29
|
+
stroke: rgba(0, 0, 0, 0.6);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.parcoords .axis line, .parcoords .axis path {
|
|
33
|
+
fill: none;
|
|
34
|
+
stroke: #222;
|
|
35
|
+
shape-rendering: crispEdges;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.parcoords canvas {
|
|
39
|
+
opacity: 1;
|
|
40
|
+
-moz-transition: opacity 0.3s;
|
|
41
|
+
-webkit-transition: opacity 0.3s;
|
|
42
|
+
-o-transition: opacity 0.3s;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.parcoords canvas.faded {
|
|
46
|
+
opacity: 0.25;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.parcoords canvas.dimmed {
|
|
50
|
+
opacity: 0.85;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.parcoords {
|
|
54
|
+
-webkit-touch-callout: none;
|
|
55
|
+
-webkit-user-select: none;
|
|
56
|
+
-khtml-user-select: none;
|
|
57
|
+
-moz-user-select: none;
|
|
58
|
+
-ms-user-select: none;
|
|
59
|
+
user-select: none;
|
|
60
|
+
background-color: white;
|
|
61
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const DefaultConfig = {
|
|
2
|
+
data: [],
|
|
3
|
+
highlighted: [],
|
|
4
|
+
marked: [],
|
|
5
|
+
dimensions: {},
|
|
6
|
+
dimensionTitleRotation: 0,
|
|
7
|
+
brushes: [],
|
|
8
|
+
brushed: false,
|
|
9
|
+
brushedColor: null,
|
|
10
|
+
alphaOnBrushed: 0.0,
|
|
11
|
+
lineWidth: 1.4,
|
|
12
|
+
highlightedLineWidth: 3,
|
|
13
|
+
mode: 'default',
|
|
14
|
+
markedLineWidth: 3,
|
|
15
|
+
markedShadowColor: '#ffffff',
|
|
16
|
+
markedShadowBlur: 10,
|
|
17
|
+
rate: 20,
|
|
18
|
+
width: 600,
|
|
19
|
+
height: 300,
|
|
20
|
+
margin: { top: 24, right: 20, bottom: 12, left: 20 },
|
|
21
|
+
nullValueSeparator: 'undefined', // set to "top" or "bottom"
|
|
22
|
+
nullValueSeparatorPadding: { top: 8, right: 0, bottom: 8, left: 0 },
|
|
23
|
+
color: '#069',
|
|
24
|
+
composite: 'source-over',
|
|
25
|
+
alpha: 0.7,
|
|
26
|
+
bundlingStrength: 0.5,
|
|
27
|
+
bundleDimension: null,
|
|
28
|
+
smoothness: 0.0,
|
|
29
|
+
showControlPoints: false,
|
|
30
|
+
hideAxis: [],
|
|
31
|
+
flipAxes: [],
|
|
32
|
+
animationTime: 1100, // How long it takes to flip the axis when you double click
|
|
33
|
+
rotateLabels: false,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default DefaultConfig;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { axisLeft } from 'd3-axis';
|
|
2
|
+
import { dispatch } from 'd3-dispatch';
|
|
3
|
+
import { scalePoint } from 'd3-scale';
|
|
4
|
+
|
|
5
|
+
import DefaultConfig from './defaultConfig';
|
|
6
|
+
|
|
7
|
+
const initState = userConfig => {
|
|
8
|
+
const config = Object.assign({}, DefaultConfig, userConfig);
|
|
9
|
+
|
|
10
|
+
if (userConfig && userConfig.dimensionTitles) {
|
|
11
|
+
console.warn(
|
|
12
|
+
'dimensionTitles passed in userConfig is deprecated. Add title to dimension object.'
|
|
13
|
+
);
|
|
14
|
+
Object.entries(userConfig.dimensionTitles).forEach(([key, value]) => {
|
|
15
|
+
if (config.dimensions[key]) {
|
|
16
|
+
config.dimensions[key].title = config.dimensions[key].title
|
|
17
|
+
? config.dimensions[key].title
|
|
18
|
+
: value;
|
|
19
|
+
} else {
|
|
20
|
+
config.dimensions[key] = {
|
|
21
|
+
title: value,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const eventTypes = [
|
|
28
|
+
'render',
|
|
29
|
+
'resize',
|
|
30
|
+
'highlight',
|
|
31
|
+
'mark',
|
|
32
|
+
'brush',
|
|
33
|
+
'brushend',
|
|
34
|
+
'brushstart',
|
|
35
|
+
'axesreorder',
|
|
36
|
+
].concat(Object.keys(config));
|
|
37
|
+
|
|
38
|
+
const events = dispatch.apply(this, eventTypes),
|
|
39
|
+
flags = {
|
|
40
|
+
brushable: false,
|
|
41
|
+
reorderable: false,
|
|
42
|
+
axes: false,
|
|
43
|
+
interactive: false,
|
|
44
|
+
debug: false,
|
|
45
|
+
},
|
|
46
|
+
xscale = scalePoint(),
|
|
47
|
+
dragging = {},
|
|
48
|
+
axis = axisLeft().ticks(5),
|
|
49
|
+
ctx = {},
|
|
50
|
+
canvas = {};
|
|
51
|
+
|
|
52
|
+
const brush = {
|
|
53
|
+
modes: {
|
|
54
|
+
None: {
|
|
55
|
+
install: function(pc) {}, // Nothing to be done.
|
|
56
|
+
uninstall: function(pc) {}, // Nothing to be done.
|
|
57
|
+
selected: function() {
|
|
58
|
+
return [];
|
|
59
|
+
}, // Nothing to return
|
|
60
|
+
brushState: function() {
|
|
61
|
+
return {};
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
mode: 'None',
|
|
66
|
+
predicate: 'AND',
|
|
67
|
+
currentMode: function() {
|
|
68
|
+
return this.modes[this.mode];
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
config,
|
|
74
|
+
events,
|
|
75
|
+
eventTypes,
|
|
76
|
+
flags,
|
|
77
|
+
xscale,
|
|
78
|
+
dragging,
|
|
79
|
+
axis,
|
|
80
|
+
ctx,
|
|
81
|
+
canvas,
|
|
82
|
+
brush,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default initState;
|