Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Release all sticky nodes

How can I modify the following example so that all sticky nodes are released when a button is pressed? I've seen this implemented for nodes to be released (unstuck) with a doubleclick (here), but I want to release them all at the same time (my plan is to include this in a restart() function for the graph).

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .links line {
    stroke: #999;
    stroke-opacity: 0.6;
  }
  
  .nodes circle {
    stroke: #fff;
    stroke-width: 1.5px;
  }
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  //create somewhere to put the force directed graph
  var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");


  var nodes_data = [{
      "name": "Travis",
      "sex": "M"
    },
    {
      "name": "Rake",
      "sex": "M"
    },
    {
      "name": "Diana",
      "sex": "F"
    },
    {
      "name": "Rachel",
      "sex": "F"
    },
    {
      "name": "Shawn",
      "sex": "M"
    },
    {
      "name": "Emerald",
      "sex": "F"
    }
  ]
  var links_data = [{
      "source": "Travis",
      "target": "Rake"
    },
    {
      "source": "Diana",
      "target": "Rake"
    },
    {
      "source": "Diana",
      "target": "Rachel"
    },
    {
      "source": "Rachel",
      "target": "Rake"
    },
    {
      "source": "Rachel",
      "target": "Shawn"
    },
    {
      "source": "Emerald",
      "target": "Rachel"
    }
  ]
  //set up the simulation 
  var simulation = d3.forceSimulation()
    //add nodes
    .nodes(nodes_data);


  //add forces
  //we're going to add a charge to each node 
  //also going to add a centering force
  //and a link force
  var link_force = d3.forceLink(links_data)
    .id(function(d) {
      return d.name;
    });
  simulation
    .force("charge_force", d3.forceManyBody())
    .force("center_force", d3.forceCenter(width / 2, height / 2))
    .force("links", link_force);

  //add tick instructions: 
  simulation.on("tick", tickActions);
  //draw circles for the links 
  var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(nodes_data)
    .enter()
    .append("circle")
    .attr("r", 10)
    .attr("fill", "red");
  //draw lines for the links 
  var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(links_data)
    .enter().append("line")
    .attr("stroke-width", 2);


  var drag_handler = d3.drag()
    .on("start", drag_start)
    .on("drag", drag_drag)
    .on("end", drag_end);

  //same as using .call on the node variable as in https://bl.ocks.org/mbostock/4062045 
  drag_handler(node)
  //drag handler
  //d is the node 
  function drag_start(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function drag_drag(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  function drag_end(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = d.x;
    d.fy = d.y;
  }

  function tickActions() {
    //update circle positions each tick of the simulation 
    node
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      });

    //update link positions 
    //simply tells one end of the line to follow one node around
    //and the other end of the line to follow the other node around
    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;
      });
  }
</script>
like image 965
tgordon18 Avatar asked Feb 17 '26 10:02

tgordon18


1 Answers

That's not a Bostock's bl.ocks, and there is no double click there to release the nodes.

Anyway, you just need to remove the fx and fy properties, and reheat the simulation again:

d3.select("button").on("click", function() {
    node.each(function(d) {
        d.fx = d.fy = null;
    })
    simulation.alphaTarget(0.3).restart();
})

I'm using alphaTarget because that's what you have in your code, but you should consider using alpha instead.

Here is the code with that change:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .links line {
    stroke: #999;
    stroke-opacity: 0.6;
  }
  
  .nodes circle {
    stroke: #fff;
    stroke-width: 1.5px;
  }
</style>
<button>Release</button>
<svg width="600" height="400"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  //create somewhere to put the force directed graph
  var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");


  var nodes_data = [{
      "name": "Travis",
      "sex": "M"
    },
    {
      "name": "Rake",
      "sex": "M"
    },
    {
      "name": "Diana",
      "sex": "F"
    },
    {
      "name": "Rachel",
      "sex": "F"
    },
    {
      "name": "Shawn",
      "sex": "M"
    },
    {
      "name": "Emerald",
      "sex": "F"
    }
  ]
  var links_data = [{
      "source": "Travis",
      "target": "Rake"
    },
    {
      "source": "Diana",
      "target": "Rake"
    },
    {
      "source": "Diana",
      "target": "Rachel"
    },
    {
      "source": "Rachel",
      "target": "Rake"
    },
    {
      "source": "Rachel",
      "target": "Shawn"
    },
    {
      "source": "Emerald",
      "target": "Rachel"
    }
  ]
  //set up the simulation 
  var simulation = d3.forceSimulation()
    //add nodes
    .nodes(nodes_data);


  //add forces
  //we're going to add a charge to each node 
  //also going to add a centering force
  //and a link force
  var link_force = d3.forceLink(links_data)
    .id(function(d) {
      return d.name;
    });
  simulation
    .force("charge_force", d3.forceManyBody())
    .force("center_force", d3.forceCenter(width / 2, height / 2))
    .force("links", link_force);

  //add tick instructions: 
  simulation.on("tick", tickActions);
  //draw circles for the links 
  var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(nodes_data)
    .enter()
    .append("circle")
    .attr("r", 10)
    .attr("fill", "red");
  //draw lines for the links 
  var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(links_data)
    .enter().append("line")
    .attr("stroke-width", 2);


  var drag_handler = d3.drag()
    .on("start", drag_start)
    .on("drag", drag_drag)
    .on("end", drag_end);

  //same as using .call on the node variable as in https://bl.ocks.org/mbostock/4062045 
  drag_handler(node)
  //drag handler
  //d is the node 
  function drag_start(d) {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function drag_drag(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  function drag_end(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = d.x;
    d.fy = d.y;
  }

  function tickActions() {
    //update circle positions each tick of the simulation 
    node
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      });

    //update link positions 
    //simply tells one end of the line to follow one node around
    //and the other end of the line to follow the other node around
    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.select("button").on("click", function() {
    node.each(function(d) {
      d.fx = d.fy = null;
    })
    simulation.alphaTarget(0.3).restart();
  })
</script>
like image 56
Gerardo Furtado Avatar answered Feb 20 '26 07:02

Gerardo Furtado



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!