blob: 57016c43e1d1176702bf52840b7bd878dab9e028 [file] [log] [blame]
Ubuntu82b8a832013-02-06 22:00:11 +00001function gui(data_source){
2 var width = 960,
3 height = 500;
4 var color = d3.scale.category20();
5
6 var svg = d3.select("body").append("svg:svg")
7 .attr("width", width)
8 .attr("height", height);
9
10 var force = d3.layout.force()
11 .charge(-500)
12 .linkDistance(100)
13 .size([width, height]);
14
15 var path = svg.selectAll("path");
16 var circle = svg.selectAll("circle");
17 var text = svg.selectAll("g");
Masayoshi Kobayashif63ef2f2013-02-20 21:47:21 +000018 var node_drag = d3.behavior.drag()
19 .on("dragstart", dragstart)
20 .on("drag", dragmove)
21 .on("dragend", dragend);
Ubuntu82b8a832013-02-06 22:00:11 +000022
Masayoshi Kobayashi274c07d2013-02-20 21:38:16 +000023 d3.json(data_source, init);
Ubuntu82b8a832013-02-06 22:00:11 +000024
Masayoshi Kobayashi1407a502013-02-27 06:23:08 +000025/* For debugging
Masayoshi Kobayashi274c07d2013-02-20 21:38:16 +000026 $("#more").click( function() {
27 $.ajax({
Masayoshi Kobayashi1407a502013-02-27 06:23:08 +000028 url: 'http://gui.onlab.us:8080/topology_more',
Masayoshi Kobayashi274c07d2013-02-20 21:38:16 +000029 success: function(json) {
Masayoshi Kobayashi1407a502013-02-27 06:23:08 +000030 update(json);
Masayoshi Kobayashi274c07d2013-02-20 21:38:16 +000031 },
32 dataType: "json"
33 });
34 });
35 $("#less").click( function() {
36 $.ajax({
Masayoshi Kobayashi1407a502013-02-27 06:23:08 +000037 url: 'http://gui.onlab.us:8080/topology_less',
Masayoshi Kobayashi274c07d2013-02-20 21:38:16 +000038 success: function(json) {
Masayoshi Kobayashi1407a502013-02-27 06:23:08 +000039 update(json);
Masayoshi Kobayashi274c07d2013-02-20 21:38:16 +000040 },
41 dataType: "json"
42 });
43 });
Masayoshi Kobayashi1407a502013-02-27 06:23:08 +000044*/
Masayoshi Kobayashif63ef2f2013-02-20 21:47:21 +000045
46 function compare_link (a, b){
47 if (a.source > b.source) {return 1;}
48 else if (a.source < b.source) {return -1;}
49 else {
50 if (a.target > b.target) {return 1 ;}
51 if (a.target < b.target) {return -1;}
52 else {return 0;}
53 }
54 }
55
56 function init(json){
57 nodes = force.nodes();
58 links = force.links();
59
60 json.nodes.forEach(function(item) {
61 nodes.push(item);
62 });
63 json.links.forEach(function(item) {
64 links.push(item);
65 });
66
67 links.sort(compare_link);
68 for (var i=1; i<links.length; i++) {
69 if (links[i].source == links[i-1].source &&
70 links[i].target == links[i-1].target) {
71 links[i].linknum = links[i-1].linknum + 1;
72 }
73 else {
74 links[i].linknum = 1;
75 };
76 };
77 init_draw(nodes, links);
78 }
79
80 /* Return nodes that is not in the current list of nodes */
81 Array.prototype.node_diff = function(arr) {
82 return this.filter(function(i) {
83 for (var j = 0; j < arr.length ; j++) {
84 if (arr[j].name === i.name)
85 return false;
86 }
87 return true;
88 });
89 };
90
91 /* Return removed links */
92 function gone_links (json, links, gone) {
93 for (var i = 0; i < links.length ; i ++){
94 var found = 0;
95 for (var j = 0; j < json.links.length ; j ++){
96 if (links[i].source.name == json.nodes[json.links[j].source].name &&
97 links[i].target.name == json.nodes[json.links[j].target].name ){
98 found = 1;
99 break;
100 }
101 }
102 if ( found == 0 ){
103 gone.push(links[i]);
104 }
105 }
106 return gone;
107 }
108
109 /* Return added links */
110 function added_links (json, links, added) {
111 for (var j = 0; j < json.links.length ; j ++){
112 var found = 0;
113 for (var i = 0; i < links.length ; i ++){
114 if (links[i].source.name == json.nodes[json.links[j].source].name &&
115 links[i].target.name == json.nodes[json.links[j].target].name ){
116 found = 1;
117 break;
118 }
119 }
120 if ( found == 0 ){
121 added.push(json.links[j]);
122 }
123 }
124 return added;
125 }
126
127 function dragstart(d, i) {
128 force.stop() // stops the force auto positioning before you start dragging
129 }
130
131 function dragmove(d, i) {
132 d.px += d3.event.dx;
133 d.py += d3.event.dy;
134 d.x += d3.event.dx;
135 d.y += d3.event.dy;
136 tick(); // this is the key to make it work together with updating both px,py,x,y on d !
137 }
138
139 function dragend(d, i) {
140 d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
141 tick();
142 force.resume();
143 }
144
145 /* check if toplogy has changed and update node[] and link[] accordingly */
146 function cdiff(json) {
147 var changed = false;
148
149 var n_adds = json.nodes.node_diff(nodes);
150 var n_rems = nodes.node_diff(json.nodes);
151 for (var i = 0; i < n_adds.length; i++) {
152 nodes.push(n_adds[i]);
153 changed = true;
154 }
155 for (var i = 0; i < n_rems.length; i++) {
156 for (var j = 0; j < nodes.length; j++) {
157 if ( nodes[j].name == n_rems[i].name ){
158 nodes.splice(j,1);
159 changed = true;
160 break;
161 }
162 }
163 }
164 var l_adds = [];
165 var l_rems = [];
166 l_adds = added_links(json, links, l_adds);
167 l_rems = gone_links(json, links, l_rems);
168 for (var i = 0; i < l_rems.length ; i++) {
169 for (var j = 0; j < links.length; j++) {
170 if (links[j].source.name == l_rems[i].source.name &&
171 links[j].target.name == l_rems[i].target.name) {
172 links.splice(j,1);
173 changed = true;
174 break;
175 }
176 }
177 }
178 // Sorce/target of an element of l_adds[] are corresponding to the index of json.node[]
179 // which is different from the index of node[] (new nodes are always added to the last)
180 // So update soure/target node indexes of l_add[] need to be fixed to point to the proper
181 // node in node[];
182 for (var i = 0; i < l_adds.length; i++) {
183 for (var j = 0; j < nodes.length; j++) {
184 if ( json.nodes[l_adds[i].source].name == nodes[j].name ){
185 l_adds[i].source = j;
186 break;
187 }
188 }
189 for (var j = 0; j < nodes.length; j++) {
190 if ( json.nodes[l_adds[i].target].name == nodes[j].name ){
191 l_adds[i].target = j;
192 break;
193 }
194 }
195 links.push(l_adds[i]);
196 changed = true;
197 }
198
199 // Update "group" attribute of nodes
200 for (var i = 0; i < nodes.length; i++) {
201 for (var j = 0; j < json.nodes.length; j++) {
202 if ( nodes[i].name == json.nodes[j].name ){
203 if (nodes[i].group != json.nodes[j].group){
204 nodes[i].group = json.nodes[j].group;
205 changed = true;
206 }
207 }
208 }
209 }
210 return changed
211 }
212
213 function draw(force, path, circle, text){
214 force.stop();
215 path.enter().append("svg:path")
216 .attr("class", function(d) { return "link"; });
217
218 circle.enter().append("svg:circle")
219 .attr("r", 8)
220 .call(node_drag);
221// .call(force.drag);
222
223 text.enter().append("svg:text")
224 .attr("x", 8)
225 .attr("y", ".31em")
Masayoshi Kobayashi1407a502013-02-27 06:23:08 +0000226 .text(function(d) { return d.name.split(":")[5] + d.name.split(":")[6] + d.name.split(":")[7] });
Masayoshi Kobayashif63ef2f2013-02-20 21:47:21 +0000227
228 circle.append("title")
229 .text(function(d) { return d.name; });
230
231 circle.attr("fill", function(d) {
232 if (d.group == 1){return "red";}
233 else if (d.group == 2){return "blue";}
234 else if (d.group == 3){return "green";}
Ubuntu5b2b24a2013-02-27 09:51:13 +0000235 else if (d.group == 4){return "orange";}
Masayoshi Kobayashif63ef2f2013-02-20 21:47:21 +0000236 else{ return "gray"; }
237 });
238
239 path.attr("stroke", function(d) {
240 if(d.type == 1){
241 return "red"
242 } else {
243 return "black"
244 }
245 }).attr("stroke-width", function(d) {
246 if(d.type == 1){
247 return "4px";
248 } else {
249 return "1.5px";
250 }
251 }).attr("marker-end", function(d) {
252 if(d.type == 1){
253 return "url(#TriangleRed)";
254 } else {
255 return "url(#Triangle)";
256 }
257 });
258
259
260 path.exit().remove();
261 circle.exit().remove();
262 text.exit().remove();
263
264 force.on("tick", tick);
265 force.start();
266
267 }
268
269 function update(json) {
270 var changed = cdiff(json);
271
272 console.log("changed? " + changed);
273
274 if (changed){
275
276 path = svg.selectAll("path").data(links)
277 circle = svg.selectAll("circle").data(nodes);
278 text = svg.selectAll("text").data(nodes);
279
280 draw(force, path, circle, text);
281 }
282 }
283
284 function init_draw(nodes, links){
Masayoshi Kobayashi1407a502013-02-27 06:23:08 +0000285 path = svg.append("svg:g").selectAll("path").data(links);
Masayoshi Kobayashif63ef2f2013-02-20 21:47:21 +0000286 circle = svg.append("svg:g").selectAll("circle").data(nodes);
287 text = svg.append("svg:g").selectAll("text").data(nodes);
288
289 draw(force, path, circle, text);
290
291 setInterval(function() {
292 $.ajax({
Masayoshi Kobayashif63ef2f2013-02-20 21:47:21 +0000293 url: data_source,
294 success: function(json) {
295 update(json)
296 },
297 dataType: "json"
298 });
299 }, 3000);
300 }
301
Masayoshi Kobayashi274c07d2013-02-20 21:38:16 +0000302
Ubuntu82b8a832013-02-06 22:00:11 +0000303 function tick() {
304 path.attr("d", function(d) {
305 var dx = d.target.x - d.source.x,
306 dy = d.target.y - d.source.y,
307 dr = 1/d.linknum; //linknum is defined above
Masayoshi Kobayashif63ef2f2013-02-20 21:47:21 +0000308 dr = 0; // 0 for direct line
Ubuntu82b8a832013-02-06 22:00:11 +0000309 return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
310 });
311// circle.attr("cx", function(d) { return d.x; }).attr("cy", function(d) { return d.y; });
312 circle.attr("transform", function(d) {
313 return "translate(" + d.x + "," + d.y + ")";
314 })
Ubuntuaea2a682013-02-08 08:30:10 +0000315 circle.attr("fill", function(d) {
316 if (d.group == 1){return "red";}
317 else if (d.group == 2){return "blue";}
318 else if (d.group == 3){return "green";}
Ubuntu5b2b24a2013-02-27 09:51:13 +0000319 else if (d.group == 3){return "orange";}
Ubuntuaea2a682013-02-08 08:30:10 +0000320 else{ return "gray"; }
321 });
Ubuntu82b8a832013-02-06 22:00:11 +0000322// text.attr("x", function(d) { return d.x; }).attr("y", function(d) { return d.y; });
Ubuntu82b8a832013-02-06 22:00:11 +0000323 text.attr("transform", function(d) {
324 return "translate(" + d.x + "," + d.y + ")";
325 });
326 }
327}
Masayoshi Kobayashif63ef2f2013-02-20 21:47:21 +0000328