Skip to content

Instantly share code, notes, and snippets.

@Kcnarf
Last active December 3, 2018 08:29
Show Gist options
  • Save Kcnarf/0e920739ca451d449f197be3c867200b to your computer and use it in GitHub Desktop.
Save Kcnarf/0e920739ca451d449f197be3c867200b to your computer and use it in GitHub Desktop.
Blobbing Particles II
license: gpl-3.0

This block is a continuation of a previous block, and a recreation :

  • apply radial gradient to circle, and clip each circle with a distanceLimitedVornoy cell
  • use 2 layers: cells of the same layer are blobbing/merging thanks to distanceLimitedVornoy technique; cells of distinct layers are overlapping

Compared to the previous block, the radial gradient is applied to basic circles. As circles have constant radius/width, the radial gradient no longer flickers. Also, each circle is clipped with its respective distanceLimitedVoronoi cell, leading to the expected blobbing effect.

Acknowledgments to:

<meta charset="utf-8">
<style>
.layer {
opacity: 0.5;
}
.particle .seed {
fill: black;
}
.particle .influence-zone {
fill: url("#radial-gradient");
stroke: none;
}
</style>
<body>
<svg>
<defs>
<radialGradient id="radial-gradient" cx="50%" cy="50%" r="50%">
<stop offset="25%" stop-color="white"></stop>
<stop offset="100%" stop-color="black"></stop>
</radialGradient>
</defs>
</svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://raw.githack.com/Kcnarf/d3-distanceLimitedVoronoi/master/distance-limited-voronoi.js"></script>
<script>
var width = 960,
height = 500,
influenceRadius = 20;
var layerCount = 2,
particleCountPerLayer = 25,
particles = new Array(layerCount);
for (var l = 0; l < layerCount; ++l) {
particles[l] = new Array(particleCountPerLayer);
for (var i = 0; i < particleCountPerLayer; ++i) {
particles[l][i] = {
x: Math.random() * width,
y: Math.random() * height,
vx: 0,
vy: 0
};
}
}
var limitedVoronoi = d3.distanceLimitedVoronoi()
.limit(influenceRadius)
.extent([[-influenceRadius,-influenceRadius],[width+influenceRadius,height+influenceRadius]])
.x( function(d) { return d.x })
.y( function(d) { return d.y });
var svg = d3.select("svg");
svg.attr("width", width)
.attr("height", height);
svg.selectAll("g").data(d3.range(layerCount))
.enter()
.append("g")
.classed("layer", true)
.attr("id", function(d) { return "layer-"+d; });
for (var l = 0; l < layerCount; l++) {
var layer = svg.select("#layer-"+l);
var clipPathName = "clip-"+l+"-";
var drawnParticles = layer.selectAll(".particle").data(particles[0]);
enteringParticles = drawnParticles.enter()
.append("g")
.classed("particle", true);
enteringParticles
.append("clipPath")
.attr("id", function(d,i){ return clipPathName+i; })
.append("path")
.classed("clip", true);
enteringParticles.append("circle")
.classed("influence-zone", true)
.attr("r", influenceRadius)
.attr("clip-path", function(d, i){ return "url(#"+clipPathName+i+")"; });
}
d3.timer(function(elapsed) {
for (var l = 0; l < layerCount; l++) {
var layer = svg.select("#layer-"+l);
for (var i = 0; i < particleCountPerLayer; ++i) {
var p = particles[l][i];
p.x += p.vx;
p.y += p.vy;
if (p.x < -influenceRadius) {
p.x += (width+2*influenceRadius);
} else if (p.x > width+influenceRadius) {
p.x -= (width+2*influenceRadius);
}
if (p.y < -influenceRadius) {
p.y += (height+2*influenceRadius);
} else if (p.y > height+influenceRadius) {
p.y -= (height+2*influenceRadius);
}
p.vx += 0.2 * (Math.random() - .5) - 0.01 * p.vx;
p.vy += 0.2 * (Math.random() - .5) - 0.01 * p.vy;
}
var limitedCells = limitedVoronoi(particles[l]);
layer.selectAll(".particle .influence-zone").data(limitedCells)
.attr("cx", function(d) { return d.datum.x; })
.attr("cy", function(d) { return d.datum.y; });
layer.selectAll(".particle .clip").data(limitedCells)
.attr("d", function(d) { return d.path; });
}
});
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment