New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@plotly/d3-sankey

Package Overview
Dependencies
Maintainers
23
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@plotly/d3-sankey - npm Package Compare versions

Comparing version 0.5.1 to 0.7.2

img/align-center.png

404

build/d3-sankey.js

@@ -1,107 +0,159 @@

// https://github.com/d3/d3-sankey Version 0.5.1. Copyright 2018 Mike Bostock.
// https://github.com/d3/d3-sankey Version 0.7.2. Copyright 2019 Mike Bostock.
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array'), require('d3-collection'), require('d3-interpolate')) :
typeof define === 'function' && define.amd ? define(['exports', 'd3-array', 'd3-collection', 'd3-interpolate'], factory) :
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array'), require('d3-collection'), require('d3-shape')) :
typeof define === 'function' && define.amd ? define(['exports', 'd3-array', 'd3-collection', 'd3-shape'], factory) :
(factory((global.d3 = global.d3 || {}),global.d3,global.d3,global.d3));
}(this, (function (exports,d3Array,d3Collection,d3Interpolate) { 'use strict';
}(this, (function (exports,d3Array,d3Collection,d3Shape) { 'use strict';
function targetDepth(d) {
return d.target.depth;
}
function left(node) {
return node.depth;
}
function right(node, n) {
return n - 1 - node.height;
}
function justify(node, n) {
return node.sourceLinks.length ? node.depth : n - 1;
}
function center(node) {
return node.targetLinks.length ? node.depth
: node.sourceLinks.length ? d3Array.min(node.sourceLinks, targetDepth) - 1
: 0;
}
function constant(x) {
return function() {
return x;
};
}
function ascendingSourceBreadth(a, b) {
return ascendingBreadth(a.source, b.source) || a.index - b.index;
}
function ascendingTargetBreadth(a, b) {
return ascendingBreadth(a.target, b.target) || a.index - b.index;
}
function ascendingBreadth(a, b) {
return a.y0 - b.y0;
}
function value(d) {
return d.value;
}
function nodeCenter(node) {
return (node.y0 + node.y1) / 2;
}
function weightedSource(link) {
return nodeCenter(link.source) * link.value;
}
function weightedTarget(link) {
return nodeCenter(link.target) * link.value;
}
function defaultId(d) {
return d.index;
}
function defaultNodes(graph) {
return graph.nodes;
}
function defaultLinks(graph) {
return graph.links;
}
function find(nodeById, id) {
var node = nodeById.get(id);
if (!node) throw new Error("missing: " + id);
return node;
}
var sankey = function() {
var sankey = {},
nodeWidth = 24,
nodePadding = 8,
size = [1, 1],
nodes = [],
links = [],
var x0 = 0, y0 = 0, x1 = 1, y1 = 1, // extent
dx = 24, // nodeWidth
py = 8, // nodePadding
id = defaultId,
align = justify,
nodes = defaultNodes,
links = defaultLinks,
iterations = 32,
maxPaddedSpace = 2 / 3; // Defined as a fraction of the total available space
function sankey() {
var graph = {nodes: nodes.apply(null, arguments), links: links.apply(null, arguments)};
computeNodeLinks(graph);
computeNodeValues(graph);
computeNodeDepths(graph);
computeNodeBreadths(graph, iterations);
computeLinkBreadths(graph);
return graph;
}
sankey.update = function(graph) {
computeLinkBreadths(graph);
return graph;
};
sankey.nodeId = function(_) {
return arguments.length ? (id = typeof _ === "function" ? _ : constant(_), sankey) : id;
};
sankey.nodeAlign = function(_) {
return arguments.length ? (align = typeof _ === "function" ? _ : constant(_), sankey) : align;
};
sankey.nodeWidth = function(_) {
if (!arguments.length) return nodeWidth;
nodeWidth = +_;
return sankey;
return arguments.length ? (dx = +_, sankey) : dx;
};
sankey.nodePadding = function(_) {
if (!arguments.length) return nodePadding;
nodePadding = +_;
return sankey;
return arguments.length ? (py = +_, sankey) : py;
};
sankey.nodes = function(_) {
if (!arguments.length) return nodes;
nodes = _;
return sankey;
return arguments.length ? (nodes = typeof _ === "function" ? _ : constant(_), sankey) : nodes;
};
sankey.links = function(_) {
if (!arguments.length) return links;
links = _;
return sankey;
return arguments.length ? (links = typeof _ === "function" ? _ : constant(_), sankey) : links;
};
sankey.size = function(_) {
if (!arguments.length) return size;
size = _;
return sankey;
return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], sankey) : [x1 - x0, y1 - y0];
};
sankey.layout = function(iterations) {
computeNodeLinks();
computeNodeValues();
computeNodeBreadths();
computeNodeDepths(iterations);
computeLinkDepths();
return sankey;
sankey.extent = function(_) {
return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], sankey) : [[x0, y0], [x1, y1]];
};
sankey.relayout = function() {
computeLinkDepths();
return sankey;
sankey.iterations = function(_) {
return arguments.length ? (iterations = +_, sankey) : iterations;
};
sankey.link = function() {
var curvature = .5;
function link(d) {
var x0 = d.source.x + d.source.dx,
x1 = d.target.x,
xi = d3Interpolate.interpolateNumber(x0, x1),
x2 = xi(curvature),
x3 = xi(1 - curvature),
y0a = d.source.y + d.sy,
y0b = y0a + d.dy,
y1a = d.target.y + d.ty,
y1b = y1a + d.dy;
return "M" + x0 + "," + y0a
+ "C" + x2 + "," + y0a
+ " " + x3 + "," + y1a
+ " " + x1 + "," + y1a
+ "L" + x1 + "," + y1b
+ "C" + x3 + "," + y1b
+ " " + x2 + "," + y0b
+ " " + x0 + "," + y0b
+ "Z";
}
link.curvature = function(_) {
if (!arguments.length) return curvature;
curvature = +_;
return link;
};
return link;
};
// Populate the sourceLinks and targetLinks for each node.
// Also, if the source and target are not objects, assume they are indices.
function computeNodeLinks() {
nodes.forEach(function(node) {
function computeNodeLinks(graph) {
graph.nodes.forEach(function(node, i) {
node.index = i;
node.sourceLinks = [];
node.targetLinks = [];
});
links.forEach(function(link, i) {
var source = link.source,
target = link.target;
if (typeof source === "number") source = link.source = nodes[link.source];
if (typeof target === "number") target = link.target = nodes[link.target];
link.originalIndex = i;
var nodeById = d3Collection.map(graph.nodes, id);
graph.links.forEach(function(link, i) {
link.index = i;
var source = link.source, target = link.target;
if (typeof source !== "object") source = link.source = find(nodeById, source);
if (typeof target !== "object") target = link.target = find(nodeById, target);
source.sourceLinks.push(link);

@@ -113,4 +165,4 @@ target.targetLinks.push(link);

// Compute the value (size) of each node by summing the associated links.
function computeNodeValues() {
nodes.forEach(function(node) {
function computeNodeValues(graph) {
graph.nodes.forEach(function(node) {
node.value = Math.max(

@@ -123,65 +175,49 @@ d3Array.sum(node.sourceLinks, value),

// Iteratively assign the breadth (x-position) for each node.
// Nodes are assigned the maximum breadth of incoming neighbors plus one;
// nodes with no incoming links are assigned breadth zero, while
// nodes with no outgoing links are assigned the maximum breadth.
function computeNodeBreadths() {
var remainingNodes = nodes,
nextNodes,
x = 0;
// Iteratively assign the depth (x-position) for each node.
// Nodes are assigned the maximum depth of incoming neighbors plus one;
// nodes with no incoming links are assigned depth zero, while
// nodes with no outgoing links are assigned the maximum depth.
function computeNodeDepths(graph) {
var nodes, next, x;
while (remainingNodes.length) {
nextNodes = [];
remainingNodes.forEach(function(node) {
node.x = x;
node.dx = nodeWidth;
for (nodes = graph.nodes, next = [], x = 0; nodes.length; ++x, nodes = next, next = []) {
nodes.forEach(function(node) {
node.depth = x;
node.sourceLinks.forEach(function(link) {
if (nextNodes.indexOf(link.target) < 0) {
nextNodes.push(link.target);
if (next.indexOf(link.target) < 0) {
next.push(link.target);
}
});
});
remainingNodes = nextNodes;
++x;
}
//
moveSinksRight(x);
scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
}
for (nodes = graph.nodes, next = [], x = 0; nodes.length; ++x, nodes = next, next = []) {
nodes.forEach(function(node) {
node.height = x;
node.targetLinks.forEach(function(link) {
if (next.indexOf(link.source) < 0) {
next.push(link.source);
}
});
});
}
// function moveSourcesRight() {
// nodes.forEach(function(node) {
// if (!node.targetLinks.length) {
// node.x = min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
// }
// });
// }
function moveSinksRight(x) {
nodes.forEach(function(node) {
if (!node.sourceLinks.length) {
node.x = x - 1;
}
var kx = (x1 - x0 - dx) / (x - 1);
graph.nodes.forEach(function(node) {
node.x1 = (node.x0 = x0 + Math.max(0, Math.min(x - 1, Math.floor(align.call(null, node, x)))) * kx) + dx;
});
}
function scaleNodeBreadths(kx) {
nodes.forEach(function(node) {
node.x *= kx;
});
}
function computeNodeDepths(iterations) {
var nodesByBreadth = d3Collection.nest()
.key(function(d) { return d.x; })
function computeNodeBreadths(graph) {
var columns = d3Collection.nest()
.key(function(d) { return d.x0; })
.sortKeys(d3Array.ascending)
.entries(nodes)
.entries(graph.nodes)
.map(function(d) { return d.values; });
//
initializeNodeDepth();
initializeNodeBreadth();
resolveCollisions();
for (var alpha = 1; iterations > 0; --iterations) {
relaxRightToLeft(alpha *= .99);
for (var alpha = 1, n = iterations; n > 0; --n) {
relaxRightToLeft(alpha *= 0.99);
resolveCollisions();

@@ -192,21 +228,20 @@ relaxLeftToRight(alpha);

function initializeNodeDepth() {
var L = d3Array.max(nodesByBreadth, function(nodes) {
function initializeNodeBreadth() {
var L = d3Array.max(columns, function(nodes) {
return nodes.length;
});
var maxNodePadding = maxPaddedSpace * size[1] / (L - 1);
if(nodePadding > maxNodePadding) nodePadding = maxNodePadding;
var ky = d3Array.min(nodesByBreadth, function(nodes) {
return (size[1] - (nodes.length - 1) * nodePadding) / d3Array.sum(nodes, value);
var maxNodePadding = maxPaddedSpace * (y1 - y0) / (L - 1);
if(py > maxNodePadding) py = maxNodePadding;
var ky = d3Array.min(columns, function(nodes) {
return (y1 - y0 - (nodes.length - 1) * py) / d3Array.sum(nodes, value);
});
nodesByBreadth.forEach(function(nodes) {
columns.forEach(function(nodes) {
nodes.forEach(function(node, i) {
node.y = i;
node.dy = node.value * ky;
node.y1 = (node.y0 = i) + node.value * ky;
});
});
links.forEach(function(link) {
link.dy = link.value * ky;
graph.links.forEach(function(link) {
link.width = link.value * ky;
});

@@ -216,36 +251,28 @@ }

function relaxLeftToRight(alpha) {
nodesByBreadth.forEach(function(nodes) {
columns.forEach(function(nodes) {
nodes.forEach(function(node) {
if (node.targetLinks.length) {
var y = d3Array.sum(node.targetLinks, weightedSource) / d3Array.sum(node.targetLinks, value);
node.y += (y - center(node)) * alpha;
var dy = (d3Array.sum(node.targetLinks, weightedSource) / d3Array.sum(node.targetLinks, value) - nodeCenter(node)) * alpha;
node.y0 += dy, node.y1 += dy;
}
});
});
function weightedSource(link) {
return center(link.source) * link.value;
}
}
function relaxRightToLeft(alpha) {
nodesByBreadth.slice().reverse().forEach(function(nodes) {
columns.slice().reverse().forEach(function(nodes) {
nodes.forEach(function(node) {
if (node.sourceLinks.length) {
var y = d3Array.sum(node.sourceLinks, weightedTarget) / d3Array.sum(node.sourceLinks, value);
node.y += (y - center(node)) * alpha;
var dy = (d3Array.sum(node.sourceLinks, weightedTarget) / d3Array.sum(node.sourceLinks, value) - nodeCenter(node)) * alpha;
node.y0 += dy, node.y1 += dy;
}
});
});
function weightedTarget(link) {
return center(link.target) * link.value;
}
}
function resolveCollisions() {
nodesByBreadth.forEach(function(nodes) {
columns.forEach(function(nodes) {
var node,
dy,
y0 = 0,
y = y0,
n = nodes.length,

@@ -255,14 +282,14 @@ i;

// Push any overlapping nodes down.
nodes.sort(ascendingDepth);
nodes.sort(ascendingBreadth);
for (i = 0; i < n; ++i) {
node = nodes[i];
dy = y0 - node.y;
if (dy > 0) node.y += dy;
y0 = node.y + node.dy + nodePadding;
dy = y - node.y0;
if (dy > 0) node.y0 += dy, node.y1 += dy;
y = node.y1 + py;
}
// If the bottommost node goes outside the bounds, push it back up.
dy = y0 - nodePadding - size[1];
dy = y - py - y1;
if (dy > 0) {
y0 = node.y -= dy;
y = (node.y0 -= dy), node.y1 -= dy;

@@ -272,5 +299,5 @@ // Push any overlapping nodes back up.

node = nodes[i];
dy = node.y + node.dy + nodePadding - y0;
if (dy > 0) node.y -= dy;
y0 = node.y;
dy = node.y1 + py - y;
if (dy > 0) node.y0 -= dy, node.y1 -= dy;
y = node.y0;
}

@@ -280,46 +307,43 @@ }

}
function ascendingDepth(a, b) {
return a.y - b.y;
}
}
function computeLinkDepths() {
nodes.forEach(function(node) {
node.sourceLinks.sort(ascendingTargetDepth);
node.targetLinks.sort(ascendingSourceDepth);
function computeLinkBreadths(graph) {
graph.nodes.forEach(function(node) {
node.sourceLinks.sort(ascendingTargetBreadth);
node.targetLinks.sort(ascendingSourceBreadth);
});
nodes.forEach(function(node) {
var sy = 0, ty = 0;
graph.nodes.forEach(function(node) {
var y0 = node.y0, y1 = y0;
node.sourceLinks.forEach(function(link) {
link.sy = sy;
sy += link.dy;
link.y0 = y0 + link.width / 2, y0 += link.width;
});
node.targetLinks.forEach(function(link) {
link.ty = ty;
ty += link.dy;
link.y1 = y1 + link.width / 2, y1 += link.width;
});
});
}
function ascendingSourceDepth(a, b) {
return (a.source.y - b.source.y) || (a.originalIndex - b.originalIndex);
}
return sankey;
};
function ascendingTargetDepth(a, b) {
return (a.target.y - b.target.y) || (a.originalIndex - b.originalIndex);
}
}
function horizontalSource(d) {
return [d.source.x1, d.y0];
}
function center(node) {
return node.y + node.dy / 2;
}
function horizontalTarget(d) {
return [d.target.x0, d.y1];
}
function value(link) {
return link.value;
}
return sankey;
var sankeyLinkHorizontal = function() {
return d3Shape.linkHorizontal()
.source(horizontalSource)
.target(horizontalTarget);
};
exports.sankey = sankey;
exports.sankeyCenter = center;
exports.sankeyLeft = left;
exports.sankeyRight = right;
exports.sankeyJustify = justify;
exports.sankeyLinkHorizontal = sankeyLinkHorizontal;

@@ -326,0 +350,0 @@ Object.defineProperty(exports, '__esModule', { value: true });

@@ -1,2 +0,2 @@

// https://github.com/d3/d3-sankey Version 0.5.1. Copyright 2018 Mike Bostock.
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("d3-array"),require("d3-collection"),require("d3-interpolate")):"function"==typeof define&&define.amd?define(["exports","d3-array","d3-collection","d3-interpolate"],t):t(n.d3=n.d3||{},n.d3,n.d3,n.d3)}(this,function(n,t,r,e){"use strict";var o=function(){function n(){v.forEach(function(n){n.sourceLinks=[],n.targetLinks=[]}),k.forEach(function(n,t){var r=n.source,e=n.target;"number"==typeof r&&(r=n.source=v[n.source]),"number"==typeof e&&(e=n.target=v[n.target]),n.originalIndex=t,r.sourceLinks.push(n),e.targetLinks.push(n)})}function o(){v.forEach(function(n){n.value=Math.max(t.sum(n.sourceLinks,y),t.sum(n.targetLinks,y))})}function u(){for(var n,t=v,r=0;t.length;)n=[],t.forEach(function(t){t.x=r,t.dx=l,t.sourceLinks.forEach(function(t){n.indexOf(t.target)<0&&n.push(t.target)})}),t=n,++r;i(r),c((h[0]-l)/(r-1))}function i(n){v.forEach(function(t){t.sourceLinks.length||(t.x=n-1)})}function c(n){v.forEach(function(t){t.x*=n})}function f(n){function e(){u.forEach(function(n){var t,r,e,u=0,i=n.length;for(n.sort(o),e=0;e<i;++e)t=n[e],r=u-t.y,r>0&&(t.y+=r),u=t.y+t.dy+g;if((r=u-g-h[1])>0)for(u=t.y-=r,e=i-2;e>=0;--e)t=n[e],r=t.y+t.dy+g-u,r>0&&(t.y-=r),u=t.y})}function o(n,t){return n.y-t.y}var u=r.nest().key(function(n){return n.x}).sortKeys(t.ascending).entries(v).map(function(n){return n.values});!function(){var n=t.max(u,function(n){return n.length}),r=x*h[1]/(n-1);g>r&&(g=r);var e=t.min(u,function(n){return(h[1]-(n.length-1)*g)/t.sum(n,y)});u.forEach(function(n){n.forEach(function(n,t){n.y=t,n.dy=n.value*e})}),k.forEach(function(n){n.dy=n.value*e})}(),e();for(var i=1;n>0;--n)!function(n){function r(n){return s(n.target)*n.value}u.slice().reverse().forEach(function(e){e.forEach(function(e){if(e.sourceLinks.length){var o=t.sum(e.sourceLinks,r)/t.sum(e.sourceLinks,y);e.y+=(o-s(e))*n}})})}(i*=.99),e(),function(n){function r(n){return s(n.source)*n.value}u.forEach(function(e){e.forEach(function(e){if(e.targetLinks.length){var o=t.sum(e.targetLinks,r)/t.sum(e.targetLinks,y);e.y+=(o-s(e))*n}})})}(i),e()}function a(){function n(n,t){return n.source.y-t.source.y||n.originalIndex-t.originalIndex}function t(n,t){return n.target.y-t.target.y||n.originalIndex-t.originalIndex}v.forEach(function(r){r.sourceLinks.sort(t),r.targetLinks.sort(n)}),v.forEach(function(n){var t=0,r=0;n.sourceLinks.forEach(function(n){n.sy=t,t+=n.dy}),n.targetLinks.forEach(function(n){n.ty=r,r+=n.dy})})}function s(n){return n.y+n.dy/2}function y(n){return n.value}var d={},l=24,g=8,h=[1,1],v=[],k=[],x=2/3;return d.nodeWidth=function(n){return arguments.length?(l=+n,d):l},d.nodePadding=function(n){return arguments.length?(g=+n,d):g},d.nodes=function(n){return arguments.length?(v=n,d):v},d.links=function(n){return arguments.length?(k=n,d):k},d.size=function(n){return arguments.length?(h=n,d):h},d.layout=function(t){return n(),o(),u(),f(t),a(),d},d.relayout=function(){return a(),d},d.link=function(){function n(n){var r=n.source.x+n.source.dx,o=n.target.x,u=e.interpolateNumber(r,o),i=u(t),c=u(1-t),f=n.source.y+n.sy,a=f+n.dy,s=n.target.y+n.ty,y=s+n.dy;return"M"+r+","+f+"C"+i+","+f+" "+c+","+s+" "+o+","+s+"L"+o+","+y+"C"+c+","+y+" "+i+","+a+" "+r+","+a+"Z"}var t=.5;return n.curvature=function(r){return arguments.length?(t=+r,n):t},n},d};n.sankey=o,Object.defineProperty(n,"__esModule",{value:!0})});
// https://github.com/d3/d3-sankey Version 0.7.2. Copyright 2019 Mike Bostock.
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("d3-array"),require("d3-collection"),require("d3-shape")):"function"==typeof define&&define.amd?define(["exports","d3-array","d3-collection","d3-shape"],t):t(n.d3=n.d3||{},n.d3,n.d3,n.d3)}(this,function(n,g,k,t){"use strict";function e(n){return n.target.depth}function p(n,t){return n.sourceLinks.length?n.depth:t-1}function v(n){return function(){return n}}function L(n,t){return E(n.source,t.source)||n.index-t.index}function x(n,t){return E(n.target,t.target)||n.index-t.index}function E(n,t){return n.y0-t.y0}function m(n){return n.value}function w(n){return(n.y0+n.y1)/2}function M(n){return w(n.source)*n.value}function b(n){return w(n.target)*n.value}function j(n){return n.index}function q(n){return n.nodes}function z(n){return n.links}function O(n,t){var e=n.get(t);if(!e)throw new Error("missing: "+t);return e}function r(n){return[n.source.x1,n.y0]}function o(n){return[n.target.x0,n.y1]}n.sankey=function(){var u=0,c=0,i=1,f=1,s=24,a=8,t=j,h=p,e=q,r=z,d=32,l=2/3;function o(){var n={nodes:e.apply(null,arguments),links:r.apply(null,arguments)};return function(n){n.nodes.forEach(function(n,t){n.index=t,n.sourceLinks=[],n.targetLinks=[]});var o=k.map(n.nodes,t);n.links.forEach(function(n,t){n.index=t;var e=n.source,r=n.target;"object"!=typeof e&&(e=n.source=O(o,e)),"object"!=typeof r&&(r=n.target=O(o,r)),e.sourceLinks.push(n),r.targetLinks.push(n)})}(n),n.nodes.forEach(function(n){n.value=Math.max(g.sum(n.sourceLinks,m),g.sum(n.targetLinks,m))}),function(n){var t,e,r;for(t=n.nodes,e=[],r=0;t.length;++r,t=e,e=[])t.forEach(function(n){n.depth=r,n.sourceLinks.forEach(function(n){e.indexOf(n.target)<0&&e.push(n.target)})});for(t=n.nodes,e=[],r=0;t.length;++r,t=e,e=[])t.forEach(function(n){n.height=r,n.targetLinks.forEach(function(n){e.indexOf(n.source)<0&&e.push(n.source)})});var o=(i-u-s)/(r-1);n.nodes.forEach(function(n){n.x1=(n.x0=u+Math.max(0,Math.min(r-1,Math.floor(h.call(null,n,r))))*o)+s})}(n),function(r){var o=k.nest().key(function(n){return n.x0}).sortKeys(g.ascending).entries(r.nodes).map(function(n){return n.values});(function(){var n=g.max(o,function(n){return n.length}),t=l*(f-c)/(n-1);t<a&&(a=t);var e=g.min(o,function(n){return(f-c-(n.length-1)*a)/g.sum(n,m)});o.forEach(function(n){n.forEach(function(n,t){n.y1=(n.y0=t)+n.value*e})}),r.links.forEach(function(n){n.width=n.value*e})})(),i();for(var n=1,t=d;0<t;--t)u(n*=.99),i(),e(n),i();function e(e){o.forEach(function(n){n.forEach(function(n){if(n.targetLinks.length){var t=(g.sum(n.targetLinks,M)/g.sum(n.targetLinks,m)-w(n))*e;n.y0+=t,n.y1+=t}})})}function u(e){o.slice().reverse().forEach(function(n){n.forEach(function(n){if(n.sourceLinks.length){var t=(g.sum(n.sourceLinks,b)/g.sum(n.sourceLinks,m)-w(n))*e;n.y0+=t,n.y1+=t}})})}function i(){o.forEach(function(n){var t,e,r,o=c,u=n.length;for(n.sort(E),r=0;r<u;++r)t=n[r],0<(e=o-t.y0)&&(t.y0+=e,t.y1+=e),o=t.y1+a;if(0<(e=o-a-f))for(o=t.y0-=e,t.y1-=e,r=u-2;0<=r;--r)t=n[r],0<(e=t.y1+a-o)&&(t.y0-=e,t.y1-=e),o=t.y0})}}(n),y(n),n}function y(n){n.nodes.forEach(function(n){n.sourceLinks.sort(x),n.targetLinks.sort(L)}),n.nodes.forEach(function(n){var t=n.y0,e=t;n.sourceLinks.forEach(function(n){n.y0=t+n.width/2,t+=n.width}),n.targetLinks.forEach(function(n){n.y1=e+n.width/2,e+=n.width})})}return o.update=function(n){return y(n),n},o.nodeId=function(n){return arguments.length?(t="function"==typeof n?n:v(n),o):t},o.nodeAlign=function(n){return arguments.length?(h="function"==typeof n?n:v(n),o):h},o.nodeWidth=function(n){return arguments.length?(s=+n,o):s},o.nodePadding=function(n){return arguments.length?(a=+n,o):a},o.nodes=function(n){return arguments.length?(e="function"==typeof n?n:v(n),o):e},o.links=function(n){return arguments.length?(r="function"==typeof n?n:v(n),o):r},o.size=function(n){return arguments.length?(u=c=0,i=+n[0],f=+n[1],o):[i-u,f-c]},o.extent=function(n){return arguments.length?(u=+n[0][0],i=+n[1][0],c=+n[0][1],f=+n[1][1],o):[[u,c],[i,f]]},o.iterations=function(n){return arguments.length?(d=+n,o):d},o},n.sankeyCenter=function(n){return n.targetLinks.length?n.depth:n.sourceLinks.length?g.min(n.sourceLinks,e)-1:0},n.sankeyLeft=function(n){return n.depth},n.sankeyRight=function(n,t){return t-1-n.height},n.sankeyJustify=p,n.sankeyLinkHorizontal=function(){return t.linkHorizontal().source(r).target(o)},Object.defineProperty(n,"__esModule",{value:!0})});
export {default as sankey} from "./src/sankey";
export {center as sankeyCenter, left as sankeyLeft, right as sankeyRight, justify as sankeyJustify} from "./src/align";
export {default as sankeyLinkHorizontal} from "./src/sankeyLinkHorizontal";
{
"name": "@plotly/d3-sankey",
"version": "0.5.1",
"version": "0.7.2",
"description": "Visualize flow between nodes in a directed acyclic network.",

@@ -12,8 +12,8 @@ "keywords": [

"name": "Mike Bostock",
"url": "http://bost.ocks.org/mike"
"url": "https://bost.ocks.org/mike/"
},
"license": "BSD-3-Clause",
"main": "build/d3-sankey.js",
"module": "index",
"jsnext:main": "index",
"module": "index",
"homepage": "https://github.com/d3/d3-sankey",

@@ -25,5 +25,5 @@ "repository": {

"scripts": {
"pretest": "rm -rf build && mkdir build && rollup --banner \"$(preamble)\" -g d3-array:d3,d3-collection:d3,d3-interpolate:d3 -f umd -n d3 -o build/d3-sankey.js -- index.js",
"pretest": "rm -rf build && mkdir build && rollup --banner \"$(preamble)\" -g d3-array:d3,d3-collection:d3,d3-shape:d3 -f umd -n d3 -o build/d3-sankey.js -- index.js",
"test": "tape 'test/**/*-test.js' && eslint index.js src",
"prepublish": "npm run test && uglifyjs --preamble \"$(preamble)\" build/d3-sankey.js -c -m -o build/d3-sankey.min.js",
"prepublish": "npm run test && uglifyjs -b beautify=false,preamble=\"'$(preamble)'\" build/d3-sankey.js -c -m -o build/d3-sankey.min.js",
"postpublish": "git push && git push --tags && zip -j build/d3-sankey.zip -- LICENSE README.md build/d3-sankey.js build/d3-sankey.min.js"

@@ -34,11 +34,11 @@ },

"d3-collection": "1",
"d3-interpolate": "1"
"d3-shape": "^1.2.0"
},
"devDependencies": {
"eslint": "3",
"package-preamble": "0.0.2",
"rollup": "0.41",
"eslint": "4",
"package-preamble": "0.1.0",
"rollup": "0.43",
"tape": "4",
"uglify-js": "2"
"uglify-js": "3"
}
}
# d3-sankey
D3 4.0 implementation of the Sankey plugin to visualize the flow between nodes in a directed acyclic network.
Sankey diagrams visualize the directed flow between nodes in an acyclic network. For example, this diagram shows a possible scenario of UK energy production and consumption in 2050:
[<img alt="Sankey diagram" src="https://raw.githubusercontent.com/d3/d3-sankey/master/img/energy.png" width="960">](https://bl.ocks.org/mbostock/ca9a0bb7ba204d12974bca90acc507c0)
Source: Department of Energy & Climate Change, Tom Counsell.
## Installing
If you use NPM, `npm install d3-sankey`. Otherwise, download the [latest release](https://github.com/d3/d3-sankey/releases/latest).
If you use NPM, `npm install d3-sankey`. Otherwise, download the [latest release](https://github.com/d3/d3-sankey/releases/latest). You can also load directly from [unpkg.com](https://unpkg.com/d3-sankey/). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3` global is exported:
You can also load directly from unpkg.
```javascript
<script src="https://d3js.org/d3.v4.js"></script>
```html
<script src="https://unpkg.com/d3-array@1"></script>
<script src="https://unpkg.com/d3-collection@1"></script>
<script src="https://unpkg.com/d3-path@1"></script>
<script src="https://unpkg.com/d3-shape@1"></script>
<script src="https://unpkg.com/d3-sankey@0"></script>

@@ -21,60 +26,214 @@ <script>

## Demo
Here is Mike Bostock's famous example [recreated with d3-sankey](http://bl.ocks.org/xaranke/9ada4c74a87b57ae7308).
## API Reference
Clone or download the block, then run `npm install` and `npm run build` to create `d3.min.js`.
<a href="#sankey" name="sankey">#</a> d3.<b>sankey</b>() [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L52 "Source")
## API Reference
Constructs a new Sankey generator with the default settings.
<a href="#sankey" name="sankey">#</a> <b>sankey</b>()
<a href="#_sankey" name="_sankey">#</a> <i>sankey</i>(<i>arguments</i>…) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L62 "Source")
Constructs a new sankey generator with the default settings.
Computes the node and link positions for the given *arguments*, returning a *graph* representing the Sankey layout. The returned *graph* has the following properties:
<a name="sankey_nodeWidth" href="#sankey_nodeWidth">#</a> <i>sankey</i>.<b>nodeWidth</b>([<i>width</i>])
* *graph*.nodes - the array of [nodes](#sankey_nodes)
* *graph*.links - the array of [links](#sankey_links)
If <i>width</i> is specified, sets the node width to the specified function or number and returns this sankey generator. If <i>width</i> is not specified, returns the current node width accessor, which defaults to:
<a href="#sankey_update" name="sankey_update">#</a> <i>sankey</i>.<b>update</b>(<i>graph</i>) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L72 "Source")
Recomputes the specified *graph*’s links’ positions, updating the following properties of each *link*:
* *link*.y0 - the link’s vertical starting position (at source node)
* *link*.y1 - the link’s vertical end position (at target node)
This method is intended to be called after computing the initial [Sankey layout](#_sankey), for example when the diagram is repositioned interactively.
<a name="sankey_nodes" href="#sankey_nodes">#</a> <i>sankey</i>.<b>nodes</b>([<i>nodes</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L93 "Source")
If *nodes* is specified, sets the Sankey generator’s nodes accessor to the specified function or array and returns this Sankey generator. If *nodes* is not specified, returns the current nodes accessor, which defaults to:
```js
function nodeWidth() {
return 24;
function nodes(graph) {
return graph.nodes;
}
```
<a name="sankey_nodePadding" href="#sankey_nodePadding">#</a> <i>sankey</i>.<b>nodePadding</b>([<i>padding</i>])
If *nodes* is specified as a function, the function is invoked when the Sankey layout is [generated](#_sankey), being passed any arguments passed to the Sankey generator. This function must return an array of nodes. If *nodes* is not a function, it must be a constant array of *nodes*.
If <i>padding</i> is specified, sets the node padding to the specified function or number and returns this sankey generator. If <i>padding</i> is not specified, returns the current node padding accessor, which defaults to:
Each *node* must be an object. The following properties are assigned by the [Sankey generator](#_sankey):
* *node*.sourceLinks - the array of outgoing [links](#sankey_links) which have this node as their source
* *node*.targetLinks - the array of incoming [links](#sankey_links) which have this node as their target
* *node*.value - the node’s value; the sum of *link*.value for the node’s incoming [links](#sankey_links)
* *node*.index - the node’s zero-based index within the array of nodes
* *node*.depth - the node’s zero-based graph depth, derived from the graph topology
* *node*.height - the node’s zero-based graph height, derived from the graph topology
* *node*.x0 - the node’s minimum horizontal position, derived from *node*.depth
* *node*.x1 - the node’s maximum horizontal position (*node*.x0 + [*sankey*.nodeWidth](#sankey_nodeWidth))
* *node*.y0 - the node’s minimum vertical position
* *node*.y1 - the node’s maximum vertical position (*node*.y1 - *node*.y0 is proportional to *node*.value)
See also [*sankey*.links](#sankey_links).
<a name="sankey_links" href="#sankey_links">#</a> <i>sankey</i>.<b>links</b>([<i>links</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L97 "Source")
If *links* is specified, sets the Sankey generator’s links accessor to the specified function or array and returns this Sankey generator. If *links* is not specified, returns the current links accessor, which defaults to:
```js
function nodePadding() {
return 8;
function links(graph) {
return graph.links;
}
```
Here padding refers to the vertical space between nodes that occupy the same horizontal space.
<a name="sankey_nodes" href="#sankey_nodes">#</a> <i>sankey</i>.<b>nodes</b>([<i>nodes</i>])
If *links* is specified as a function, the function is invoked when the Sankey layout is [generated](#_sankey), being passed any arguments passed to the Sankey generator. This function must return an array of links. If *links* is not a function, it must be a constant array of *links*.
If <i>nodes</i> is specified, sets the list of nodes to the specified function or array and returns this sankey generator. If <i>nodes</i> is not specified, returns the current accessor to the list of nodes, which defaults to:
Each *link* must be an object with the following properties:
* *link*.source - the link’s source [node](#sankey_nodes)
* *link*.target - the link’s target [node](#sankey_nodes)
* *link*.value - the link’s numeric value
For convenience, a link’s source and target may be initialized using numeric or string identifiers rather than object references; see [*sankey*.nodeId](#sankey_nodeId). The following properties are assigned to each link by the [Sankey generator](#_sankey):
* *link*.y0 - the link’s vertical starting position (at source node)
* *link*.y1 - the link’s vertical end position (at target node)
* *link*.width - the link’s width (proportional to *link*.value)
* *link*.index - the zero-based index of *link* within the array of links
<a name="sankey_nodeId" href="#sankey_nodeId">#</a> <i>sankey</i>.<b>nodeId</b>([<i>id</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L77 "Source")
If *id* is specified, sets the node id accessor to the specified function and returns this Sankey generator. If *id* is not specified, returns the current node id accessor, which defaults to the numeric *node*.index:
```js
function nodes() {
return [];
function id(d) {
return d.index;
}
```
<a name="sankey_links" href="#sankey_links">#</a> <i>sankey</i>.<b>links</b>([<i>links</i>])
The default id accessor allows each link’s source and target to be specified as a zero-based index into the [nodes](#sankey_nodes) array. For example:
If <i>links</i> is specified, sets the list of links to the specified function or array and returns this sankey generator. If <i>links</i> is not specified, returns the current accessor to the list of links, which defaults to:
```js
var nodes = [
{"id": "Alice"},
{"id": "Bob"},
{"id": "Carol"}
];
var links = [
{"source": 0, "target": 1}, // Alice → Bob
{"source": 1, "target": 2} // Bob → Carol
];
```
Now consider a different id accessor that returns a string:
```js
function links() {
return [];
function id(d) {
return d.id;
}
```
<a name="sankey_layout" href="#sankey_layout">#</a> <i>sankey</i>.<b>layout</b>([<i>iterations</i>])
With this accessor, you can use named sources and targets:
Returns the current accessor to the SVG layout object. Here <i>iterations</i> is the number of times the converging function <b>computeNodeDepths</b> is run.
```js
var nodes = [
{"id": "Alice"},
{"id": "Bob"},
{"id": "Carol"}
];
<a name="sankey_relayout" href="#sankey_relayout">#</a> <i>sankey</i>.<b>relayout</b>()
var links = [
{"source": "Alice", "target": "Bob"},
{"source": "Bob", "target": "Carol"}
];
```
Similar to <b>layout</b> but only recalculates the depth of links. Primarily used when a node is moved vertically.
This is particularly useful when representing graphs in JSON, as JSON does not allow references. See [this example](https://bl.ocks.org/mbostock/f584aa36df54c451c94a9d0798caed35).
<a name="sankey_nodeAlign" href="#sankey_nodeAlign">#</a> <i>sankey</i>.<b>nodeAlign</b>([<i>align</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L81 "Source")
If *align* is specified, sets the node [alignment method](#alignments) the specified function and returns this Sankey generator. If *align* is not specified, returns the current node alignment method, which defaults to [d3.sankeyJustify](#sankeyJustify). The specified function is evaluated for each input *node* in order, being passed the current *node* and the total depth *n* of the graph (one plus the maximum *node*.depth), and must return an integer between 0 and *n* - 1 that indicates the desired horizontal position of the node in the generated Sankey diagram.
<a name="sankey_nodeWidth" href="#sankey_nodeWidth">#</a> <i>sankey</i>.<b>nodeWidth</b>([<i>width</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L85 "Source")
If *width* is specified, sets the node width to the specified number and returns this Sankey generator. If *width* is not specified, returns the current node width, which defaults to 24.
<a name="sankey_nodePadding" href="#sankey_nodePadding">#</a> <i>sankey</i>.<b>nodePadding</b>([<i>padding</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L89 "Source")
If *padding* is specified, sets the vertical separation between nodes at each column to the specified number and returns this Sankey generator. If *padding* is not specified, returns the current node padding, which defaults to 8.
<a name="sankey_extent" href="#sankey_extent">#</a> <i>sankey</i>.<b>extent</b>([<i>extent</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L105 "Source")
If *extent* is specified, sets the extent of the Sankey layout to the specified bounds and returns the layout. The *extent* bounds are specified as an array \[\[<i>x0</i>, <i>y0</i>\], \[<i>x1</i>, <i>y1</i>\]\], where *x0* is the left side of the extent, *y0* is the top, *x1* is the right and *y1* is the bottom. If *extent* is not specified, returns the current extent which defaults to [[0, 0], [1, 1]].
<a name="sankey_size" href="#sankey_size">#</a> <i>sankey</i>.<b>size</b>([<i>size</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L101 "Source")
An alias for [*sankey*.extent](#sankey_extent) where the minimum *x* and *y* of the extent are ⟨0,0⟩. Equivalent to:
```js
sankey.extent([[0, 0], size]);
```
<a name="sankey_iterations" href="#sankey_iterations">#</a> <i>sankey</i>.<b>iterations</b>([<i>iterations</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L109 "Source")
If *iterations* is specified, sets the number of relaxation iterations when [generating the layout](#_sankey) and returns this Sankey generator. If *iterations* is not specified, returns the current number of relaxation iterations, which defaults to 32.
### Alignments
See [*sankey*.nodeAlign](#sankey_nodeAlign).
<a name="sankeyLeft" href="#sankeyLeft">#</a> d3.<b>sankeyLeft</b>(<i>node</i>, <i>n</i>) [<>](https://github.com/d3/d3-sankey/blob/master/src/align.js#L7 "Source")
<img alt="left" src="https://raw.githubusercontent.com/d3/d3-sankey/master/img/align-left.png" width="480">
Returns *node*.depth.
<a name="sankeyRight" href="#sankeyRight">#</a> d3.<b>sankeyRight</b>(<i>node</i>, <i>n</i>) [<>](https://github.com/d3/d3-sankey/blob/master/src/align.js#L11 "Source")
<img alt="right" src="https://raw.githubusercontent.com/d3/d3-sankey/master/img/align-right.png" width="480">
Returns *n* - 1 - *node*.height.
<a name="sankeyCenter" href="#sankeyCenter">#</a> d3.<b>sankeyCenter</b>(<i>node</i>, <i>n</i>) [<>](https://github.com/d3/d3-sankey/blob/master/src/align.js#L19 "Source")
<img alt="center" src="https://raw.githubusercontent.com/d3/d3-sankey/master/img/align-center.png" width="480">
Like [d3.sankeyLeft](#sankeyLeft), except that nodes without any incoming links are moved as right as possible.
<a name="sankeyJustify" href="#sankeyJustify">#</a> d3.<b>sankeyJustify</b>(<i>node</i>, <i>n</i>) [<>](https://github.com/d3/d3-sankey/blob/master/src/align.js#L15 "Source")
<img alt="justify" src="https://raw.githubusercontent.com/d3/d3-sankey/master/img/energy.png" width="480">
Like [d3.sankeyLeft](#sankeyLeft), except that nodes without any outgoing links are moved to the far right.
### Links
<a name="sankeyLinkHorizontal" href="#sankeyLinkHorizontal">#</a> d3.<b>sankeyLinkHorizontal</b>() [<>](https://github.com/d3/d3-sankey/blob/master/src/sankeyLinkHorizontal.js "Source")
Returns a [horizontal link shape](https://github.com/d3/d3-shape/blob/master/README.md#linkHorizontal) suitable for a Sankey diagram. The [source accessor](https://github.com/d3/d3-shape/blob/master/README.md#link_source) is defined as:
```js
function source(d) {
return [d.source.x1, d.y0];
}
```
The [target accessor](https://github.com/d3/d3-shape/blob/master/README.md#link_target) is defined as:
```js
function target(d) {
return [d.target.x0, d.y1];
}
```
For example, to render the links of a Sankey diagram in SVG, you might say:
```js
svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.selectAll("path")
.data(graph.links)
.enter().append("path")
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke-width", function(d) { return d.width; });
```

@@ -1,104 +0,129 @@

import {ascending, min, sum, max} from "d3-array";
import {nest} from "d3-collection";
import {interpolateNumber} from "d3-interpolate";
import {ascending, min, max, sum} from "d3-array";
import {map, nest} from "d3-collection";
import {justify} from "./align";
import constant from "./constant";
function ascendingSourceBreadth(a, b) {
return ascendingBreadth(a.source, b.source) || a.index - b.index;
}
function ascendingTargetBreadth(a, b) {
return ascendingBreadth(a.target, b.target) || a.index - b.index;
}
function ascendingBreadth(a, b) {
return a.y0 - b.y0;
}
function value(d) {
return d.value;
}
function nodeCenter(node) {
return (node.y0 + node.y1) / 2;
}
function weightedSource(link) {
return nodeCenter(link.source) * link.value;
}
function weightedTarget(link) {
return nodeCenter(link.target) * link.value;
}
function defaultId(d) {
return d.index;
}
function defaultNodes(graph) {
return graph.nodes;
}
function defaultLinks(graph) {
return graph.links;
}
function find(nodeById, id) {
var node = nodeById.get(id);
if (!node) throw new Error("missing: " + id);
return node;
}
export default function() {
var sankey = {},
nodeWidth = 24,
nodePadding = 8,
size = [1, 1],
nodes = [],
links = [],
var x0 = 0, y0 = 0, x1 = 1, y1 = 1, // extent
dx = 24, // nodeWidth
py = 8, // nodePadding
id = defaultId,
align = justify,
nodes = defaultNodes,
links = defaultLinks,
iterations = 32,
maxPaddedSpace = 2 / 3; // Defined as a fraction of the total available space
function sankey() {
var graph = {nodes: nodes.apply(null, arguments), links: links.apply(null, arguments)};
computeNodeLinks(graph);
computeNodeValues(graph);
computeNodeDepths(graph);
computeNodeBreadths(graph, iterations);
computeLinkBreadths(graph);
return graph;
}
sankey.update = function(graph) {
computeLinkBreadths(graph);
return graph;
};
sankey.nodeId = function(_) {
return arguments.length ? (id = typeof _ === "function" ? _ : constant(_), sankey) : id;
};
sankey.nodeAlign = function(_) {
return arguments.length ? (align = typeof _ === "function" ? _ : constant(_), sankey) : align;
};
sankey.nodeWidth = function(_) {
if (!arguments.length) return nodeWidth;
nodeWidth = +_;
return sankey;
return arguments.length ? (dx = +_, sankey) : dx;
};
sankey.nodePadding = function(_) {
if (!arguments.length) return nodePadding;
nodePadding = +_;
return sankey;
return arguments.length ? (py = +_, sankey) : py;
};
sankey.nodes = function(_) {
if (!arguments.length) return nodes;
nodes = _;
return sankey;
return arguments.length ? (nodes = typeof _ === "function" ? _ : constant(_), sankey) : nodes;
};
sankey.links = function(_) {
if (!arguments.length) return links;
links = _;
return sankey;
return arguments.length ? (links = typeof _ === "function" ? _ : constant(_), sankey) : links;
};
sankey.size = function(_) {
if (!arguments.length) return size;
size = _;
return sankey;
return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], sankey) : [x1 - x0, y1 - y0];
};
sankey.layout = function(iterations) {
computeNodeLinks();
computeNodeValues();
computeNodeBreadths();
computeNodeDepths(iterations);
computeLinkDepths();
return sankey;
sankey.extent = function(_) {
return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], sankey) : [[x0, y0], [x1, y1]];
};
sankey.relayout = function() {
computeLinkDepths();
return sankey;
sankey.iterations = function(_) {
return arguments.length ? (iterations = +_, sankey) : iterations;
};
sankey.link = function() {
var curvature = .5;
function link(d) {
var x0 = d.source.x + d.source.dx,
x1 = d.target.x,
xi = interpolateNumber(x0, x1),
x2 = xi(curvature),
x3 = xi(1 - curvature),
y0a = d.source.y + d.sy,
y0b = y0a + d.dy,
y1a = d.target.y + d.ty,
y1b = y1a + d.dy;
return "M" + x0 + "," + y0a
+ "C" + x2 + "," + y0a
+ " " + x3 + "," + y1a
+ " " + x1 + "," + y1a
+ "L" + x1 + "," + y1b
+ "C" + x3 + "," + y1b
+ " " + x2 + "," + y0b
+ " " + x0 + "," + y0b
+ "Z";
}
link.curvature = function(_) {
if (!arguments.length) return curvature;
curvature = +_;
return link;
};
return link;
};
// Populate the sourceLinks and targetLinks for each node.
// Also, if the source and target are not objects, assume they are indices.
function computeNodeLinks() {
nodes.forEach(function(node) {
function computeNodeLinks(graph) {
graph.nodes.forEach(function(node, i) {
node.index = i;
node.sourceLinks = [];
node.targetLinks = [];
});
links.forEach(function(link, i) {
var source = link.source,
target = link.target;
if (typeof source === "number") source = link.source = nodes[link.source];
if (typeof target === "number") target = link.target = nodes[link.target];
link.originalIndex = i;
var nodeById = map(graph.nodes, id);
graph.links.forEach(function(link, i) {
link.index = i;
var source = link.source, target = link.target;
if (typeof source !== "object") source = link.source = find(nodeById, source);
if (typeof target !== "object") target = link.target = find(nodeById, target);
source.sourceLinks.push(link);

@@ -110,4 +135,4 @@ target.targetLinks.push(link);

// Compute the value (size) of each node by summing the associated links.
function computeNodeValues() {
nodes.forEach(function(node) {
function computeNodeValues(graph) {
graph.nodes.forEach(function(node) {
node.value = Math.max(

@@ -120,65 +145,49 @@ sum(node.sourceLinks, value),

// Iteratively assign the breadth (x-position) for each node.
// Nodes are assigned the maximum breadth of incoming neighbors plus one;
// nodes with no incoming links are assigned breadth zero, while
// nodes with no outgoing links are assigned the maximum breadth.
function computeNodeBreadths() {
var remainingNodes = nodes,
nextNodes,
x = 0;
// Iteratively assign the depth (x-position) for each node.
// Nodes are assigned the maximum depth of incoming neighbors plus one;
// nodes with no incoming links are assigned depth zero, while
// nodes with no outgoing links are assigned the maximum depth.
function computeNodeDepths(graph) {
var nodes, next, x;
while (remainingNodes.length) {
nextNodes = [];
remainingNodes.forEach(function(node) {
node.x = x;
node.dx = nodeWidth;
for (nodes = graph.nodes, next = [], x = 0; nodes.length; ++x, nodes = next, next = []) {
nodes.forEach(function(node) {
node.depth = x;
node.sourceLinks.forEach(function(link) {
if (nextNodes.indexOf(link.target) < 0) {
nextNodes.push(link.target);
if (next.indexOf(link.target) < 0) {
next.push(link.target);
}
});
});
remainingNodes = nextNodes;
++x;
}
//
moveSinksRight(x);
scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
}
for (nodes = graph.nodes, next = [], x = 0; nodes.length; ++x, nodes = next, next = []) {
nodes.forEach(function(node) {
node.height = x;
node.targetLinks.forEach(function(link) {
if (next.indexOf(link.source) < 0) {
next.push(link.source);
}
});
});
}
// function moveSourcesRight() {
// nodes.forEach(function(node) {
// if (!node.targetLinks.length) {
// node.x = min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
// }
// });
// }
function moveSinksRight(x) {
nodes.forEach(function(node) {
if (!node.sourceLinks.length) {
node.x = x - 1;
}
var kx = (x1 - x0 - dx) / (x - 1);
graph.nodes.forEach(function(node) {
node.x1 = (node.x0 = x0 + Math.max(0, Math.min(x - 1, Math.floor(align.call(null, node, x)))) * kx) + dx;
});
}
function scaleNodeBreadths(kx) {
nodes.forEach(function(node) {
node.x *= kx;
});
}
function computeNodeDepths(iterations) {
var nodesByBreadth = nest()
.key(function(d) { return d.x; })
function computeNodeBreadths(graph) {
var columns = nest()
.key(function(d) { return d.x0; })
.sortKeys(ascending)
.entries(nodes)
.entries(graph.nodes)
.map(function(d) { return d.values; });
//
initializeNodeDepth();
initializeNodeBreadth();
resolveCollisions();
for (var alpha = 1; iterations > 0; --iterations) {
relaxRightToLeft(alpha *= .99);
for (var alpha = 1, n = iterations; n > 0; --n) {
relaxRightToLeft(alpha *= 0.99);
resolveCollisions();

@@ -189,21 +198,20 @@ relaxLeftToRight(alpha);

function initializeNodeDepth() {
var L = max(nodesByBreadth, function(nodes) {
function initializeNodeBreadth() {
var L = max(columns, function(nodes) {
return nodes.length;
});
var maxNodePadding = maxPaddedSpace * size[1] / (L - 1);
if(nodePadding > maxNodePadding) nodePadding = maxNodePadding;
var ky = min(nodesByBreadth, function(nodes) {
return (size[1] - (nodes.length - 1) * nodePadding) / sum(nodes, value);
var maxNodePadding = maxPaddedSpace * (y1 - y0) / (L - 1);
if(py > maxNodePadding) py = maxNodePadding;
var ky = min(columns, function(nodes) {
return (y1 - y0 - (nodes.length - 1) * py) / sum(nodes, value);
});
nodesByBreadth.forEach(function(nodes) {
columns.forEach(function(nodes) {
nodes.forEach(function(node, i) {
node.y = i;
node.dy = node.value * ky;
node.y1 = (node.y0 = i) + node.value * ky;
});
});
links.forEach(function(link) {
link.dy = link.value * ky;
graph.links.forEach(function(link) {
link.width = link.value * ky;
});

@@ -213,36 +221,28 @@ }

function relaxLeftToRight(alpha) {
nodesByBreadth.forEach(function(nodes) {
columns.forEach(function(nodes) {
nodes.forEach(function(node) {
if (node.targetLinks.length) {
var y = sum(node.targetLinks, weightedSource) / sum(node.targetLinks, value);
node.y += (y - center(node)) * alpha;
var dy = (sum(node.targetLinks, weightedSource) / sum(node.targetLinks, value) - nodeCenter(node)) * alpha;
node.y0 += dy, node.y1 += dy;
}
});
});
function weightedSource(link) {
return center(link.source) * link.value;
}
}
function relaxRightToLeft(alpha) {
nodesByBreadth.slice().reverse().forEach(function(nodes) {
columns.slice().reverse().forEach(function(nodes) {
nodes.forEach(function(node) {
if (node.sourceLinks.length) {
var y = sum(node.sourceLinks, weightedTarget) / sum(node.sourceLinks, value);
node.y += (y - center(node)) * alpha;
var dy = (sum(node.sourceLinks, weightedTarget) / sum(node.sourceLinks, value) - nodeCenter(node)) * alpha;
node.y0 += dy, node.y1 += dy;
}
});
});
function weightedTarget(link) {
return center(link.target) * link.value;
}
}
function resolveCollisions() {
nodesByBreadth.forEach(function(nodes) {
columns.forEach(function(nodes) {
var node,
dy,
y0 = 0,
y = y0,
n = nodes.length,

@@ -252,14 +252,14 @@ i;

// Push any overlapping nodes down.
nodes.sort(ascendingDepth);
nodes.sort(ascendingBreadth);
for (i = 0; i < n; ++i) {
node = nodes[i];
dy = y0 - node.y;
if (dy > 0) node.y += dy;
y0 = node.y + node.dy + nodePadding;
dy = y - node.y0;
if (dy > 0) node.y0 += dy, node.y1 += dy;
y = node.y1 + py;
}
// If the bottommost node goes outside the bounds, push it back up.
dy = y0 - nodePadding - size[1];
dy = y - py - y1;
if (dy > 0) {
y0 = node.y -= dy;
y = (node.y0 -= dy), node.y1 -= dy;

@@ -269,5 +269,5 @@ // Push any overlapping nodes back up.

node = nodes[i];
dy = node.y + node.dy + nodePadding - y0;
if (dy > 0) node.y -= dy;
y0 = node.y;
dy = node.y1 + py - y;
if (dy > 0) node.y0 -= dy, node.y1 -= dy;
y = node.y0;
}

@@ -277,43 +277,21 @@ }

}
function ascendingDepth(a, b) {
return a.y - b.y;
}
}
function computeLinkDepths() {
nodes.forEach(function(node) {
node.sourceLinks.sort(ascendingTargetDepth);
node.targetLinks.sort(ascendingSourceDepth);
function computeLinkBreadths(graph) {
graph.nodes.forEach(function(node) {
node.sourceLinks.sort(ascendingTargetBreadth);
node.targetLinks.sort(ascendingSourceBreadth);
});
nodes.forEach(function(node) {
var sy = 0, ty = 0;
graph.nodes.forEach(function(node) {
var y0 = node.y0, y1 = y0;
node.sourceLinks.forEach(function(link) {
link.sy = sy;
sy += link.dy;
link.y0 = y0 + link.width / 2, y0 += link.width;
});
node.targetLinks.forEach(function(link) {
link.ty = ty;
ty += link.dy;
link.y1 = y1 + link.width / 2, y1 += link.width;
});
});
function ascendingSourceDepth(a, b) {
return (a.source.y - b.source.y) || (a.originalIndex - b.originalIndex);
}
function ascendingTargetDepth(a, b) {
return (a.target.y - b.target.y) || (a.originalIndex - b.originalIndex);
}
}
function center(node) {
return node.y + node.dy / 2;
}
function value(link) {
return link.value;
}
return sankey;
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc