block by nbremer e627a8fced026a9286932c6c0a02d71b

Gooey Collision Detection

Full Screen

A gooey version of Mike Bostock’s famous collision detection example that appears on my blog on More fun data visualizations with the gooey effect. I felt that example would lend itself very nicely for a bit of gooey fun :)

It is rather heavy on the CPU though

You can find the other examples from the blog here

Older examples with the Gooey effect

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>

	///////////////////////////////////////////////////////////////////////////
	/////////////////////////////// Set-up ////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////	
	
	var width = 960,
	    height = 500;

	var nodes = d3.range(150).map(function() { return {radius: Math.random() * 14 + 8}; }),
	    root = nodes[0];

	root.radius = 0;
	root.fixed = true;

	var force = d3.layout.force()
	    .gravity(0.05)
	    .charge(function(d, i) { return i ? 0 : -2000; })
	    .nodes(nodes)
	    .size([width, height]);

	force.start();

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

	///////////////////////////////////////////////////////////////////////////
	///////////////////////////// Create filter ///////////////////////////////
	///////////////////////////////////////////////////////////////////////////	

	//SVG filter for the gooey effect
	//Code taken from //tympanus.net/codrops/2015/03/10/creative-gooey-effects/
	var defs = svg.append("defs");
	var filter = defs.append("filter").attr("id","gooeyCodeFilter");
	filter.append("feGaussianBlur")
		.attr("in","SourceGraphic")
		.attr("stdDeviation","10")
		//to fix safari: //stackoverflow.com/questions/24295043/svg-gaussian-blur-in-safari-unexpectedly-lightens-image
		.attr("color-interpolation-filters","sRGB") 
		.attr("result","blur");
	filter.append("feColorMatrix")
		.attr("in","blur")
		.attr("mode","matrix")
		.attr("values","1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7")
		.attr("result","gooey");
	//If you want the end shapes to be exactly the same size as without the filter
	//add the feComposite below. However this will result in a less beautiful gooey effect
	//filter.append("feBlend")
	//	.attr("in","SourceGraphic")
	//	.attr("in2","gooey");
	//Instead of the feBlend, you can do feComposite. This will also place a sharp image on top
	//But it will result in smaller circles
	//filter.append("feComposite") //feBlend
	// 	.attr("in","SourceGraphic")
	// 	.attr("in2","gooey")
	// 	.attr("operator","atop");
	
	///////////////////////////////////////////////////////////////////////////
	///////////////////////////// Create circles //////////////////////////////
	///////////////////////////////////////////////////////////////////////////	
			
	var circleWrapper = svg.append("g")
		.attr("class", "circleWrapper")
		.style("filter", "url(#gooeyCodeFilter)");
				
	circleWrapper.selectAll("circle")
	    .data(nodes.slice(1))
	  .enter().append("circle")
	    .attr("r", function(d) { return d.radius; })
	    .style("fill", "#EE3E64");

	///////////////////////////////////////////////////////////////////////////
	/////////////////////////////// Functions /////////////////////////////////
	///////////////////////////////////////////////////////////////////////////	
  
	force.on("tick", function(e) {
	  var q = d3.geom.quadtree(nodes),
	      i = 0,
	      n = nodes.length;

	  while (++i < n) q.visit(collide(nodes[i]));

	  svg.selectAll("circle")
	      .attr("cx", function(d) { return d.x; })
	      .attr("cy", function(d) { return d.y; });
	});

	svg.on("mousemove", function() {
	  var p1 = d3.mouse(this);
	  root.px = p1[0];
	  root.py = p1[1];
	  force.resume();
	});

	function collide(node) {
	  var r = node.radius + 16,
	      nx1 = node.x - r,
	      nx2 = node.x + r,
	      ny1 = node.y - r,
	      ny2 = node.y + r;
	  return function(quad, x1, y1, x2, y2) {
	    if (quad.point && (quad.point !== node)) {
	      var x = node.x - quad.point.x,
	          y = node.y - quad.point.y,
	          l = Math.sqrt(x * x + y * y),
	          r = node.radius + quad.point.radius;
	      if (l < r) {
	        l = (l - r) / l * .5;
	        node.x -= x *= l;
	        node.y -= y *= l;
	        quad.point.x += x;
	        quad.point.y += y;
	      }
	    }
	    return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
	  };
	}

</script>