github.com/cayleygraph/cayley@v0.7.7/static/js/query_viz.js (about) 1 // Copyright 2014 The Cayley Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 function Network() { 16 var height = 800; 17 var width = 800; 18 19 // Things in the first force graph. 20 var nodesG = null; 21 var linksG = null; 22 var linkNodesG = null; 23 24 var node = null; 25 var linknode = null; 26 var link = null; 27 28 var curLinksData = null; 29 var curNodesData = null; 30 31 32 // Things in the second force graph 33 var tag_nodesG = null; 34 var tag_linksG = null; 35 36 var tag_node = null; 37 var tag_link = null; 38 39 var curTagLinksData = null; 40 var curTagNodesData = null; 41 42 43 var allData = null; 44 45 var dragstart = function(d) { 46 d3.select(this).classed("fixed", d.fixed = true); 47 } 48 49 //our force directed layout 50 var force = d3.layout.force() 51 var force_drag = force.drag().on("dragstart", dragstart) 52 var tag_force = d3.layout.force() 53 54 //color function used to color nodes 55 var baseNodeColors = d3.scale.category20() 56 var linkNodeColors = function(d) { 57 return d3.rgb("#555").toString() 58 } 59 var strokeFor = function(d) { 60 return d3.rgb("#000").brighter().toString() 61 } 62 63 var rotationTransformForLinkNode = function (d) { 64 if (!d.link) { 65 return null 66 } 67 var center_x = (d.link.source.x + d.link.target.x) / 2 68 var center_y = (d.link.source.y + d.link.target.y) / 2 69 var dx = d.link.target.x - d.link.source.x 70 var dy = d.link.target.y - d.link.source.y 71 var rotation_radians = Math.atan2(dy,dx) 72 var rotation_degrees = rotation_radians * (180 / Math.PI) 73 return "rotate(" + rotation_degrees + ", " + center_x + ", " + center_y + ")" 74 } 75 76 var updateLink = function() { 77 this.attr("x1", function(d) {return d.source.x}) 78 .attr("y1", function(d) {return d.source.y}) 79 .attr("x2", function(d) {return d.target.x}) 80 .attr("y2", function(d) {return d.target.y}) 81 } 82 83 var forceTick = function(e) { 84 tag_force.start() 85 node 86 .attr("cx", function(d) {return d.x}) 87 .attr("cy", function(d) {return d.y}) 88 89 linknode.each(function(d) { 90 if (d.link) { 91 d.x = (d.link.source.x + d.link.target.x) / 2 92 d.y = (d.link.source.y + d.link.target.y) / 2 93 } 94 }) 95 96 linknode 97 .attr("cx", function(d) {return d.x }) 98 .attr("cy", function(d) {return d.y }) 99 .attr("transform", rotationTransformForLinkNode) 100 101 if (link) { 102 link.call(updateLink); 103 } 104 105 tagForceTick(e) 106 } 107 108 var tagForceTick = function(e) { 109 tag_node.each(function(d) { 110 if(d.is_tag === false) { 111 d.x = d.node.x; 112 d.y = d.node.y; 113 } else { 114 var b = this.childNodes[1].getBBox(); 115 var diffX = d.x - d.node.x; 116 var diffY = d.y - d.node.y; 117 118 var dist = Math.sqrt(diffX * diffX + diffY * diffY); 119 120 var shiftX = b.width * (diffX - dist) / (dist * 2); 121 shiftX = Math.max(-b.width, Math.min(0, shiftX)); 122 var shiftY = 5; 123 this.childNodes[1].setAttribute("transform", "translate(" + shiftX + "," + shiftY + ")"); 124 } 125 }); 126 tag_node 127 .attr("transform", function(d) { 128 return "translate(" + d.x + "," + d.y + ")"; 129 }); 130 131 tag_link.call(updateLink); 132 } 133 134 var setupData = function (data) { 135 data.nodes.forEach(function (n) { 136 n.x = randomnumber=Math.floor(Math.random()*width) 137 n.y = randomnumber=Math.floor(Math.random()*height) 138 n.radius = 10 139 }); 140 141 var nodesMap = mapNodes(data.nodes) 142 143 if (data.links) { 144 data.links.forEach(function (l) { 145 l.source = nodesMap.get(l.source) 146 l.target = nodesMap.get(l.target) 147 nodesMap.get(l.link_node).link = l 148 }) 149 } 150 151 data.tag_links = [] 152 data.tag_nodes = [] 153 var tag_id_counter = 0 154 155 data.nodes.forEach(function (n) { 156 if (n.tags !== undefined) { 157 n.tags.forEach( function (tag) { 158 var tag_node = {} 159 tag_node.id = "tag" + tag_id_counter 160 tag_node.tag = tag 161 tag_node.x = n.x 162 tag_node.y = n.y 163 tag_node.is_tag = true 164 tag_node.is_value = false 165 tag_node.node = n 166 tag_node.radius = 0 167 tag_id_counter += 1; 168 var fake_node = {} 169 fake_node.id = "tag" + tag_id_counter 170 fake_node.x = n.x 171 fake_node.y = n.y 172 fake_node.node = n 173 fake_node.radius = 0 174 fake_node.is_tag = false 175 tag_id_counter += 1; 176 var tag_link = {} 177 tag_link.source = fake_node 178 tag_link.target = tag_node 179 data.tag_nodes.push(tag_node) 180 data.tag_nodes.push(fake_node) 181 data.tag_links.push(tag_link) 182 }) 183 } 184 if (n.values !== undefined) { 185 n.values.forEach( function (value) { 186 var tag_node = {} 187 tag_node.id = "tag" + tag_id_counter 188 tag_node.tag = value 189 tag_node.x = n.x 190 tag_node.y = n.y 191 tag_node.is_tag = true 192 tag_node.is_value = true 193 tag_node.node = n 194 tag_node.radius = 0 195 tag_id_counter += 1; 196 var fake_node = {} 197 fake_node.id = "tag" + tag_id_counter 198 fake_node.x = n.x 199 fake_node.y = n.y 200 fake_node.node = n 201 fake_node.radius = 0 202 fake_node.is_tag = false 203 tag_id_counter += 1; 204 var tag_link = {} 205 tag_link.source = fake_node 206 tag_link.target = tag_node 207 data.tag_nodes.push(tag_node) 208 data.tag_nodes.push(fake_node) 209 data.tag_links.push(tag_link) 210 }) 211 } 212 }) 213 214 return data; 215 } 216 217 var mapNodes = function (nodes) { 218 var nodesMap = d3.map() 219 nodes.forEach(function (n) { 220 nodesMap.set(n.id, n) 221 }) 222 return nodesMap 223 } 224 225 var network = function (selection, data) { 226 allData = setupData(data) 227 var vis = d3.select(selection).append("svg") 228 .attr("width", width) 229 .attr("height", height) 230 231 vis.append("defs").append("marker") 232 .attr("id", "arrowhead") 233 .attr("refX", 6 + 3) // shift? 234 .attr("refY", 2) 235 .attr("markerWidth", 6) 236 .attr("markerHeight", 4) 237 .attr("orient", "auto") 238 .append("path") 239 .attr("d", "M 0,0 V 4 L6,2 Z"); 240 241 linksG = vis.append("g").attr("id", "links") 242 nodesG = vis.append("g").attr("id", "nodes") 243 linkNodesG = vis.append("g").attr("id", "link-nodes") 244 245 tagNodesG = vis.append("g").attr("id", "tag_nodes") 246 tagLinksG = vis.append("g").attr("id", "tag_links") 247 248 force.size([width, height]) 249 force.on("tick", forceTick) 250 .charge(-200) 251 .linkDistance(100); 252 253 tag_force 254 .gravity(0) 255 .linkDistance(25) 256 .linkStrength(20) 257 .charge(-100) 258 .size([width, height]) 259 260 tag_force.start() 261 262 //perform rendering and start force layout 263 update() 264 265 } 266 267 268 269 var update = function () { 270 allNodes = allData.nodes; 271 curLinksData = allData.links; 272 curNodesData = $.grep(allData.nodes, function(n) { return n.is_link_node === false; }) 273 curLinkNodesData = $.grep(allData.nodes, function(n) { return n.is_link_node; }) 274 275 curTagLinksData = allData.tag_links 276 curTagNodesData = allData.tag_nodes 277 278 force.nodes(allNodes); 279 node = nodesG.selectAll("circle.node") 280 .data(curNodesData, function(d) {return d.id}) 281 282 node.enter().append("circle") 283 .attr("class", "node") 284 .attr("cx", function(d) {return d.x}) 285 .attr("cy", function(d) {return d.y}) 286 .attr("r", function(d) {return d.radius;}) 287 .style("fill", function(d) {return baseNodeColors(d.id);}) 288 .style("stroke", function(d) {return strokeFor(d);}) 289 .style("stroke-width", 1.0) 290 .call(force_drag) 291 292 node.exit().remove(); 293 294 linknode = linkNodesG.selectAll("ellipse.node") 295 .data(curLinkNodesData, function(d) { return d.id }) 296 297 linknode.enter().append("ellipse") 298 .attr("class", "node") 299 .attr("cx", function(d) {return d.x}) 300 .attr("cy", function(d) {return d.y}) 301 .attr("rx", function(d) {return d.radius;}) 302 .attr("ry", function(d) {return d.radius / 2;}) 303 .style("fill", function(d) {return linkNodeColors(d.id);}) 304 .style("stroke", function(d) {return strokeFor(d);}) 305 .style("stroke-width", 1.0); 306 307 linknode.exit().remove(); 308 309 310 if (curLinksData) { 311 force.links(curLinksData); 312 313 link = linksG.selectAll("line.link") 314 .data(curLinksData, function(d) { return d.source.id + "_" + d.target.id}); 315 316 link.enter().append("line") 317 .attr("class", "link") 318 .attr("stroke", "#222") 319 .attr("stroke-opacity", 1.0) 320 .attr("marker-end", "url(#arrowhead)") 321 .style("stroke-width", 2.0) 322 .attr("x1", function(d) {return d.source.x}) 323 .attr("y1", function(d) {return d.source.y}) 324 .attr("x2", function(d) {return d.target.x}) 325 .attr("y2", function(d) {return d.target.y}); 326 327 link.exit().remove(); 328 } 329 330 force.start(); 331 332 tag_force.nodes(curTagNodesData); 333 tag_node = nodesG.selectAll("g.tag_node") 334 .data(curTagNodesData, function(d) { return d.id }) 335 var tag_g = tag_node.enter().append("g").attr("class", "tag_node") 336 tag_g.append("svg:circle").attr("r", 0).style("fill", "#FFF"); 337 tag_g.append("svg:text") 338 .text(function(d) { return d.is_tag ? d.tag : "" }) 339 .style("fill", "#555") 340 .style("font-family", function(d) { return d.is_value ? "Courier" : "Arial"}) 341 .style("font-size", 12); 342 343 tag_force.links(curTagLinksData); 344 tag_link = linksG.selectAll("line.tag_link") 345 .data(curTagLinksData, function(d) { return d.source.id + "_" + d.target.id}); 346 347 tag_link.enter().append("line") 348 .attr("class", "tag_link") 349 .attr("stroke", "#ddd") 350 .attr("stroke-opacity", 0.5) 351 .attr("x1", function(d) {return d.source.x}) 352 .attr("y1", function(d) {return d.source.y}) 353 .attr("x2", function(d) {return d.target.x}) 354 .attr("y2", function(d) {return d.target.y}); 355 356 tag_link.exit().remove(); 357 tag_force.start() 358 } 359 360 return network 361 }; 362