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