|
1 <!DOCTYPE html> |
|
2 <html> |
|
3 <head> |
|
4 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> |
|
5 <title>Force-Directed Graph</title> |
|
6 <script type="text/javascript" src="../../d3.js"></script> |
|
7 <script type="text/javascript" src="../../d3.geom.js"></script> |
|
8 <script type="text/javascript" src="../../d3.layout.js"></script> |
|
9 <style type="text/css"> |
|
10 |
|
11 circle.node { |
|
12 cursor: pointer; |
|
13 stroke: #3182bd; |
|
14 stroke-width: 1.5px; |
|
15 } |
|
16 |
|
17 line.link { |
|
18 fill: none; |
|
19 stroke: #9ecae1; |
|
20 stroke-width: 1.5px; |
|
21 } |
|
22 |
|
23 </style> |
|
24 </head> |
|
25 <body> |
|
26 <div id="chart"></div> |
|
27 <script type="text/javascript"> |
|
28 |
|
29 var w = 960, |
|
30 h = 500, |
|
31 node, |
|
32 link, |
|
33 root; |
|
34 |
|
35 var force = d3.layout.force() |
|
36 .on("tick", tick) |
|
37 .size([w, h]); |
|
38 |
|
39 var vis = d3.select("#chart").append("svg:svg") |
|
40 .attr("width", w) |
|
41 .attr("height", h); |
|
42 |
|
43 d3.json("../data/flare.json", function(json) { |
|
44 root = json; |
|
45 update(); |
|
46 }); |
|
47 |
|
48 function update() { |
|
49 var nodes = flatten(root), |
|
50 links = d3.layout.tree().links(nodes); |
|
51 |
|
52 // Restart the force layout. |
|
53 force |
|
54 .nodes(nodes) |
|
55 .links(links) |
|
56 .start(); |
|
57 |
|
58 // Update the links… |
|
59 link = vis.selectAll("line.link") |
|
60 .data(links, function(d) { return d.target.id; }); |
|
61 |
|
62 // Enter any new links. |
|
63 link.enter().insert("svg:line", ".node") |
|
64 .attr("class", "link") |
|
65 .attr("x1", function(d) { return d.source.x; }) |
|
66 .attr("y1", function(d) { return d.source.y; }) |
|
67 .attr("x2", function(d) { return d.target.x; }) |
|
68 .attr("y2", function(d) { return d.target.y; }); |
|
69 |
|
70 // Exit any old links. |
|
71 link.exit().remove(); |
|
72 |
|
73 // Update the nodes… |
|
74 node = vis.selectAll("circle.node") |
|
75 .data(nodes, function(d) { return d.id; }) |
|
76 .style("fill", color); |
|
77 |
|
78 // Enter any new nodes. |
|
79 node.enter().append("svg:circle") |
|
80 .attr("class", "node") |
|
81 .attr("cx", function(d) { return d.x; }) |
|
82 .attr("cy", function(d) { return d.y; }) |
|
83 .attr("r", function(d) { return Math.sqrt(d.size) / 10 || 4.5; }) |
|
84 .style("fill", color) |
|
85 .on("click", click) |
|
86 .call(force.drag); |
|
87 |
|
88 // Exit any old nodes. |
|
89 node.exit().remove(); |
|
90 } |
|
91 |
|
92 function tick() { |
|
93 link.attr("x1", function(d) { return d.source.x; }) |
|
94 .attr("y1", function(d) { return d.source.y; }) |
|
95 .attr("x2", function(d) { return d.target.x; }) |
|
96 .attr("y2", function(d) { return d.target.y; }); |
|
97 |
|
98 node.attr("cx", function(d) { return d.x; }) |
|
99 .attr("cy", function(d) { return d.y; }); |
|
100 } |
|
101 |
|
102 // Color leaf nodes orange, and packages white or blue. |
|
103 function color(d) { |
|
104 return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c"; |
|
105 } |
|
106 |
|
107 // Toggle children on click. |
|
108 function click(d) { |
|
109 if (d.children) { |
|
110 d._children = d.children; |
|
111 d.children = null; |
|
112 } else { |
|
113 d.children = d._children; |
|
114 d._children = null; |
|
115 } |
|
116 update(); |
|
117 } |
|
118 |
|
119 // Returns a list of all nodes under the root. |
|
120 function flatten(root) { |
|
121 var nodes = [], i = 0; |
|
122 |
|
123 function recurse(node) { |
|
124 if (node.children) node.children.forEach(recurse); |
|
125 if (!node.id) node.id = ++i; |
|
126 nodes.push(node); |
|
127 } |
|
128 |
|
129 recurse(root); |
|
130 return nodes; |
|
131 } |
|
132 |
|
133 </script> |
|
134 </body> |
|
135 </html> |