|
| 1 | +<!DOCTYPE html> |
| 2 | +<meta charset="utf-8"> |
| 3 | +<title>PivotGraph (Rollup) Layout</title> |
| 4 | + |
| 5 | +<style> |
| 6 | + body { font-family: sans-serif; font-size: 10px; } |
| 7 | + path { fill: none; stroke: #ccc; } |
| 8 | + circle { fill: #fff; stroke: steelblue; } |
| 9 | +</style> |
| 10 | +<script src="../../d3.js"></script> |
| 11 | + |
| 12 | +<body> |
| 13 | +<script> |
| 14 | +var social = { |
| 15 | + nodes: [ |
| 16 | + {gender: "M", group: 1}, |
| 17 | + {gender: "F", group: 2}, |
| 18 | + {gender: "M", group: 1}, |
| 19 | + {gender: "M", group: 2}, |
| 20 | + {gender: "F", group: 2}, |
| 21 | + {gender: "M", group: 1}, |
| 22 | + {gender: "F", group: 1}, |
| 23 | + {gender: "M", group: 2}, |
| 24 | + {gender: "F", group: 2}, |
| 25 | + {gender: "F", group: 2} |
| 26 | + ], |
| 27 | + links: [ |
| 28 | + {source: 0, target: 1}, |
| 29 | + {source: 1, target: 2}, |
| 30 | + {source: 2, target: 3}, |
| 31 | + {source: 2, target: 4}, |
| 32 | + {source: 2, target: 5}, |
| 33 | + {source: 2, target: 6}, |
| 34 | + {source: 2, target: 7}, |
| 35 | + {source: 5, target: 6}, |
| 36 | + {source: 6, target: 7}, |
| 37 | + {source: 5, target: 8}, |
| 38 | + {source: 8, target: 6}, |
| 39 | + {source: 6, target: 9} |
| 40 | + ] |
| 41 | +}; |
| 42 | + |
| 43 | +var w = 100, |
| 44 | + h = 100, |
| 45 | + p = 30, |
| 46 | + x = d3.scale.ordinal().domain(social.nodes.map(fx)).range([0, w]), |
| 47 | + y = d3.scale.ordinal().domain(social.nodes.map(fy)).range([0, h]); |
| 48 | + |
| 49 | +function fx(d) { return d.gender; } |
| 50 | +function fy(d) { return d.group; } |
| 51 | + |
| 52 | +var vis = d3.select("body").append("svg") |
| 53 | + .attr("w", w + 2 * p) |
| 54 | + .attr("h", h + 2 * p) |
| 55 | + .append("g") |
| 56 | + .attr("transform", "translate(" + [p, p] + ")"); |
| 57 | + |
| 58 | +var layout = rollup() |
| 59 | + .x(function(d) { return x(fx(d)); }) |
| 60 | + .y(function(d) { return y(fy(d)); }); |
| 61 | + |
| 62 | +var g = layout(social); |
| 63 | + |
| 64 | +vis.selectAll("path") |
| 65 | + .data(g.links) |
| 66 | + .enter().append("path") |
| 67 | + .style("stroke-width", function(d) { return d.value * 1.5; }) |
| 68 | + .attr("d", function(d) { |
| 69 | + var tx = d.target.x, |
| 70 | + sx = d.source.x, |
| 71 | + ty = d.target.y, |
| 72 | + sy = d.source.y, |
| 73 | + dx = tx - sx, |
| 74 | + dy = ty - sy, |
| 75 | + dr = 2 * Math.sqrt(dx * dx + dy * dy); |
| 76 | + return "M" + sx + "," + sy + "A" + dr + "," + dr + " 0 0,1 " + tx + "," + ty; |
| 77 | + }); |
| 78 | + |
| 79 | +vis.selectAll("circle") |
| 80 | + .data(g.nodes) |
| 81 | + .enter().append("circle") |
| 82 | + .attr("r", function(d) { return Math.sqrt(d.nodes.length * 20); }) |
| 83 | + .attr("cx", function(d) { return d.x; }) |
| 84 | + .attr("cy", function(d) { return d.y; }); |
| 85 | + |
| 86 | +vis.selectAll("text.x") |
| 87 | + .data(x.domain()) |
| 88 | + .enter().append("text") |
| 89 | + .attr("class", "x") |
| 90 | + .attr("x", x) |
| 91 | + .attr("y", -10) |
| 92 | + .attr("text-anchor", "middle") |
| 93 | + .text(String); |
| 94 | + |
| 95 | +vis.selectAll("text.y") |
| 96 | + .data(y.domain()) |
| 97 | + .enter().append("text") |
| 98 | + .attr("class", "y") |
| 99 | + .attr("x", -10) |
| 100 | + .attr("dy", ".3em") |
| 101 | + .attr("y", y) |
| 102 | + .attr("text-anchor", "end") |
| 103 | + .text(String); |
| 104 | + |
| 105 | +function rollup() { |
| 106 | + var directed = true, |
| 107 | + x_ = rollupX, |
| 108 | + y_ = rollupY, |
| 109 | + nodes_ = rollupNodes, |
| 110 | + links_ = rollupLinks, |
| 111 | + linkValue = rollupLinkValue, |
| 112 | + linkSource = rollupLinkSource, |
| 113 | + linkTarget = rollupLinkTarget; |
| 114 | + |
| 115 | + function rollup(d, i) { |
| 116 | + var nodes = nodes_.call(this, d, i), |
| 117 | + links = links_.call(this, d, i), |
| 118 | + n = nodes.length, |
| 119 | + m = links.length, |
| 120 | + i = -1, |
| 121 | + x = [], |
| 122 | + y = [], |
| 123 | + rnindex = 0, |
| 124 | + rnodes = {}, |
| 125 | + rlinks = {}; |
| 126 | + |
| 127 | + // Compute rollup nodes. |
| 128 | + while (++i < n) { |
| 129 | + (d = nodes[i]).index = i; |
| 130 | + x[i] = x_.call(this, d, i); |
| 131 | + y[i] = y_.call(this, d, i); |
| 132 | + var nodeId = id(i), |
| 133 | + rn = rnodes[nodeId]; |
| 134 | + if (!rn) { |
| 135 | + rn = rnodes[nodeId] = { |
| 136 | + index: rnindex++, |
| 137 | + x: x[i], |
| 138 | + y: y[i], |
| 139 | + nodes: [] |
| 140 | + }; |
| 141 | + } |
| 142 | + rn.nodes.push(d); |
| 143 | + } |
| 144 | + |
| 145 | + // Compute rollup links. |
| 146 | + i = -1; while (++i < m) { |
| 147 | + var value = linkValue.call(this, d = links[i], i), |
| 148 | + source = linkSource.call(this, d, i), |
| 149 | + target = linkTarget.call(this, d, i), |
| 150 | + rsource = rnodes[id(typeof source === "number" ? source : source.index)], |
| 151 | + rtarget = rnodes[id(typeof target === "number" ? target : target.index)], |
| 152 | + linkId = !directed && rsource.index > rtarget.index |
| 153 | + ? rtarget.index + "," + rsource.index |
| 154 | + : rsource.index + "," + rtarget.index, |
| 155 | + rl = rlinks[linkId]; |
| 156 | + if (!rl) { |
| 157 | + rl = rlinks[linkId] = { |
| 158 | + source: rsource, |
| 159 | + target: rtarget, |
| 160 | + value: 0, |
| 161 | + links: [] |
| 162 | + }; |
| 163 | + } |
| 164 | + rl.links.push(links[i]); |
| 165 | + rl.value += value; |
| 166 | + } |
| 167 | + |
| 168 | + return { |
| 169 | + nodes: d3.values(rnodes), |
| 170 | + links: d3.values(rlinks) |
| 171 | + }; |
| 172 | + |
| 173 | + function id(i) { |
| 174 | + return x[i] + "," + y[i]; |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + rollup.x = function(x) { |
| 179 | + if (!arguments.length) return x_; |
| 180 | + x_ = x; |
| 181 | + return rollup; |
| 182 | + }; |
| 183 | + |
| 184 | + rollup.y = function(x) { |
| 185 | + if (!arguments.length) return y_; |
| 186 | + y_ = x; |
| 187 | + return rollup; |
| 188 | + }; |
| 189 | + |
| 190 | + rollup.nodes = function(x) { |
| 191 | + if (!arguments.length) return nodes_; |
| 192 | + nodes_ = x; |
| 193 | + return rollup; |
| 194 | + }; |
| 195 | + |
| 196 | + rollup.links = function(x) { |
| 197 | + if (!arguments.length) return links_; |
| 198 | + links_ = x; |
| 199 | + return rollup; |
| 200 | + }; |
| 201 | + |
| 202 | + rollup.linkSource = function(x) { |
| 203 | + if (!arguments.length) return linkSource; |
| 204 | + linkSource = x; |
| 205 | + return rollup; |
| 206 | + }; |
| 207 | + |
| 208 | + rollup.linkTarget = function(x) { |
| 209 | + if (!arguments.length) return linkTarget; |
| 210 | + linkTarget = x; |
| 211 | + return rollup; |
| 212 | + }; |
| 213 | + |
| 214 | + rollup.linkValue = function(x) { |
| 215 | + if (!arguments.length) return linkValue; |
| 216 | + linkValue = x; |
| 217 | + return rollup; |
| 218 | + }; |
| 219 | + |
| 220 | + rollup.directed = function(x) { |
| 221 | + if (!arguments.length) return directed; |
| 222 | + directed = x; |
| 223 | + return rollup; |
| 224 | + }; |
| 225 | + |
| 226 | + return rollup; |
| 227 | + |
| 228 | + function rollupX(d) { return d.x; } |
| 229 | + function rollupY(d) { return d.y; } |
| 230 | + function rollupNodes(d) { return d.nodes; } |
| 231 | + function rollupLinks(d) { return d.links; } |
| 232 | + function rollupLinkValue(d) { return 1; } |
| 233 | + function rollupLinkSource(d) { return d.source; } |
| 234 | + function rollupLinkTarget(d) { return d.target; } |
| 235 | +} |
| 236 | +</script> |
0 commit comments