2010-11-29 00:15:07 +03:00
|
|
|
d3["layout"] = {};
|
|
|
|
d3["layout"]["chord"] = function() {
|
|
|
|
var chord = {},
|
|
|
|
chords,
|
|
|
|
groups,
|
|
|
|
matrix,
|
|
|
|
n,
|
|
|
|
padding = 0,
|
|
|
|
sortGroups,
|
|
|
|
sortSubgroups,
|
|
|
|
sortChords;
|
|
|
|
|
|
|
|
function relayout() {
|
|
|
|
var subgroups = {},
|
|
|
|
groupSums = [],
|
|
|
|
groupIndex = d3.range(n),
|
|
|
|
subgroupIndex = [],
|
|
|
|
k,
|
|
|
|
x,
|
|
|
|
x0,
|
|
|
|
i,
|
|
|
|
j;
|
|
|
|
|
|
|
|
chords = [];
|
|
|
|
groups = [];
|
|
|
|
|
|
|
|
// Compute the sum.
|
|
|
|
k = 0, i = -1; while (++i < n) {
|
|
|
|
x = 0, j = -1; while (++j < n) {
|
|
|
|
x += matrix[i][j];
|
|
|
|
}
|
|
|
|
groupSums.push(x);
|
|
|
|
subgroupIndex.push(d3.range(n));
|
|
|
|
k += x;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort groups…
|
|
|
|
if (sortGroups) {
|
|
|
|
groupIndex.sort(function(a, b) {
|
|
|
|
return sortGroups(groupSums[a], groupSums[b]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort subgroups…
|
|
|
|
if (sortSubgroups) {
|
|
|
|
subgroupIndex.forEach(function(d, i) {
|
|
|
|
d.sort(function(a, b) {
|
|
|
|
return sortSubgroups(matrix[i][a], 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?
|
|
|
|
k = (2 * Math.PI - padding * n) / k;
|
|
|
|
|
|
|
|
// Compute the start and end angle for each group and subgroup.
|
|
|
|
x = 0, i = -1; while (++i < n) {
|
|
|
|
x0 = x, j = -1; while (++j < n) {
|
|
|
|
var di = groupIndex[i],
|
|
|
|
dj = subgroupIndex[di][j],
|
|
|
|
v = matrix[di][dj];
|
|
|
|
subgroups[i + "-" + j] = {
|
|
|
|
"index": di,
|
|
|
|
"subindex": dj,
|
|
|
|
"startAngle": x,
|
|
|
|
"endAngle": x += v * k,
|
|
|
|
"value": v
|
|
|
|
};
|
|
|
|
}
|
|
|
|
groups.push({
|
|
|
|
"index": di,
|
|
|
|
"startAngle": x0,
|
|
|
|
"endAngle": x,
|
|
|
|
"value": (x - x0) / k
|
|
|
|
});
|
|
|
|
x += padding;
|
|
|
|
}
|
|
|
|
|
2010-11-29 03:28:48 +03:00
|
|
|
// Generate chords for each (non-empty) subgroup-subgroup link.
|
2010-11-29 00:15:07 +03:00
|
|
|
i = -1; while (++i < n) {
|
|
|
|
j = i - 1; while (++j < n) {
|
2010-11-29 03:28:48 +03:00
|
|
|
var source = subgroups[i + "-" + j],
|
|
|
|
target = subgroups[j + "-" + i];
|
|
|
|
if (source["value"] || target["value"]) {
|
|
|
|
chords.push({
|
|
|
|
"source": source,
|
|
|
|
"target": target
|
|
|
|
})
|
|
|
|
}
|
2010-11-29 00:15:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sortChords) resort();
|
|
|
|
}
|
|
|
|
|
|
|
|
function resort() {
|
|
|
|
chords.sort(function(a, b) {
|
|
|
|
a = Math.min(a["source"]["value"], a["target"]["value"]);
|
|
|
|
b = Math.min(b["source"]["value"], b["target"]["value"]);
|
|
|
|
return sortChords(a, b);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
chord["matrix"] = function(x) {
|
|
|
|
if (!arguments.length) return matrix;
|
|
|
|
n = (matrix = x) && matrix.length;
|
|
|
|
chords = groups = null;
|
|
|
|
return chord;
|
|
|
|
};
|
|
|
|
|
|
|
|
chord["padding"] = function(x) {
|
|
|
|
if (!arguments.length) return padding;
|
|
|
|
padding = x;
|
|
|
|
chords = groups = null;
|
|
|
|
return chord;
|
|
|
|
};
|
|
|
|
|
|
|
|
chord["sortGroups"] = function(x) {
|
|
|
|
if (!arguments.length) return sortGroups;
|
|
|
|
sortGroups = x;
|
|
|
|
chords = groups = null;
|
|
|
|
return chord;
|
|
|
|
};
|
|
|
|
|
|
|
|
chord["sortSubgroups"] = function(x) {
|
|
|
|
if (!arguments.length) return sortSubgroups;
|
|
|
|
sortSubgroups = x;
|
|
|
|
chords = null;
|
|
|
|
return chord;
|
|
|
|
};
|
|
|
|
|
|
|
|
chord["sortChords"] = function(x) {
|
|
|
|
if (!arguments.length) return sortChords;
|
|
|
|
sortChords = x;
|
|
|
|
if (chords) resort();
|
|
|
|
return chord;
|
|
|
|
};
|
|
|
|
|
|
|
|
chord["chords"] = function() {
|
|
|
|
if (!chords) relayout();
|
|
|
|
return chords;
|
|
|
|
};
|
|
|
|
|
|
|
|
chord["groups"] = function() {
|
|
|
|
if (!groups) relayout();
|
|
|
|
return groups;
|
|
|
|
};
|
|
|
|
|
|
|
|
return chord;
|
|
|
|
};
|