1

I have a force directed graph generated via D3 that isn't playing well with the responsive code or the bounding box code I've found. Since the radius of all of my circles varies, I think it's throwing some things off... Any help is appreciated!

I have to use a custom length on the lines because the nodes run into each other if I don't manually space them out because the radii aren't the same.

(Please don't link me to the d3 page with the code, I've tried it, but maybe I'm placing it in the wrong spot if you think it would work on this. I also tried to post an image, but I don't have enough reputation.)

var width = 876,
    height = 600;

var color = d3.scale.category20();

var force = d3.layout.force()
    .charge(-1010)
    .linkDistance(function(d) { return  d.distance; })
    .size([width, height])
    .gravity(0.7);
    
        
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);
    
var tip = d3.tip()
    .attr('class', 'd3-tip')
    .offset([-5, 0])
    .html(function (d) {
    return  d.name + "  (" + d.instances + ")";
})
    svg.call(tip);

d3.json("datawords.json", function(error, graph) {
  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();

  var link = svg.selectAll(".link")
      .data(graph.links)
    .enter().append("line")
      .attr("class", "link")
      .attr("width", function(d) { return d.totalLength; })
      .style("stroke-width", function(d) { return Math.sqrt(d.value); });

  var node = svg.selectAll(".node")
      .data(graph.nodes)
.enter().append("circle")
      .attr("class", "node")
      .attr("r", function(d) {return d.instances;})
      .style("fill", function(d) { return color(d.instances); })
      .call(force.drag)
    .on('mouseover', tip.show)
    .on('mouseout', tip.hide)
    .on('click', connectedNodes)


  force.on("tick", function() {
    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; });
      
    node.attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });

    node.each(collide(0.5))
    });

    
    //Toggle stores whether the highlighting is on
var toggle = 0;
//Create an array logging what is connected to what
var linkedByIndex = {};
for (i = 0; i < graph.nodes.length; i++) {
    linkedByIndex[i + "," + i] = 1;
};
graph.links.forEach(function (d) {
    linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
//This function looks up whether a pair are neighbours
function neighboring(a, b) {
    return linkedByIndex[a.index + "," + b.index];
}
function connectedNodes() {
    if (toggle == 0) {
        //Reduce the opacity of all but the neighbouring nodes
        d = d3.select(this).node().__data__;
        node.style("opacity", function (o) {
            return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
        });
        link.style("opacity", function (o) {
            return d.index==o.source.index | d.index==o.target.index ? 1 : 0.1;
        });
        //Reduce the op
        toggle = 1;
    } else {
        //Put them back to opacity=1
        node.style("opacity", 1);
        link.style("opacity", 1);
        toggle = 0;
    };
};
    
    var padding = 10, // separation between circles
    radius=15;

    function collide(alpha) {
  var quadtree = d3.geom.quadtree(graph.nodes);
  return function(d) {
    var rb = 4*radius + padding,
        nx1 = d.x - rb,
        nx2 = d.x + rb,
        ny1 = d.y - rb,
        ny2 = d.y + rb;
    
    quadtree.visit(function(quad, x1, y1, x2, y2) {
      if (quad.point && (quad.point !== d)) {
        var x = d.x - quad.point.x,
            y = d.y - quad.point.y,
            l = Math.sqrt(x * x + y * y);
          if (l < rb) {
          l = (l - rb) / l * alpha;
          d.x -= x *= l;
          d.y -= y *= l;
          quad.point.x += x;
          quad.point.y += y;
        }
      }
      return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
    });
  };
};
    window.addEventListener('resize', resize); 

function resize() {
    width = window.innerWidth, height = window.innerHeight;
    svg.attr("width", width).attr("height", height);
    force.size([width, height]).resume();
}
});

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

.link {
  stroke: #999;
  stroke-opacity: .6;
}
    
    .node-active{
  stroke: #555;
  stroke-width: 1.5px;
}
    
        .node:hover{
  stroke: #555;
  stroke-width: 1.5px;
}
    marker {
display:none;
    }
    
    .d3-tip {
  line-height: 1;
  font-weight: bold;
  padding: 12px;
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  border-radius: 2px;
}

.d3-tip.n:after {
  margin: -1px 0 0 0;
  top: 200%;
  left: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="d3/d3tip.js"></script>
    <div class="graph"></div>
</body>
Jess
  • 21
  • 1
  • 5
  • Here is an example of a force layout that makes all nodes stay within a bounding box: http://bl.ocks.org/mbostock/1129492 – Lars Kotthoff May 05 '15 at 16:27
  • You just did linked to what I said I had already looked at, and tried in my code. Where would you place that in the code to make it work? Maybe I'm placing it in the wrong spot. – Jess May 05 '15 at 16:37
  • 1
    Sorry, but I can't know what you had a look at if you don't say what you had a look at. I'm not sure what you mean by "placing the code" -- it's a complete example and all you need to do it take the 2 lines that set the node positions. – Lars Kotthoff May 05 '15 at 16:49

0 Answers0