Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active Oct 26, 2016
Embed
What would you like to do?
Capacity Constrained Point Distributions
license: mit
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.polygons {
fill: none;
stroke: #333;
}
</style>
<svg width="940" height="480"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20b);
var sites = d3.range(512)
.map(function (d) {
return [Math.random() * width, Math.random() * height];
});
var voronoi = d3.voronoi().size([width, height]),
diagram = voronoi(sites);
var polygon = svg.append("g")
.attr("class", "polygons")
.selectAll("path")
.data(diagram.polygons())
.enter().append("path")
.attr('fill', function (d, i) {
return color(i)
})
.attr('fill-opacity', 0.1)
.attr('stroke-opacity', 0.1);
const capacity = 20;
var points = d3.range(capacity * sites.length)
.map(function (d) {
return [((Math.random() > 0.5 ? -1 : 1) / 4 + (Math.random() - Math.random()) / 4 + 1 / 2) * width, (1 + Math.random() - Math.random()) / 2 * height];
});
function distance2(a, b) {
var dx = a[0] - b[0],
dy = a[1] - b[1];
return /*Math.sqrt*/ (dx * dx + dy * dy);
}
function iterate() {
var changes = 0;
for (var i = 1; i < sites.length; i++) {
for (var j = 0; j < i; j++) {
var Hi = [],
Hj = [],
k, ki, kj;
for (k = 0; k < capacity; k++) {
Hi.push(distance2(points[i * capacity + k], sites[j]) - distance2(points[i * capacity + k], sites[i]));
Hj.push(distance2(points[j * capacity + k], sites[i]) - distance2(points[j * capacity + k], sites[j]));
}
while (Hi.length > 0 && Hj.length > 0 && ((ki = d3.scan(Hi)) || true) && ((kj = d3.scan(Hj)) || true) && (Hi[ki] + Hj[kj] < 0)) {
changes++;
var temp = points[i * capacity + ki];
points[i * capacity + ki] = points[j * capacity + kj];
points[j * capacity + kj] = temp;
Hi = Hi.slice(0, ki).concat(Hi.slice(ki + 1));
Hj = Hj.slice(0, kj).concat(Hj.slice(kj + 1));
}
}
}
return changes;
}
var site = svg.selectAll('circle')
.data(sites)
.enter()
.append('circle')
.attr('transform', function (d) {
return 'translate(' + d + ')';
})
.attr('r', 3)
.attr('fill', function (d, i) {
return color(i);
})
if (false)
svg.selectAll('rect')
.data(points)
.enter()
.append('rect')
.attr('transform', function (d) {
return 'translate(' + d + ')';
})
.attr('width', 2)
.attr('height', 2)
.attr('fill', function (d, i) {
return color(Math.floor(i / capacity));
})
var lastchanges = null;
var interval = d3.interval(function () {
var changes = iterate();
svg.selectAll('circle')
.data(sites)
.attr('transform', function (d) {
return 'translate(' + d + ')';
})
svg.selectAll('rect')
.data(points)
.attr('transform', function (d) {
return 'translate(' + d + ')';
})
diagram = voronoi(sites);
polygon = polygon.data(diagram.polygons())
.attr("d", function (d) {
return d ? "M" + d.join("L") + "Z" : null;
});;
sites = sites.map(function (site, i) {
var pts = points.slice(i * capacity, i * capacity + capacity);
site[0] = d3.mean(pts.map(function (d) {
return d[0];
}));
site[1] = d3.mean(pts.map(function (d) {
return d[1];
}));
return site;
});
console.log("changes", changes);
if (changes == lastchanges) {
console.log("stabilized!")
interval.stop();
polygon
.transition()
.duration(4000)
.attr('fill-opacity', 0)
.attr('stroke-opacity', 0.1);
site.transition()
.duration(4000)
.attr('fill', 'black');
}
lastchanges = changes;
})
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment