|
(function (global, factory) { |
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array'), require('d3-path')) : |
|
typeof define === 'function' && define.amd ? define(['exports', 'd3-array', 'd3-path'], factory) : |
|
(factory((global.d3 = global.d3 || {}),global.d3,global.d3)); |
|
}(this, function (exports,d3Array,d3Path) { 'use strict'; |
|
|
|
var cos = Math.cos; |
|
var sin = Math.sin; |
|
var pi = Math.PI; |
|
var halfPi = pi / 2; |
|
var tau = pi * 2; |
|
var max = Math.max; |
|
|
|
function compareValue(compare) { |
|
return function(a, b) { |
|
return compare( |
|
a.source.value + a.target.value, |
|
b.source.value + b.target.value |
|
); |
|
}; |
|
} |
|
|
|
function multichord() { |
|
var padAngle = 0, |
|
sortGroups = null, |
|
sortSubgroups = null, |
|
sortChords = null; |
|
|
|
function multichord(matrix) { |
|
var n = matrix.length, |
|
nCategories = matrix[0][0].length, |
|
groupSums = {}, |
|
groupIndex = d3Array.range(n), |
|
subgroupIndex = [], |
|
chords = [], |
|
groups = chords.groups = new Array(n), |
|
subgroups = chords.subgroups = new Array(n * n), |
|
z, |
|
k, |
|
x, |
|
x0, |
|
dx, |
|
i, |
|
j; |
|
|
|
// Compute the sum. |
|
z = 0, i = -1; while (++i < n) { |
|
if (!groupSums[i]){ |
|
groupSums[i] = {} |
|
} |
|
|
|
x = 0, j = -1; while (++j < n) { |
|
if (!groupSums[j]){ |
|
groupSums[j] = {} |
|
} |
|
x += d3Array.sum(matrix[i][j]) |
|
if (!groupSums[i].in){ |
|
groupSums[i].in = d3Array.sum(matrix[i][j]) |
|
} else { |
|
groupSums[i].in += d3Array.sum(matrix[i][j]) |
|
} |
|
|
|
if (!groupSums[j].out){ |
|
groupSums[j].out = d3Array.sum(matrix[i][j]) |
|
} else { |
|
groupSums[j].out += d3Array.sum(matrix[i][j]) |
|
} |
|
|
|
} |
|
subgroupIndex.push(d3Array.range(n)); |
|
z += x; |
|
} |
|
|
|
// Sort groups… |
|
if (sortGroups) groupIndex.sort(function(a, b) { |
|
return sortGroups(groupSums[a].in, groupSums[b].in); |
|
}); |
|
|
|
// Sort subgroups… |
|
if (sortSubgroups) subgroupIndex.forEach(function(d, i) { |
|
d.sort(function(a, b) { |
|
return sortSubgroups(d3Array.sum(matrix[i][a]), d3Array.sum(matrix[i][b])); |
|
}); |
|
}); |
|
|
|
// Convert the sum to scaling factor for [0, 2pi]. |
|
// TODO Allow start and end angle to be specified? |
|
// TODO Allow padding to be specified as percentage? |
|
z = max(0, tau - padAngle * n) / z; |
|
dx = z ? padAngle : tau / n; |
|
|
|
// Compute the start and end angle for each group and subgroup. |
|
// Note: Opera has a bug reordering object literal properties! |
|
x = 0, i = -1; while (++i < n) { |
|
x0 = x, j = -1; while (++j < n) { |
|
var di = groupIndex[i], |
|
dj = subgroupIndex[di][j], |
|
v = d3Array.sum(matrix[di][dj]), |
|
a0 = x; |
|
x += v * z; |
|
|
|
subgroups[dj * n + di] = new Array(nCategories), k = -1; while (++k < nCategories) { |
|
v = matrix[di][dj][k]; |
|
var b0 = a0, |
|
b1 = a0 += v * z; |
|
|
|
subgroups[dj * n + di][k] = { |
|
index: di, |
|
subindex: dj, |
|
startAngle: b0, |
|
endAngle: b1, |
|
value: v, |
|
category: k, |
|
}; |
|
}; |
|
} |
|
groups[di] = { |
|
index: di, |
|
startAngle: x0, |
|
endAngle: x, |
|
value: {in: groupSums[di].in, |
|
out: groupSums[di].out} |
|
}; |
|
x += dx; |
|
} |
|
|
|
// Generate chords for each (non-empty) subgroup-subgroup link. |
|
i = -1; while (++i < n) { |
|
j = i - 1; while (++j < n) { |
|
k = -1; while (++k < nCategories) { |
|
var source = subgroups[j * n + i][k], |
|
target = subgroups[i * n + j][k]; |
|
if (source.value || target.value) { |
|
chords.push(source.value < target.value |
|
? {source: target, target: source} |
|
: {source: source, target: target}); |
|
} |
|
} |
|
} |
|
} |
|
|
|
return sortChords ? chords.sort(sortChords) : chords; |
|
} |
|
|
|
multichord.padAngle = function(_) { |
|
return arguments.length ? (padAngle = max(0, _), multichord) : padAngle; |
|
}; |
|
|
|
multichord.sortGroups = function(_) { |
|
return arguments.length ? (sortGroups = _, multichord) : sortGroups; |
|
}; |
|
|
|
multichord.sortSubgroups = function(_) { |
|
return arguments.length ? (sortSubgroups = _, multichord) : sortSubgroups; |
|
}; |
|
|
|
multichord.sortChords = function(_) { |
|
return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, multichord) : sortChords && sortChords._; |
|
}; |
|
|
|
return multichord; |
|
} |
|
|
|
var slice = Array.prototype.slice; |
|
|
|
function constant(x) { |
|
return function() { |
|
return x; |
|
}; |
|
} |
|
|
|
function defaultSource(d) { |
|
return d.source; |
|
} |
|
|
|
function defaultTarget(d) { |
|
return d.target; |
|
} |
|
|
|
function defaultRadius(d) { |
|
return d.radius; |
|
} |
|
|
|
function defaultStartAngle(d) { |
|
return d.startAngle; |
|
} |
|
|
|
function defaultEndAngle(d) { |
|
return d.endAngle; |
|
} |
|
|
|
function ribbon() { |
|
var source = defaultSource, |
|
target = defaultTarget, |
|
radius = defaultRadius, |
|
startAngle = defaultStartAngle, |
|
endAngle = defaultEndAngle, |
|
context = null; |
|
|
|
function ribbon() { |
|
var buffer, |
|
argv = slice.call(arguments), |
|
s = source.apply(this, argv), |
|
t = target.apply(this, argv), |
|
sr = +radius.apply(this, (argv[0] = s, argv)), |
|
sa0 = startAngle.apply(this, argv) - halfPi, |
|
sa1 = endAngle.apply(this, argv) - halfPi, |
|
sx0 = sr * cos(sa0), |
|
sy0 = sr * sin(sa0), |
|
tr = +radius.apply(this, (argv[0] = t, argv)), |
|
ta0 = startAngle.apply(this, argv) - halfPi, |
|
ta1 = endAngle.apply(this, argv) - halfPi; |
|
|
|
if (!context) context = buffer = d3Path.path(); |
|
|
|
context.moveTo(sx0, sy0); |
|
context.arc(0, 0, sr, sa0, sa1); |
|
if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr? |
|
context.quadraticCurveTo(0, 0, tr * cos(ta0), tr * sin(ta0)); |
|
context.arc(0, 0, tr, ta0, ta1); |
|
} |
|
context.quadraticCurveTo(0, 0, sx0, sy0); |
|
context.closePath(); |
|
|
|
if (buffer) return context = null, buffer + "" || null; |
|
} |
|
|
|
ribbon.radius = function(_) { |
|
return arguments.length ? (radius = typeof _ === "function" ? _ : constant(+_), ribbon) : radius; |
|
}; |
|
|
|
ribbon.startAngle = function(_) { |
|
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant(+_), ribbon) : startAngle; |
|
}; |
|
|
|
ribbon.endAngle = function(_) { |
|
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant(+_), ribbon) : endAngle; |
|
}; |
|
|
|
ribbon.source = function(_) { |
|
return arguments.length ? (source = _, ribbon) : source; |
|
}; |
|
|
|
ribbon.target = function(_) { |
|
return arguments.length ? (target = _, ribbon) : target; |
|
}; |
|
|
|
ribbon.context = function(_) { |
|
return arguments.length ? ((context = _ == null ? null : _), ribbon) : context; |
|
}; |
|
|
|
return ribbon; |
|
} |
|
|
|
exports.multichord = multichord; |
|
exports.ribbon = ribbon; |
|
|
|
Object.defineProperty(exports, '__esModule', { value: true }); |
|
|
|
})); |