block by emeeks 7914714

Taffy Edges

Full Screen

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
body { font-family: Hevetica; }

.node {
  stroke: #fff;
  stroke-width: 1.5px;
}

.link {
  stroke: #999;
  stroke-opacity: 0;
}

</style>
<title>Taffy Edges</title>
</head>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>

var width = 960,
    height = 500;
    
stateChange = true;
refreshGraph = 100;

  nodeSettings = {
    emperor: {number: 1, color: "red", size: 15},
    governor: {number: 0, color: "orange", size: 10},
    hierarchical3: {number: 0, color: "yellow", size: 8},
    hierarchical4: {number: 0, color: "green", size: 6},
    hierarchical5: {number: 0, color: "blue", size: 4},
    hierarchical6: {number: 0, color: "darkblue", size: 2},
    outsiderA: {number: 0, color: "lightgray", size: 8},
    outsiderB: {number: 2, color: "gray", size: 4}
  }


var color = d3.scale.category20();

var force = d3.layout.force()
    .charge(function(d) {return d.weight * -30})
    .linkDistance(40)
    .linkStrength(function(d) {return d.weight})
    .gravity(.05)
    .size([width, height]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

taffyEdge = d3.svg.line().x(function(d) {return d.x}).y(function(d) {return d.y}).interpolate("basis");

function edgePoints(sourceNode, targetNode) {
  var sourceWidth = Math.max(nodeSettings[sourceNode.type].size * 2) / 2
  var targetWidth = Math.max(nodeSettings[targetNode.type].size * 2) / 2

  var xOffset = sourceNode.x - targetNode.x;
  var yOffset = sourceNode.y - targetNode.y;
  
  if (Math.abs(xOffset) > Math.abs(yOffset)) {
    sourceWidthX = 0;
    sourceWidthY = sourceWidth;
    targetWidthX = 0;
    targetWidthY = targetWidth;
  }
  else {
    sourceWidthX = sourceWidth;
    sourceWidthY = 0;
    targetWidthX = targetWidth;
    targetWidthY = 0;    
  }
  
//  var width = 10;
  var taffyPoints = [
                     {x: sourceNode.x, y: sourceNode.y},
                     {x: sourceNode.x - sourceWidthX, y: sourceNode.y - sourceWidthY},
                     {x: (sourceNode.x + targetNode.x) / 2, y: (sourceNode.y + targetNode.y) / 2},
                     {x: targetNode.x - sourceWidthX, y: targetNode.y - sourceWidthY},
                     {x: targetNode.x, y: targetNode.y},
                     {x: targetNode.x + targetWidthX, y: targetNode.y + targetWidthY},
                     {x: (sourceNode.x + targetNode.x) / 2, y: (sourceNode.y + targetNode.y) / 2},
                     {x: sourceNode.x + targetWidthX, y: sourceNode.y + targetWidthY},
                     {x: sourceNode.x, y: sourceNode.y}
                    ]
  return taffyPoints;
}

numCommunities = 6;

function maxIndex(x) {
  var maxValue = d3.max(x);
  return x.indexOf(maxValue);
}

var graphVariable;

  graph = graphConstructor();

  graphVariable = graph;
  

    graphVariable.nodes.forEach( function (d) {
    d.nodeStrength = 1;
    d.communities = [];
    d.communityBuffer = [];
  })
    
  testCommunities();

  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();
  
  /*
  graph.nodes[0].fixed = true;
  graph.nodes[0].x = 100;
  graph.nodes[0].px = 100;
  graph.nodes[0].y = 100;
  graph.nodes[0].py = 100;

  graph.nodes[62].fixed = true;
  graph.nodes[62].x = 860;
  graph.nodes[62].px = 860;
  graph.nodes[62].y = 400;
  graph.nodes[62].py = 400;
  */
  
  force
      .linkStrength(function(d) {return (d.source.nodeStrength == 0 || d.target.nodeStrength == 0) ? 0 : 1})
      .charge(function(d) {return d.nodeStrength * -60})

      
  var link = svg.selectAll(".link")
      .data(graph.links)
    .enter().append("path")
      .attr("class", "link")
      .attr("d", function (d) {return taffyEdge(edgePoints(d.source, d.target)) + "Z"})
      .style("opacity", 0);

  var node = svg.selectAll(".node")
      .data(graph.nodes)
    .enter().append("g")
      .attr("class", "node")
      .on("click", grayOut)
      .call(force.drag);
      
      node.append("circle")
      .attr("r", 1)
      .style("fill", "gray")

        node.append("text")
      .text(function(d) { return d.label; })
      .style("stroke", "none")
      .style("font-size", "0px")
      .style("font-weight", 0)
      .attr("text-anchor", "middle")
      .style("pointer-events", "none");
      
      
      d3.selectAll("g.node").select("circle")
      .each(function(d,i) {
      d3.select(this)
      .transition()
      .delay(2000 + (i * 300))
//      .duration(function(d, i) {return i * 100})
      .attr("r", Math.max(10, nodeSettings[d.type].size * 2))
      .style("fill", nodeSettings[d.type].color)
      .transition()
      .duration(1000)
      .attr("r", nodeSettings[d.type].size)
      });
      
      d3.selectAll("g.node").select("text")
      .each(function(d,i) {
      d3.select(this)
      .transition()
      .delay(2000 + (i * 300))
      .style("font-size", function (d) {return Math.max(12, nodeSettings[d.type].size * 2) + "px"})
      .style("font-weight", 500)
      .transition()
      .duration(1000)
      .style("font-size", function (d) {return nodeSettings[d.type].size + "px"})
      .style("font-weight", 150)
      })
      
      d3.selectAll("path.link")
      .transition()
      .delay(function(d,i) {return 2000 + (i * 300)})
      .style("opacity", 1)
      .transition()
      .duration(1000)
      .style("fill", function(d) {return d.type == "horizontal" ? "lightblue" : "lightgreen"})





  force.on("tick", tick);
  
  function tick() {
    
    /*
    if(refreshGraph > 0) {
      console.log("skipped")
      refreshGraph--;
      return;
    }
    
    */
    
    force.alpha(.05);
    
    if (Math.random() < .025) {

    
      var randomPerturb = (Math.floor(Math.random() * (graphVariable.nodes.length)));
      
      graphVariable.nodes[randomPerturb].nodeStrength = Math.max(0, graphVariable.nodes[randomPerturb].nodeStrength -  (Math.random() > .50 ? -1 : 1))
      stateChange = true;
    }
    
    if (stateChange == true) {
      force.stop();

      stateChange = false;
      
      d3.selectAll("circle.node")
//      .style("fill", function(d) {return d.nodeStrength == 0 ? "gray" : color(maxIndex(d.communities))})
      .style("display", function(d) {return d.nodeStrength == 0 ? "none" : "block"})
      .attr("opacity", function(d) {return Math.max(.33, (d.nodeStrength * .1))})

      d3.selectAll("path.link")
      .style("display", function(d) {return (d.source.nodeStrength == 0 || d.target.nodeStrength == 0) ? "none" : "block"});
      
      setTimeout(function() {force.start()}, 100);
      
      refreshGraph = 100;

    }

    d3.selectAll(".link")
    .attr("d", function (d) {return taffyEdge(edgePoints(d.source, d.target)) + "Z"})
    
/*    link.attr("x1", function(d) { return d.source.x; })
        .attr("y1", function(d) { return d.source.y; })
        .attr("x2", function(d) { return d.target.x; })
        .attr("y2", function(d) { return d.target.y; });
*/

    d3.selectAll("g.node")
    .attr("transform", function(d) { return "translate("+d.x+","+d.y+")"})
  }
  
  function grayOut(d,i) {
    d.fixed = true;
    testCommunities()
//    stateChange = true;
  }


function testCommunities() {
  
    graphVariable.nodes.forEach( function(node) {
    node.communities = [];
    node.communityBuffer = [];
    for (var community = 0; community < numCommunities; community++) {
      // Initialize with a small Exponential variate
      node.communities[community] = 0.01 * -Math.log(Math.random());
      node.communityBuffer[community] = 0.0;
    }
  });

  var communitySums = [];

  for (var iteration = 0; iteration < 20; iteration++) {
    for (var community = 0; community < numCommunities; community++) {
      communitySums[community] = 0.0;
    } 

    // Estimate community memberships for each edge
    graphVariable.links.forEach( function(edge) {
//      if (edge.source.nodeStrength > 0 && edge.target.nodeStrength > 0) {
      var sourceCommunities = edge.source.communities;
      var targetCommunities = edge.target.communities;
      var distribution = [];

      // Multiply the two community membership vectors
      for (var community = 0; community < numCommunities; community++) {
        distribution[community] = sourceCommunities[community] * targetCommunities[community];
      }

      // Normalize and add to the gradient
      var normalizer = edge.weight / d3.sum(distribution);
      for (var community = 0; community < numCommunities; community++) {
        distribution[community] *= normalizer;
        communitySums[community] += distribution[community];
        edge.source.communityBuffer[community] += distribution[community];
        edge.target.communityBuffer[community] += distribution[community];
      }
//      }
    });

    // We need to divide each node value by the square root of the community sum.
    var communityNormalizers = []
    for (var community = 0; community < numCommunities; community++) {
      communityNormalizers[community] = 1.0 / Math.sqrt(communitySums[community]);
    }

    // Update parameters and clear the buffer.
    graphVariable.nodes.forEach( function(node) {
      for (var community = 0; community < numCommunities; community++) {
        node.communities[community] = node.communityBuffer[community] * communityNormalizers[community];
        node.communityBuffer[community] = 0.0;
      }
    });
  }

  d3.selectAll("circle.node")
        .style("fill", function(d) { return color(maxIndex(d.communities)); })
  
}

function graphConstructor() {
  var nodes = [], links = [];


  var verticalEdgeSettings = 
    [
    {source: "governor", target: "emperor", number: 6, numbermin: 4},
    {source: "hierarchical3", target: "governor", number: 3, numbermin: 2},
    {source: "hierarchical4", target: "hierarchical3", number: 3, numbermin: 1},
    {source: "hierarchical5", target: "hierarchical4", number: 3, numbermin: 1},
    {source: "hierarchical6", target: "hierarchical5", number: 5, numbermin: 0},
    {source: "outsiderA", target: "emperor", number: 2, numbermin: 2}
    ]

  var horizontalEdgeSettings = 
    [
    {source: "governor", target: "governor", probability: .01},
    {source: "hierarchical3", target: "hierarchical3", probability: .05},
    {source: "hierarchical4", target: "hierarchical4", probability: .025},
    {source: "hierarchical5", target: "hierarchical5", probability: .01},
    {source: "outsiderA", target: "emperor", probability: .1},
    {source: "outsiderA", target: "governor", probability: .25},
    {source: "outsiderB", target: "governor", probability: .1},
    {source: "outsiderB", target: "hierarchical3", probability: .2}
    ]
  
  for (nodeClass in nodeSettings) {
    var x = 1;
    while (x <= nodeSettings[nodeClass].number) {
      var topNode = {label: nodeClass + "-" + x, type: nodeClass}
      nodes.push(topNode);
      x++;
    }
  }
  var node = 0;
  while (node < nodes.length && node < 500) {
    for (verticalEdge in verticalEdgeSettings) {
      if (nodes[node].type == verticalEdgeSettings[verticalEdge].target) {
        var x = 1;
        var x = Math.min(verticalEdgeSettings[verticalEdge].number - verticalEdgeSettings[verticalEdge].numbermin, Math.ceil(verticalEdgeSettings[verticalEdge].number * Math.random()))
        while (x < verticalEdgeSettings[verticalEdge].number) {
          var spawnNode = {label: verticalEdgeSettings[verticalEdge].source + "-" + x, type: verticalEdgeSettings[verticalEdge].source}
          nodes.push(spawnNode);
          var newLink = {source: nodes[node], target: spawnNode, weight: 2, type: "vertical"};
          links.push(newLink);

          x++;
        }
      }
    }
    node++;
  }

  for (nodex in nodes) {
    for (nodey in nodes) {
      for (horizontalEdge in horizontalEdgeSettings) {
        if (horizontalEdgeSettings[horizontalEdge].source == nodes[nodex].type && horizontalEdgeSettings[horizontalEdge].target == nodes[nodey].type) {
          var randomChance = Math.random();
          if (randomChance < horizontalEdgeSettings[horizontalEdge].probability) {
            var newLink = {source: nodes[nodex], target: nodes[nodey], weight: 1, type: "horizontal"};
            links.push(newLink);
          }
        }
      }
      }
  }
  genNodes = nodes;
  genEdges = links;
  var returnObject = {links: genEdges, nodes: genNodes};
  return returnObject;
  
}

function populateLists() {
  var textNodes = "<h3>Node List</h3><p>id<br>";
  for (x in force.nodes()) {
    textNodes += force.nodes()[x].label;
    textNodes += "<br>";
  }  
  textNodes += "</p>"


  textNodes += "<h3>Edge List</h3><p>source,target,type<br>";
  for (x in force.links()) {
    textNodes += force.links()[x].source.label;
    textNodes += ",";
    textNodes += force.links()[x].target.label;
    textNodes += ",";
    textNodes += force.links()[x].type;
    textNodes += "<br>";
    
  }
  textNodes += "</p>"

  d3.select("#code").html(textNodes);
}




</script>

<p>Settings: <input type="button" value="Edge List" onclick="populateLists()" /></p>
<pre id="code">
nodeSettings = {
    emperor: {number: 1, color: "red", size: 15},
    hierarchical2: {number: 0, color: "orange", size: 10},
    hierarchical3: {number: 0, color: "yellow", size: 8},
    hierarchical4: {number: 0, color: "green", size: 6},
    hierarchical5: {number: 0, color: "blue", size: 4},
    hierarchical6: {number: 0, color: "darkblue", size: 2},
    outsiderA: {number: 0, color: "lightgray", size: 8},
    outsiderB: {number: 2, color: "gray", size: 4}
  }
  
var verticalEdgeSettings = 
    [
    {source: "hierarchical2", target: "emperor", number: 6, numbermin: 4},
    {source: "hierarchical3", target: "hierarchical2", number: 3, numbermin: 2},
    {source: "hierarchical4", target: "hierarchical3", number: 3, numbermin: 1},
    {source: "hierarchical5", target: "hierarchical4", number: 3, numbermin: 1},
    {source: "hierarchical6", target: "hierarchical5", number: 5, numbermin: 0},
    {source: "outsiderA", target: "emperor", number: 2, numbermin: 2}
    ]
    
horizontalEdgeSettings = 
    [
    {source: "hierarchical2", target: "hierarchical2", probability: .01},
    {source: "hierarchical3", target: "hierarchical3", probability: .05},
    {source: "hierarchical4", target: "hierarchical4", probability: .025},
    {source: "hierarchical5", target: "hierarchical5", probability: .01},
    {source: "outsiderA", target: "hierarchical1", probability: .1},
    {source: "outsiderA", target: "hierarchical2", probability: .25},
    {source: "outsiderB", target: "hierarchical2", probability: .1},
    {source: "outsiderB", target: "hierarchical3", probability: .2}
    ]
</pre>
</body>
</html>