d3的chord部分用於將關係或網路流繪製成一種圓形佈局。
這部分內容分為兩個方面,一方面是構造一個弦佈局,另一方面是構造一個產生帶狀圖形的生成器。
chord
chord的原始碼如下:
function chord() {
var padAngle = 0,
sortGroups = null,
sortSubgroups = null,
sortChords = null;
function chord(matrix) {
var n = matrix.length,
//matrix中每組數的總和
groupSums = [],
groupIndex = range(n),
subgroupIndex = [],
chords = [],
groups = chords.groups = new Array(n),
subgroups = new Array(n * n),
k,
x,
x0,
dx,
i,
j;
// 計算每組數的和以及所有數值的總和
k = 0, i = -1; while (++i < n) {
x = 0, j = -1; while (++j < n) {
x += matrix[i][j];
}
groupSums.push(x);
subgroupIndex.push(range(n));
k += x;
}
// sortGroups函式根據每組資料和的大小對groupIndex進行排序
if (sortGroups) groupIndex.sort(function(a, b) {
return sortGroups(groupSums[a], groupSums[b]);
});
// sortSubgroups函式根據每個資料大小在該組內進行索引的排序
if (sortSubgroups) subgroupIndex.forEach(function(d, i) {
d.sort(function(a, b) {
return sortSubgroups(matrix[i][a], matrix[i][b]);
});
});
// 計算除去padAngle之後的單位弧度(每單位數值對應的弧度)
k = max$1(0, tau$3 - padAngle * n) / k;
dx = k ? padAngle : tau$3 / n;
// 計算每個資料對應的startAngle和endAngle
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],
// startAngle
a0 = x,
// 計算endAngle
a1 = x += v * k;
//記錄matrix中每個資料在弦圖中的資訊
subgroups[dj * n + di] = {
index: di,
subindex: dj,
startAngle: a0,
endAngle: a1,
value: v
};
}
//記錄matrix中每組資料在弦圖中的資訊
groups[di] = {
index: di,
startAngle: x0,
endAngle: x,
value: groupSums[di]
};
//考慮弦圖中每組之間的間距
x += dx;
}
// 產生source和target
i = -1; while (++i < n) {
j = i - 1; while (++j < n) {
var source = subgroups[j * n + i],
target = subgroups[i * n + j];
if (source.value || target.value) {
//將value大的設定為source,小的設定為target
chords.push(source.value < target.value
? {source: target, target: source}
: {source: source, target: target});
}
}
}
return sortChords ? chords.sort(sortChords) : chords;
}
// 設定相鄰組之間的間距,以弧度形式表示
chord.padAngle = function(_) {
return arguments.length ? (padAngle = max$1(0, _), chord) : padAngle;
};
// 對groupIndex進行排序
chord.sortGroups = function(_) {
return arguments.length ? (sortGroups = _, chord) : sortGroups;
};
// 對subgroupIndex進行排序
chord.sortSubgroups = function(_) {
return arguments.length ? (sortSubgroups = _, chord) : sortSubgroups;
};
//對chords陣列進行排序,影響的是chord的層疊順序,兩根弦重疊,重疊部分後面的會覆蓋掉前面的
chord.sortChords = function(_) {
return arguments.length ? (_ == null ? sortChords = null : (sortChords = compareValue(_))._ = _, chord) : sortChords && sortChords._;
};
return chord;
}
複製程式碼
chord函式最終會得到一個包含多組source
和target
物件的陣列以及groups
陣列,通過將該結果傳遞給d3.arc
來繪製弦圖外層的圓弧,而其內部的帶狀圖則通過d3.ribbon
來實現。
ribbon
用於繪製弦圖中間部分表示各塊之間聯絡的帶狀區域。
function ribbon() {
var source = defaultSource,
target = defaultTarget,
radius = defaultRadius$1,
startAngle = defaultStartAngle,
endAngle = defaultEndAngle,
context = null;
function ribbon() {
var buffer,
argv = slice$5.call(arguments),
//source物件
s = source.apply(this, argv),
//target物件
t = target.apply(this, argv),
//帶狀圖形中弧線的半徑
sr = +radius.apply(this, (argv[0] = s, argv)),
sa0 = startAngle.apply(this, argv) - halfPi$2,
sa1 = endAngle.apply(this, argv) - halfPi$2,
sx0 = sr * cos(sa0),
sy0 = sr * sin(sa0),
tr = +radius.apply(this, (argv[0] = t, argv)),
ta0 = startAngle.apply(this, argv) - halfPi$2,
ta1 = endAngle.apply(this, argv) - halfPi$2;
//構造path物件,用於儲存路徑
if (!context) context = buffer = path();
//移動到startAngle對應的起始點
context.moveTo(sx0, sy0);
//向endAngle位置畫弧線
context.arc(0, 0, sr, sa0, sa1);
//判斷source和target是否是同個位置
if (sa0 !== ta0 || sa1 !== ta1) { // TODO sr !== tr?
// 從source的endAngle位置繪製貝塞爾曲線至target的startAngle處
context.quadraticCurveTo(0, 0, tr * cos(ta0), tr * sin(ta0));
//target的startAngle繪製圓弧至endAngle位置
context.arc(0, 0, tr, ta0, ta1);
}
//以(0, 0)為控制點繪製貝塞爾曲線至startAngle位置
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$11(+_), ribbon) : radius;
};
ribbon.startAngle = function(_) {
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant$11(+_), ribbon) : startAngle;
};
ribbon.endAngle = function(_) {
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant$11(+_), 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;
}
複製程式碼