block by enjalot c24ebf736802fb37707f

force targets

Full Screen

Drag the white circles to move targets used by the force layout.

In this example the nodes are each assigned a target that they will cluster to. One could have the targets be different categories arranged in some semanticly meaningful way.

Built with blockbuilder.org

index.html

<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <style>
      body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; 
        background-color: #111;
      }
      svg { width: 100%; height: 100%; }
    </style>
  </head>

  <body>
    <script>
      var width = 960
      var height = 500
      var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height)

      var targets = [
        {"x":200,"y":200},{"x":663,"y":168},
        {"x":417,"y":307},{"x":221,"y":307},
        {"x":443,"y":192}
      ]
      function rand(t,i) {
        var d = { 
          x: t.x + (Math.random()-0.5) * 600, 
          y: t.y + (Math.random()-0.5) * 300,
          i: i
        }
        return d;
      }
      var nodes = [];
      targets.forEach(function(t,i) { 
        console.log("I", i)
        var d = rand(t,i);
        nodes.push(d);
        d = rand(t,i);nodes.push(d);
        d = rand(t,i);nodes.push(d);
        d = rand(t,i);nodes.push(d);
        d = rand(t,i);nodes.push(d);
      })
      console.log("NODES", nodes)
      ////bl.ocks.org/mbostock/1021841
      var force = d3.layout.force()
        .size([width, height])
        .gravity(0.05)
        .friction(0.95)
      	.charge(-50)
        .nodes(nodes)

      var targetCircles = svg.selectAll("circle.target")
        .data(targets)
      
      targetCircles.enter()
        .append("circle")
        .classed("target", true)
      
      targetCircles.attr({
        cx: function(d) { return d.x },
        cy: function(d) { return d.y },
        r: 9,
        fill: "#efefef"
      })
      
      var drag = d3.behavior.drag()
        .on("drag", function(d) {
          force.resume();
          d3.select(this).attr({
            cx: function(d) { d.x = d3.event.x; return d.x },
            cy: function(d) { d.y = d3.event.y; return d.y },
          })
        })
      	.on("dragend", function(d) {
          console.log(JSON.stringify(targets))
        })
      targetCircles.call(drag)
      

      var fcircles = svg.selectAll("circle.force")
        .data(nodes)
      fcircles.enter()
        .append("circle")
      	.classed("force", true)
      fcircles.attr({
        r: 3,
        "fill-opacity": 0.3,
        stroke: "#efefef"
      })
      .call(force.drag)
      
      force.on("tick", function(e) {
        
        var k = 0.16 * e.alpha
        nodes.forEach(function(t,i) {
          t.x += (-t.x + targets[t.i].x) * k;
      		t.y += (-t.y + targets[t.i].y) * k;
        })
        fcircles.attr({
          cx: function(d) { return d.x },
          cy: function(d) { return d.y }
        })
      })
      force.start();
    </script>
  </body>