chartx
Version:
Data Visualization Chart Library
510 lines (497 loc) • 14.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _default;
var _canvax = require("canvax");
function _default() {
var sankey = {},
nodeWidth = 24,
nodePadding = 8,
size = [1, 1],
nodes = [],
sort = function sort(a, b) {
return a.y - b.y;
},
// sort = function(a, b) {
// return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
// },
links = [];
sankey.nodeWidth = function (_) {
if (!arguments.length) return nodeWidth;
nodeWidth = +_;
return sankey;
};
sankey.nodeSort = function (_) {
if (!arguments.length) return sort;
sort = _;
return sankey;
};
sankey.nodePadding = function (_) {
if (!arguments.length) return nodePadding;
nodePadding = +_;
return sankey;
};
sankey.nodes = function (_) {
if (!arguments.length) return nodes;
nodes = _;
return sankey;
};
sankey.links = function (_) {
if (!arguments.length) return links;
links = _;
return sankey;
};
sankey.size = function (_) {
if (!arguments.length) return size;
size = _;
return sankey;
};
sankey.layout = function (iterations) {
computeNodeLinks();
computeNodeValues();
computeNodeBreadths();
computeNodeDepths(iterations);
computeLinkDepths();
return sankey;
};
sankey.relayout = function () {
computeLinkDepths();
return sankey;
};
//d3
function d3_interpolateNumber(a, b) {
a = +a, b = +b;
return function (t) {
return a * (1 - t) + b * t;
};
}
sankey.link = function () {
var curvature = .5;
function link(d) {
var x0 = d.source.x + d.source.dx,
x1 = d.target.x,
xi = d3_interpolateNumber(x0, x1),
x2 = xi(curvature),
x3 = xi(1 - curvature),
//y0 = d.source.y + d.sy + d.dy / 2,
//y1 = d.target.y + d.ty + d.dy / 2;
y0 = d.source.y + d.sy,
y1 = d.target.y + d.ty;
var dy = d.dy;
if (dy < 1) {
dy = 1;
}
;
var path = "M" + x0 + "," + y0 + "C" + x2 + "," + y0 + " " + x3 + "," + y1 + " " + x1 + "," + y1;
path += "v" + dy;
path += "C" + x3 + "," + (y1 + dy) + " " + x2 + "," + (y0 + dy) + " " + x0 + "," + (y0 + dy);
path += "v" + -dy + "z";
return path;
}
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) {
node.sourceLinks = [];
node.targetLinks = [];
});
links.forEach(function (link) {
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];
source.sourceLinks.push(link);
target.targetLinks.push(link);
});
}
function d3_sum(e, t) {
var n = 0,
r = e.length,
i,
s = -1;
if (arguments.length === 1) while (++s < r) isNaN(i = +e[s]) || (n += i);else while (++s < r) isNaN(i = +t.call(e, e[s], s)) || (n += i);
return n;
}
function d3_min(e, t) {
var n = -1,
r = e.length,
i,
s;
if (arguments.length === 1) {
while (++n < r && ((i = e[n]) == null || i != i)) i = undefined;
while (++n < r) (s = e[n]) != null && i > s && (i = s);
} else {
while (++n < r && ((i = t.call(e, e[n], n)) == null || i != i)) i = undefined;
while (++n < r) (s = t.call(e, e[n], n)) != null && i > s && (i = s);
}
return i;
}
function d3_max(e, t) {
var n = -1,
r = e.length,
i,
s;
if (arguments.length === 1) {
while (++n < r && ((i = e[n]) == null || i != i)) i = undefined;
while (++n < r) (s = e[n]) != null && s > i && (i = s);
} else {
while (++n < r && ((i = t.call(e, e[n], n)) == null || i != i)) i = undefined;
while (++n < r) (s = t.call(e, e[n], n)) != null && s > i && (i = s);
}
return i;
}
// Compute the value (size) of each node by summing the associated links.
function computeNodeValues() {
nodes.forEach(function (node) {
node.value = Math.max(d3_sum(node.sourceLinks, value), d3_sum(node.targetLinks, 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;
while (remainingNodes.length) {
nextNodes = [];
remainingNodes.forEach(function (node) {
node.x = x;
node.dx = nodeWidth;
node.sourceLinks.forEach(function (link) {
if (nextNodes.indexOf(link.target) < 0) {
nextNodes.push(link.target);
}
});
});
remainingNodes = nextNodes;
++x;
}
//
moveSinksRight(x);
scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
}
function moveSourcesRight() {
nodes.forEach(function (node) {
if (!node.targetLinks.length) {
node.x = d3_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;
}
});
}
function scaleNodeBreadths(kx) {
nodes.forEach(function (node) {
node.x *= kx;
});
}
//d3 core
function d3_class(ctor, properties) {
if (Object.defineProperty) {
for (var key in properties) {
//TODO:d3这里不支持ie,要想办法解决
Object.defineProperty(ctor.prototype, key, {
value: properties[key],
enumerable: false
});
}
} else {
//ie解决方案
_canvax._.extend(ctor.prototype, properties);
}
}
var d3_nest = function d3_nest() {
var nest = {},
keys = [],
sortKeys = [],
sortValues,
rollup;
function map(mapType, array, depth) {
if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
var i = -1,
n = array.length,
key = keys[depth++],
keyValue,
object,
setter,
valuesByKey = new d3_Map(),
values;
while (++i < n) {
if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
values.push(object);
} else {
valuesByKey.set(keyValue, [object]);
}
}
if (mapType) {
object = mapType();
setter = function setter(keyValue, values) {
object.set(keyValue, map(mapType, values, depth));
};
} else {
object = {};
setter = function setter(keyValue, values) {
object[keyValue] = map(mapType, values, depth);
};
}
valuesByKey.forEach(setter);
return object;
}
function entries(map, depth) {
if (depth >= keys.length) return map;
var array = [],
sortKey = sortKeys[depth++];
map.forEach(function (key, keyMap) {
array.push({
key: key,
values: entries(keyMap, depth)
});
});
return sortKey ? array.sort(function (a, b) {
return sortKey(a.key, b.key);
}) : array;
}
nest.map = function (array, mapType) {
return map(mapType, array, 0);
};
nest.entries = function (array) {
return entries(map(d3_map, array, 0), 0);
};
nest.key = function (d) {
keys.push(d);
return nest;
};
// Specifies the order for the most-recently specified key.
// Note: only applies to entries. Map keys are unordered!
nest.sortKeys = function (order) {
sortKeys[keys.length - 1] = order;
return nest;
};
// Specifies the order for leaf values.
// Applies to both maps and entries array.
nest.sortValues = function (order) {
sortValues = order;
return nest;
};
nest.rollup = function (f) {
rollup = f;
return nest;
};
return nest;
};
var d3_map = function d3_map(object, f) {
var map = new d3_Map();
if (object instanceof d3_Map) {
object.forEach(function (key, value) {
map.set(key, value);
});
} else if (Array.isArray(object)) {
var i = -1,
n = object.length,
o;
if (arguments.length === 1) while (++i < n) map.set(i, object[i]);else while (++i < n) map.set(f.call(object, o = object[i], i), o);
} else {
for (var key in object) map.set(key, object[key]);
}
return map;
};
function d3_Map() {
this._ = Object.create(null);
}
var d3_map_proto = "__proto__",
d3_map_zero = "\0";
d3_class(d3_Map, {
has: d3_map_has,
get: function get(key) {
return this._[d3_map_escape(key)];
},
set: function set(key, value) {
return this._[d3_map_escape(key)] = value;
},
remove: d3_map_remove,
keys: d3_map_keys,
values: function values() {
var values = [];
for (var key in this._) values.push(this._[key]);
return values;
},
entries: function entries() {
var entries = [];
for (var key in this._) entries.push({
key: d3_map_unescape(key),
value: this._[key]
});
return entries;
},
size: d3_map_size,
empty: d3_map_empty,
forEach: function forEach(f) {
for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]);
}
});
function d3_map_escape(key) {
return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key;
}
function d3_map_unescape(key) {
return (key += "")[0] === d3_map_zero ? key.slice(1) : key;
}
function d3_map_has(key) {
return d3_map_escape(key) in this._;
}
function d3_map_remove(key) {
return (key = d3_map_escape(key)) in this._ && delete this._[key];
}
function d3_map_keys() {
var keys = [];
for (var key in this._) keys.push(d3_map_unescape(key));
return keys;
}
function d3_map_size() {
var size = 0;
for (var key in this._) ++size;
return size;
}
function d3_map_empty() {
for (var key in this._) return false;
return true;
}
function d3_sortKey(a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
function computeNodeDepths(iterations) {
var nodesByBreadth = d3_nest().key(function (d) {
return d.x;
}).sortKeys(d3_sortKey).entries(nodes).map(function (d) {
return d.values;
});
//
initializeNodeDepth();
resolveCollisions();
for (var alpha = 1; iterations > 0; --iterations) {
relaxRightToLeft(alpha *= .99);
resolveCollisions();
relaxLeftToRight(alpha);
resolveCollisions();
}
function initializeNodeDepth() {
var ky = d3_min(nodesByBreadth, function (nodes) {
return (size[1] - (nodes.length - 1) * nodePadding) / d3_sum(nodes, value);
});
nodesByBreadth.forEach(function (nodes) {
nodes.forEach(function (node, i) {
node.y = i;
node.dy = node.value * ky;
});
});
links.forEach(function (link) {
link.dy = link.value * ky;
});
}
function relaxLeftToRight(alpha) {
nodesByBreadth.forEach(function (nodes, breadth) {
nodes.forEach(function (node) {
if (node.targetLinks.length) {
var y = d3_sum(node.targetLinks, weightedSource) / d3_sum(node.targetLinks, value);
node.y += (y - center(node)) * alpha;
}
});
});
function weightedSource(link) {
return center(link.source) * link.value;
}
}
function relaxRightToLeft(alpha) {
nodesByBreadth.slice().reverse().forEach(function (nodes) {
nodes.forEach(function (node) {
if (node.sourceLinks.length) {
var y = d3_sum(node.sourceLinks, weightedTarget) / d3_sum(node.sourceLinks, value);
node.y += (y - center(node)) * alpha;
}
});
});
function weightedTarget(link) {
return center(link.target) * link.value;
}
}
function resolveCollisions() {
nodesByBreadth.forEach(function (nodes) {
var node,
dy,
y0 = 0,
n = nodes.length,
i;
// Push any overlapping nodes down.
sort && nodes.sort(sort);
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;
}
// If the bottommost node goes outside the bounds, push it back up.
dy = y0 - nodePadding - size[1];
if (dy > 0) {
y0 = node.y -= dy;
// Push any overlapping nodes back up.
for (i = n - 2; i >= 0; --i) {
node = nodes[i];
dy = node.y + node.dy + nodePadding - y0;
if (dy > 0) node.y -= dy;
y0 = node.y;
}
}
});
}
function ascendingDepth(a, b) {
return a.y - b.y;
}
}
function computeLinkDepths() {
nodes.forEach(function (node) {
node.sourceLinks.sort(ascendingTargetDepth);
node.targetLinks.sort(ascendingSourceDepth);
});
nodes.forEach(function (node) {
var sy = 0,
ty = 0;
node.sourceLinks.forEach(function (link) {
link.sy = sy;
sy += link.dy;
});
node.targetLinks.forEach(function (link) {
link.ty = ty;
ty += link.dy;
});
});
function ascendingSourceDepth(a, b) {
return a.source.y - b.source.y;
}
function ascendingTargetDepth(a, b) {
return a.target.y - b.target.y;
}
}
function center(node) {
return node.y + node.dy / 2;
}
function value(link) {
return link.value;
}
return sankey;
}
;