title: D3.js add gravity
body: <p>I am new to D3 and am trying trying to modify this example to make a relatively stationary directed graph: <a href="http://bl.ocks.org/rkirsling/5001347" rel="nofollow">http://bl.ocks.org/rkirsling/5001347</a></p>
  
  <p>Right now I have it so that each of my nodes have been assigned a position and they are set to fixed. This generally works but the nodes are stuck and inanimate. What I would like is for the positions the nodes are assigned to to be their position but if I drag one of the nodes away it moves back to its position, like the gravity in this example: <a href="http://bl.ocks.org/mbostock/1804919" rel="nofollow">http://bl.ocks.org/mbostock/1804919</a></p>
  
  <p>How can I modify the code below to include this?</p>
  
  <pre><code>function start(element, numNodes, numEdges) {
              // set up SVG for D3
              var width = 960,
                  height = 500,
                  colors = d3.scale.category10();
              var svg = d3.select("#visrep").append('svg').attr('oncontextmenu', 'return false;').attr('width', width).attr('height', height);
              //var force = d3.layout.force().gravity(0.05).distance(100).charge(-100).size([width, height]);
              //var force = cola.d3adaptor().avoidOverlaps(true).linkDistance(30).size([width, height]).nodes(nodes).links(links).size([width, height]);
              // set up initial nodes and links
              //  - nodes are known by 'id', not by index in array.
              //  - reflexive edges are indicated on the node (as a bold black circle).
              //  - links are always source &lt; target; edge directions are set by 'left' and 'right'.
              var nodes, links;
              var nodes = [
                  { id: 0, reflexive: false },
                  { id: 1, reflexive: true },
                  { id: 2, reflexive: false }
              ],
                lastNodeId = 2;
              nodes = [];
              var count = 0, lineLimit = 10;
              for (var i = 0; i &lt; numNodes; i++) {
                  if (nod[i].nodeID &lt; lineLimit) count++;
              }
              var L = 50, r = 12;
              var d = 2 * r + L;
              var R = (count - 1) * d;
              var m = width / 2;
              var X;
              //alert("L: " + L + " r: " + r + " d: " + d + " R: " + R + " m: " + m);
              for (var i = 0; i &lt; numNodes; i++) {
  
                  if (nod[i].nodeID &lt;= lineLimit) {
                      X = m - (R / 2) + (i * d);
                     // alert("doing node: " + nod[i].nodeID + "   at =&gt; " + X);
                      nodes.push({ x: X, y: (height)/3, id: nod[i].nodeID, reflexive: true });
                  }
                  else {
                      X = m + (R / 2) - ((i-count) * d);
                     // alert("doing node: " + nod[i].nodeID + " at =&gt; " + X);
                      nodes.push({ x: X, y: (height * 2) / 3, id: nod[i].nodeID, reflexive: true });
                  }
              }
  
              lastNodeId = numNodes - 1;
              var links = [
                  { source: nodes[0], target: nodes[1], left: false, right: true },
                  { source: nodes[1], target: nodes[2], left: false, right: true }
              ]
              links = [];
              for (var i = 0; i &lt; numEdges; ++i) {
                  //d3.select(element).append("h3").text("Source: " + nodes[edges[i].source - 1].id + " Target: " + nodes[edges[i].target - 1].id);
                  links.push({ source: nodes[edges[i].source - 1], target: nodes[edges[i].target - 1], left: false, right: true });
              }
  
              // init D3 force layout
              var force = d3.layout.force()
                  .nodes(nodes)
                  .links(links)
                  .size([width, height])
                  .linkDistance(150)
                  .charge(-500)
                  .on('tick', tick);
              //var force = cola.d3adaptor().avoidOverlaps(true).size([width, height]).nodes(nodes).links(links).size([width, height]).on('tick', tick);
  
              // define arrow markers for graph links
              svg.append('svg:defs').append('svg:marker')
                  .attr('id', 'end-arrow')
                  .attr('viewBox', '0 -5 10 10')
                  .attr('refX', 6)
                  .attr('markerWidth', 3)
                  .attr('markerHeight', 3)
                  .attr('orient', 'auto')
                .append('svg:path')
                  .attr('d', 'M0,-5L10,0L0,5')
                  .attr('fill', '#000');
  
              svg.append('svg:defs').append('svg:marker')
                  .attr('id', 'start-arrow')
                  .attr('viewBox', '0 -5 10 10')
                  .attr('refX', 4)
                  .attr('markerWidth', 3)
                  .attr('markerHeight', 3)
                  .attr('orient', 'auto')
                .append('svg:path')
                  .attr('d', 'M10,-5L0,0L10,5')
                  .attr('fill', '#000');
  
              // line displayed when dragging new nodes
              var drag_line = svg.append('svg:path')
                .attr('class', 'link dragline hidden')
                .attr('d', 'M0,0L0,0');
  
              // handles to link and node element groups
              var path = svg.append('svg:g').selectAll('path'),
                  circle = svg.append('svg:g').selectAll('g');
  
              // mouse event vars
              var selected_node = null,
                  selected_link = null,
                  mousedown_link = null,
                  mousedown_node = null,
                  mouseup_node = null;
  
              function resetMouseVars() {
                  mousedown_node = null;
                  mouseup_node = null;
                  mousedown_link = null;
              }
  
              var n = nodes.length; nodes.forEach(function (d, i) {
                  if (d.Category == "Chip Life Cycle") {
                      //d.x = (width / (n * i)) + 10;
                      //d.y = height * 0.75;
                      d.fixed = true;
                  }
                  else if (d.Category == "Abstraction") {
                      //d.x = (width / (n * i)) + 10;
                      //d.y = height * 0.75;
                      d.fixed = true;
                  }
                  else if (d.Category == "Properties") {
                      //d.x = (width / (n * i)) + 10;
                      //d.y = height * 0.4;
                      d.fixed = true;
                  }
                  else {
                      //d.x = (width / (n * i)) + 10;
                      //d.y = height * 0.4;
                      d.fixed = true;
                  }
                  ++i;
              });
              // update force layout (called automatically each iteration)
              function tick() {
                  // draw directed edges with proper padding from node centers
                  path.attr('d', function (d) {
                      var deltaX = d.target.x - d.source.x,
                          deltaY = d.target.y - d.source.y,
                          dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
                          normX = deltaX / dist,
                          normY = deltaY / dist,
                          sourcePadding = d.left ? 17 : 12,
                          targetPadding = d.right ? 17 : 12,
                          sourceX = d.source.x + (sourcePadding * normX),
                          sourceY = d.source.y + (sourcePadding * normY),
                          targetX = d.target.x - (targetPadding * normX),
                          targetY = d.target.y - (targetPadding * normY);
                      return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY;
                  });
  
                  circle.attr('transform', function (d) {
                      return 'translate(' + d.x + ',' + d.y + ')';
                  });
              }
  
              // update graph (called when needed)
              function restart() {
                  // path (link) group
                  path = path.data(links);
  
                  // update existing links
                  path.classed('selected', function (d) { return d === selected_link; })
                    .style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
                    .style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; });
  
  
                  // add new links
                  path.enter().append('svg:path')
                    .attr('class', 'link')
                    .classed('selected', function (d) { return d === selected_link; })
                    .style('marker-start', function (d) { return d.left ? 'url(#start-arrow)' : ''; })
                    .style('marker-end', function (d) { return d.right ? 'url(#end-arrow)' : ''; })
                    .on('mousedown', function (d) {
                        if (d3.event.ctrlKey) return;
  
                        // select link
                        mousedown_link = d;
                        if (mousedown_link === selected_link) selected_link = null;
                        else selected_link = mousedown_link;
                        selected_node = null;
                        restart();
                    });
  
                  // remove old links
                  path.exit().remove();
  
  
                  // circle (node) group
                  // NB: the function arg is crucial here! nodes are known by id, not by index!
                  circle = circle.data(nodes, function (d) { return d.id; });
  
                  // update existing nodes (reflexive &amp; selected visual states)
                  circle.selectAll('circle')
                    .style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
                    .classed('reflexive', function (d) { return d.reflexive; });
  
                  // add new nodes
                  var g = circle.enter().append('svg:g');
  
                  g.append('svg:circle')
                    .attr('class', 'node')
                    .attr('r', 12)
                    .style('fill', function (d) { return (d === selected_node) ? d3.rgb(colors(d.id)).brighter().toString() : colors(d.id); })
                    .style('stroke', function (d) { return d3.rgb(colors(d.id)).darker().toString(); })
                    .classed('reflexive', function (d) { return d.reflexive; })
                    .on('mouseover', function (d) {
                        if (!mousedown_node || d === mousedown_node) return;
                        // enlarge target node
                        d3.select(this).attr('transform', 'scale(1.1)');
                    })
                    .on('mouseout', function (d) {
                        if (!mousedown_node || d === mousedown_node) return;
                        // unenlarge target node
                        d3.select(this).attr('transform', '');
                    })
                    .on('mousedown', function (d) {
                        if (d3.event.ctrlKey) return;
  
                        // select node
                        mousedown_node = d;
                        if (mousedown_node === selected_node) selected_node = null;
                        else selected_node = mousedown_node;
                        selected_link = null;
  
                        // reposition drag line
                        drag_line
                          .style('marker-end', 'url(#end-arrow)')
                          .classed('hidden', false)
                          .attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + mousedown_node.x + ',' + mousedown_node.y);
  
                        restart();
                    })
                    .on('mouseup', function (d) {
                        if (!mousedown_node) return;
  
                        // needed by FF
                        drag_line
                          .classed('hidden', true)
                          .style('marker-end', '');
  
                        // check for drag-to-self
                        mouseup_node = d;
                        if (mouseup_node === mousedown_node) { resetMouseVars(); return; }
  
                        // unenlarge target node
                        d3.select(this).attr('transform', '');
  
                        // add link to graph (update if exists)
                        // NB: links are strictly source &lt; target; arrows separately specified by booleans
                        var source, target, direction;
                        if (mousedown_node.id &lt; mouseup_node.id) {
                            source = mousedown_node;
                            target = mouseup_node;
                            direction = 'right';
                        } else {
                            source = mouseup_node;
                            target = mousedown_node;
                            direction = 'left';
                        }
  
                        var link;
                        link = links.filter(function (l) {
                            return (l.source === source &amp;&amp; l.target === target);
                        })[0];
  
                        if (link) {
                            link[direction] = true;
                        } else {
                            link = { source: source, target: target, left: false, right: false };
                            link[direction] = true;
                            links.push(link);
                        }
  
                        // select new link
                        selected_link = link;
                        selected_node = null;
                        restart();
                    });
  
                  // show node IDs
                  g.append('svg:text')
                      .attr('x', 0)
                      .attr('y', 4)
                      .attr('class', 'id')
                      .text(function (d) { return d.id; });
  
                  // remove old nodes
                  circle.exit().remove();
  
                  // set the graph in motion
                  force.start();
              }
  
              function mousedown() {
                  // prevent I-bar on drag
                  //d3.event.preventDefault();
  
                  // because :active only works in WebKit?
                  svg.classed('active', true);
  
                  if (d3.event.ctrlKey || mousedown_node || mousedown_link) return;
  
                  // insert new node at point
                  var point = d3.mouse(this),
                      node = { id: ++lastNodeId, reflexive: false };
                  node.x = point[0];
                  node.y = point[1];
                  nodes.push(node);
  
                  restart();
              }
  
              function mousemove() {
                  if (!mousedown_node) return;
  
                  // update drag line
                  drag_line.attr('d', 'M' + mousedown_node.x + ',' + mousedown_node.y + 'L' + d3.mouse(this)[0] + ',' + d3.mouse(this)[1]);
  
                  restart();
              }
  
              function mouseup() {
                  if (mousedown_node) {
                      // hide drag line
                      drag_line
                        .classed('hidden', true)
                        .style('marker-end', '');
                  }
  
                  // because :active only works in WebKit?
                  svg.classed('active', false);
  
                  // clear mouse event vars
                  resetMouseVars();
              }
  
              function spliceLinksForNode(node) {
                  var toSplice = links.filter(function (l) {
                      return (l.source === node || l.target === node);
                  });
                  toSplice.map(function (l) {
                      links.splice(links.indexOf(l), 1);
                  });
              }
  
              // only respond once per keydown
              var lastKeyDown = -1;
  
              function keydown() {
                  d3.event.preventDefault();
  
                  if (lastKeyDown !== -1) return;
                  lastKeyDown = d3.event.keyCode;
  
                  // ctrl
                  if (d3.event.keyCode === 17) {
                      circle.call(force.drag);
                      svg.classed('ctrl', true);
                  }
  
                  if (!selected_node &amp;&amp; !selected_link) return;
                  switch (d3.event.keyCode) {
                      case 8: // backspace
                      case 46: // delete
                          if (selected_node) {
                              nodes.splice(nodes.indexOf(selected_node), 1);
                              spliceLinksForNode(selected_node);
                          } else if (selected_link) {
                              links.splice(links.indexOf(selected_link), 1);
                          }
                          selected_link = null;
                          selected_node = null;
                          restart();
                          break;
                      case 66: // B
                          if (selected_link) {
                              // set link direction to both left and right
                              selected_link.left = true;
                              selected_link.right = true;
                          }
                          restart();
                          break;
                      case 76: // L
                          if (selected_link) {
                              // set link direction to left only
                              selected_link.left = true;
                              selected_link.right = false;
                          }
                          restart();
                          break;
                      case 82: // R
                          if (selected_node) {
                              // toggle node reflexivity
                              selected_node.reflexive = !selected_node.reflexive;
                          } else if (selected_link) {
                              // set link direction to right only
                              selected_link.left = false;
                              selected_link.right = true;
                          }
                          restart();
                          break;
                  }
              }
  
              function keyup() {
                  lastKeyDown = -1;
  
                  // ctrl
                  if (d3.event.keyCode === 17) {
                      circle
                        .on('mousedown.drag', null)
                        .on('touchstart.drag', null);
                      svg.classed('ctrl', false);
                  }
              }
  
              // app starts here
              svg.on('mousedown', mousedown)
                .on('mousemove', mousemove)
                .on('mouseup', mouseup);
              d3.select(window)
                .on('keydown', keydown)
                .on('keyup', keyup);
              restart();
          }
  
          var nod;
          var edges;
          function visualize(element, numEdges, numNodes) {
              //start();
              //graph = new Visualizer(element);
              for (var i = 0; i &lt; numEdges ; ++i) {
                  //graph.addLink(edges[i].source, nodeEdge[i].target);
                  d3.select(element).append("h3").text("Source: "+ edges[i].source + " Target: " + edges[i].target);
              }
              for (var i = 0; i &lt; numNodes; ++i) {
                  //graph.addNode(node[i].nodeID, nodes[i].Category);
                  d3.select(element).append("h3").text("Node: " + nod[i].nodeID + " Category: " + nod[i].Category);
              }
              start(element, numNodes, numEdges);
          //    //graph.start();
          }
  </code></pre>
  
  <p><strong>Edit</strong></p>
  
  <p>As requested, the HTML though it is fairly boring. My C# passes to the visualize function the div element "#visrep" in the 'element' parameter.</p>
  
  <pre><code>&lt;asp:Content ID="DescriptionContent" ContentPlaceHolderID="MainContent" runat="server"&gt;
      &lt;script type="text/javascript" src="Scripts/d3.js"&gt;&lt;/script&gt;
        &lt;div id="visJumboContainer" class="jumbotron"&gt;
            &lt;div id="visrep" class="ContentHead"&gt;&lt;/div&gt;
        &lt;/div&gt;
     &lt;script type="text/javascript"&gt;
  
         &lt;%--This is where the script from above sits--%&gt;
  
     &lt;/script&gt;
  &lt;/asp:Content&gt;
  </code></pre>