Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1656c5b
squashed Sankey feature branch for 1st look
monfera Feb 22, 2017
e7892fe
PR feedback and other updates to Sankey
monfera Apr 13, 2017
65b2d57
fix up bundle files
monfera Apr 28, 2017
aa41f28
removed colorbar leftover
monfera Apr 28, 2017
dec701a
unit shown in attribute doc
monfera Apr 28, 2017
1fb7cbd
removed vestigial attribs
monfera May 1, 2017
426b50d
flipping attrib representation to columnar
monfera May 1, 2017
dab87ae
circularity detection
monfera May 1, 2017
fdf9849
Revert "removed colorbar leftover"
monfera May 1, 2017
62257b4
restore dist to what was before
monfera May 1, 2017
4ea240f
mock update
monfera May 1, 2017
4e98b12
working around image gen sensitivity
monfera May 1, 2017
747eb23
Alex's suggestion for checking self-links too
monfera May 1, 2017
1cbc2f2
attribute work:
monfera May 1, 2017
e69e3a2
attribute work:
monfera May 1, 2017
90c003a
having published our d3-sankey fork under the @plotly scope
monfera May 2, 2017
3d10714
using traceOut in defaults for downstream default generation
monfera May 2, 2017
a2a416e
jasmine tests for Sankey
monfera May 2, 2017
3e2a455
jasmine tests for Sankey - warning / error test (and defaults.js improv)
monfera May 2, 2017
b91aa25
default empty arrays in attributes.js
monfera May 2, 2017
223421e
warning message update (PR feedback, thanks Alex!)
monfera May 2, 2017
4c3521a
removed unused colorbar
monfera May 2, 2017
af1c43b
PR feedback: noOpacity; no need to add toSVG; no empty line after fun…
monfera May 9, 2017
9214c92
PR feedback: moved circularity check into calc.js
monfera May 9, 2017
aa83612
PR feedback: testing lifecycle methods and visible; file rename
monfera May 9, 2017
7427284
PR feedback: hover tooltip tests; switching to addTraces
monfera May 9, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions src/traces/sankey/calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,44 @@

'use strict';

module.exports = function calc() {
return [{}];
var tarjan = require('strongly-connected-components');
var Lib = require('../../lib');

function circularityPresent(nodeList, sources, targets) {

var nodes = nodeList.map(function() {return [];});

for(var i = 0; i < Math.min(sources.length, targets.length); i++) {
if(sources[i] === targets[i]) {
return true; // self-link which is also a scc of one
}
nodes[sources[i]].push(targets[i]);
}

var scc = tarjan(nodes);

// Tarján's strongly connected components algorithm coded by Mikola Lysenko
// returns at least one non-singular component if there's circularity in the graph
return scc.components.some(function(c) {
return c.length > 1;
});
}

module.exports = function calc(gd, trace) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A very nice first step. Thanks @monfera !


if(circularityPresent(trace.node.label, trace.link.source, trace.link.target)) {
Lib.error('Circularity is present in the Sankey data. Removing all nodes and links.');
trace.link.label = [];
trace.link.source = [];
trace.link.target = [];
trace.link.value = [];
trace.link.color = [];
trace.node.label = [];
trace.node.color = [];
}

return [{
link: trace.link,
node: trace.node
}];
};
32 changes: 0 additions & 32 deletions src/traces/sankey/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,6 @@ var attributes = require('./attributes');
var colors = require('../../components/color/attributes').defaults;
var Color = require('../../components/color');
var tinycolor = require('tinycolor2');
var tarjan = require('strongly-connected-components');

function circularityPresent(nodeList, sources, targets) {

var nodes = nodeList.map(function() {return [];});

for(var i = 0; i < Math.min(sources.length, targets.length); i++) {
if(sources[i] === targets[i]) {
return true; // self-link which is also a scc of one
}
nodes[sources[i]].push(targets[i]);
}

var scc = tarjan(nodes);

// Tarján's strongly connected components algorithm coded by Mikola Lysenko
// returns at least one non-singular component if there's circularity in the graph
return scc.components.some(function(c) {
return c.length > 1;
});
}

module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {

Expand Down Expand Up @@ -85,15 +64,4 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
if(traceOut.node.label.some(missing)) {
Lib.warn('Some of the nodes are neither sources nor targets, they will not be displayed.');
}

if(circularityPresent(traceOut.node.label, traceOut.link.source, traceOut.link.target)) {
Lib.error('Circularity is present in the Sankey data. Removing all nodes and links.');
traceOut.link.label = [];
traceOut.link.source = [];
traceOut.link.target = [];
traceOut.link.value = [];
traceOut.link.color = [];
traceOut.node.label = [];
traceOut.node.color = [];
}
};
149 changes: 87 additions & 62 deletions test/jasmine/tests/sankey_test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
var attributes = require('@src/traces/sankey/attributes');
var Lib = require('@src/lib');
var mock = require('@mocks/energy.json');
var Plots = require('@src/plots/plots');
var Sankey = require('@src/traces/sankey');
var attributes = require('@src/traces/sankey/attributes');

describe('sankey tests', function() {

Expand Down Expand Up @@ -46,48 +48,6 @@ describe('sankey tests', function() {
});
});

describe('remove nodes if encountering circularity', function() {

it('removing a single self-pointing node', function() {
var fullTrace = _supply({
node: {
label: ['a']
},
link: {
value: [1],
source: [0],
target: [0]
}
});

expect(fullTrace.node.label).toEqual([], 'node label(s) removed');
expect(fullTrace.link.value).toEqual([], 'link value(s) removed');
expect(fullTrace.link.source).toEqual([], 'link source(s) removed');
expect(fullTrace.link.target).toEqual([], 'link target(s) removed');

});

it('removing everything if detecting a circle', function() {
var fullTrace = _supply({
node: {
label: ['a', 'b', 'c', 'd', 'e']
},
link: {
value: [1, 1, 1, 1, 1, 1, 1, 1],
source: [0, 1, 2, 3],
target: [1, 2, 0, 4]
}
});

expect(fullTrace.node.label).toEqual([], 'node label(s) removed');
expect(fullTrace.link.value).toEqual([], 'link value(s) removed');
expect(fullTrace.link.source).toEqual([], 'link source(s) removed');
expect(fullTrace.link.target).toEqual([], 'link target(s) removed');

});

});

describe('log warning if issue is encountered with graph structure',
function() {

Expand All @@ -111,31 +71,20 @@ describe('sankey tests', function() {

expect(warnings.length).toEqual(1);
});
});

it('circularity is detected', function() {

var errors = [];
spyOn(Lib, 'error').and.callFake(function(msg) {
errors.push(msg);
});

_supply({
node: {
label: ['a', 'b', 'c']
},
link: {
value: [1, 1, 1],
source: [0, 1, 2],
target: [1, 2, 0]
}
});
describe('sankey global defaults', function() {

expect(errors.length).toEqual(1);
});
it('should not coerce trace opacity', function() {
var gd = Lib.extendDeep({}, mock);

Plots.supplyDefaults(gd);

expect(gd._fullData[0].opacity).toBeUndefined();
});

});

describe('sankey defaults', function() {

it('\'Sankey\' specification should have proper arrays where mandatory',
Expand Down Expand Up @@ -223,4 +172,80 @@ describe('sankey tests', function() {
});

});
Copy link
Contributor

@etpinard etpinard May 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should at the very least add some restyle tests checking that we can toggle visible: true || false correctly and that sankey subplots get clear correctly on deleteTraces.

Some hover labels tests and click/hover event data assertions would be much appreciated too.


describe('sankey calc', function() {

function _calc(trace) {
var gd = { data: [trace] };

Plots.supplyDefaults(gd);
var fullTrace = gd._fullData[0];
Sankey.calc(gd, fullTrace);
return fullTrace;
}

var base = { type: 'sankey' };

it('circularity is detected', function() {

var errors = [];
spyOn(Lib, 'error').and.callFake(function(msg) {
errors.push(msg);
});

_calc(Lib.extendDeep({}, base, {
node: {
label: ['a', 'b', 'c']
},
link: {
value: [1, 1, 1],
source: [0, 1, 2],
target: [1, 2, 0]
}
}));

expect(errors.length).toEqual(1);
});

describe('remove nodes if encountering circularity', function() {

it('removing a single self-pointing node', function() {
var fullTrace = _calc(Lib.extendDeep({}, base, {
node: {
label: ['a']
},
link: {
value: [1],
source: [0],
target: [0]
}
}));

expect(fullTrace.node.label).toEqual([], 'node label(s) removed');
expect(fullTrace.link.value).toEqual([], 'link value(s) removed');
expect(fullTrace.link.source).toEqual([], 'link source(s) removed');
expect(fullTrace.link.target).toEqual([], 'link target(s) removed');

});

it('removing everything if detecting a circle', function() {
var fullTrace = _calc(Lib.extendDeep({}, base, {
node: {
label: ['a', 'b', 'c', 'd', 'e']
},
link: {
value: [1, 1, 1, 1, 1, 1, 1, 1],
source: [0, 1, 2, 3],
target: [1, 2, 0, 4]
}
}));

expect(fullTrace.node.label).toEqual([], 'node label(s) removed');
expect(fullTrace.link.value).toEqual([], 'link value(s) removed');
expect(fullTrace.link.source).toEqual([], 'link source(s) removed');
expect(fullTrace.link.target).toEqual([], 'link target(s) removed');

});
});
});
});