|
1 d3.layout.chord = function() { |
|
2 var chord = {}, |
|
3 chords, |
|
4 groups, |
|
5 matrix, |
|
6 n, |
|
7 padding = 0, |
|
8 sortGroups, |
|
9 sortSubgroups, |
|
10 sortChords; |
|
11 |
|
12 function relayout() { |
|
13 var subgroups = {}, |
|
14 groupSums = [], |
|
15 groupIndex = d3.range(n), |
|
16 subgroupIndex = [], |
|
17 k, |
|
18 x, |
|
19 x0, |
|
20 i, |
|
21 j; |
|
22 |
|
23 chords = []; |
|
24 groups = []; |
|
25 |
|
26 // Compute the sum. |
|
27 k = 0, i = -1; while (++i < n) { |
|
28 x = 0, j = -1; while (++j < n) { |
|
29 x += matrix[i][j]; |
|
30 } |
|
31 groupSums.push(x); |
|
32 subgroupIndex.push(d3.range(n)); |
|
33 k += x; |
|
34 } |
|
35 |
|
36 // Sort groups… |
|
37 if (sortGroups) { |
|
38 groupIndex.sort(function(a, b) { |
|
39 return sortGroups(groupSums[a], groupSums[b]); |
|
40 }); |
|
41 } |
|
42 |
|
43 // Sort subgroups… |
|
44 if (sortSubgroups) { |
|
45 subgroupIndex.forEach(function(d, i) { |
|
46 d.sort(function(a, b) { |
|
47 return sortSubgroups(matrix[i][a], matrix[i][b]); |
|
48 }); |
|
49 }); |
|
50 } |
|
51 |
|
52 // Convert the sum to scaling factor for [0, 2pi]. |
|
53 // TODO Allow start and end angle to be specified. |
|
54 // TODO Allow padding to be specified as percentage? |
|
55 k = (2 * Math.PI - padding * n) / k; |
|
56 |
|
57 // Compute the start and end angle for each group and subgroup. |
|
58 // Note: Opera has a bug reordering object literal properties! |
|
59 x = 0, i = -1; while (++i < n) { |
|
60 x0 = x, j = -1; while (++j < n) { |
|
61 var di = groupIndex[i], |
|
62 dj = subgroupIndex[di][j], |
|
63 v = matrix[di][dj], |
|
64 a0 = x, |
|
65 a1 = x += v * k; |
|
66 subgroups[di + "-" + dj] = { |
|
67 index: di, |
|
68 subindex: dj, |
|
69 startAngle: a0, |
|
70 endAngle: a1, |
|
71 value: v |
|
72 }; |
|
73 } |
|
74 groups.push({ |
|
75 index: di, |
|
76 startAngle: x0, |
|
77 endAngle: x, |
|
78 value: (x - x0) / k |
|
79 }); |
|
80 x += padding; |
|
81 } |
|
82 |
|
83 // Generate chords for each (non-empty) subgroup-subgroup link. |
|
84 i = -1; while (++i < n) { |
|
85 j = i - 1; while (++j < n) { |
|
86 var source = subgroups[i + "-" + j], |
|
87 target = subgroups[j + "-" + i]; |
|
88 if (source.value || target.value) { |
|
89 chords.push(source.value < target.value |
|
90 ? {source: target, target: source} |
|
91 : {source: source, target: target}); |
|
92 } |
|
93 } |
|
94 } |
|
95 |
|
96 if (sortChords) resort(); |
|
97 } |
|
98 |
|
99 function resort() { |
|
100 chords.sort(function(a, b) { |
|
101 return sortChords( |
|
102 (a.source.value + a.target.value) / 2, |
|
103 (b.source.value + b.target.value) / 2); |
|
104 }); |
|
105 } |
|
106 |
|
107 chord.matrix = function(x) { |
|
108 if (!arguments.length) return matrix; |
|
109 n = (matrix = x) && matrix.length; |
|
110 chords = groups = null; |
|
111 return chord; |
|
112 }; |
|
113 |
|
114 chord.padding = function(x) { |
|
115 if (!arguments.length) return padding; |
|
116 padding = x; |
|
117 chords = groups = null; |
|
118 return chord; |
|
119 }; |
|
120 |
|
121 chord.sortGroups = function(x) { |
|
122 if (!arguments.length) return sortGroups; |
|
123 sortGroups = x; |
|
124 chords = groups = null; |
|
125 return chord; |
|
126 }; |
|
127 |
|
128 chord.sortSubgroups = function(x) { |
|
129 if (!arguments.length) return sortSubgroups; |
|
130 sortSubgroups = x; |
|
131 chords = null; |
|
132 return chord; |
|
133 }; |
|
134 |
|
135 chord.sortChords = function(x) { |
|
136 if (!arguments.length) return sortChords; |
|
137 sortChords = x; |
|
138 if (chords) resort(); |
|
139 return chord; |
|
140 }; |
|
141 |
|
142 chord.chords = function() { |
|
143 if (!chords) relayout(); |
|
144 return chords; |
|
145 }; |
|
146 |
|
147 chord.groups = function() { |
|
148 if (!groups) relayout(); |
|
149 return groups; |
|
150 }; |
|
151 |
|
152 return chord; |
|
153 }; |