Skip to content

Instantly share code, notes, and snippets.

@Ugarz
Forked from christophermanning/README.md
Created August 28, 2016 20:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ugarz/7ee417ad898850973fe0287cff5edab0 to your computer and use it in GitHub Desktop.
Save Ugarz/7ee417ad898850973fe0287cff5edab0 to your computer and use it in GitHub Desktop.
Voronoi Diagram with Force Directed Nodes and Delaunay Links

Created by Christopher Manning

Summary

Nodes are linked to nodes in neighboring cells. The cell's color is a function of its area.

The white lines are the Delaunay triangulation and the purple cells are the Voronoi diagram.

Controls

  • Drag the cells to interact with the diagram.
  • Use the mousewheel to add/remove nodes.
  • Hold shift while using the mousewheel to change the initialization spiral
  • Press the letter s to toggle the simulation

References

Run this gist at bl.ocks.org

<!DOCTYPE html>
<html>
<head>
<title>Voronoi Diagram with Force Directed Nodes and Delaunay Links</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
circle {
stroke: #EFEDF5;
fill: #EFEDF5;
pointer-events: none;
}
line {
pointer-events: none;
stroke: #EFEDF5;
stroke-width: 2px;
opacity: .05;
}
path{
stroke: #EFEDF5;
stroke-width: 4px;
}
</style>
</head>
<body>
<div id="chart">
</div>
<script type="text/javascript">
var w = window.innerWidth > 960 ? 960 : (window.innerWidth || 960),
h = window.innerHeight > 500 ? 500 : (window.innerHeight || 500),
radius = 5.25,
links = [],
simulate = true,
zoomToAdd = true,
// https://github.com/mbostock/d3/blob/master/lib/colorbrewer/colorbrewer.js#L105
color = d3.scale.quantize().domain([10000, 7250]).range(["#dadaeb","#bcbddc","#9e9ac8","#807dba","#6a51a3","#54278f","#3f007d"])
var numVertices = (w*h) / 3000;
var vertices = d3.range(numVertices).map(function(i) {
angle = radius * (i+10);
return {x: angle*Math.cos(angle)+(w/2), y: angle*Math.sin(angle)+(h/2)};
});
var d3_geom_voronoi = d3.geom.voronoi().x(function(d) { return d.x; }).y(function(d) { return d.y; })
var prevEventScale = 1;
var zoom = d3.behavior.zoom().on("zoom", function(d,i) {
if (zoomToAdd){
if (d3.event.scale > prevEventScale) {
angle = radius * vertices.length;
vertices.push({x: angle*Math.cos(angle)+(w/2), y: angle*Math.sin(angle)+(h/2)})
} else if (vertices.length > 2 && d3.event.scale != prevEventScale) {
vertices.pop();
}
force.nodes(vertices).start()
} else {
if (d3.event.scale > prevEventScale) {
radius+= .01
} else {
radius -= .01
}
vertices.forEach(function(d, i) {
angle = radius * (i+10);
vertices[i] = {x: angle*Math.cos(angle)+(w/2), y: angle*Math.sin(angle)+(h/2)};
});
force.nodes(vertices).start()
}
prevEventScale = d3.event.scale;
});
d3.select(window)
.on("keydown", function() {
// shift
if(d3.event.keyCode == 16) {
zoomToAdd = false
}
// s
if(d3.event.keyCode == 83) {
simulate = !simulate
if(simulate) {
force.start()
} else {
force.stop()
}
}
})
.on("keyup", function() {
zoomToAdd = true
})
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h)
.call(zoom)
var force = d3.layout.force()
.charge(-300)
.size([w, h])
.on("tick", update);
force.nodes(vertices).start();
var circle = svg.selectAll("circle");
var path = svg.selectAll("path");
var link = svg.selectAll("line");
function update(e) {
path = path.data(d3_geom_voronoi(vertices))
path.enter().append("path")
// drag node by dragging cell
.call(d3.behavior.drag()
.on("drag", function(d, i) {
vertices[i] = {x: vertices[i].x + d3.event.dx, y: vertices[i].y + d3.event.dy}
})
)
.style("fill", function(d, i) { return color(0) })
path.attr("d", function(d) { return "M" + d.join("L") + "Z"; })
.transition().duration(150).style("fill", function(d, i) { return color(d3.geom.polygon(d).area()) })
path.exit().remove();
circle = circle.data(vertices)
circle.enter().append("circle")
.attr("r", 0)
.transition().duration(1000).attr("r", 5);
circle.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
circle.exit().transition().attr("r", 0).remove();
link = link.data(d3_geom_voronoi.links(vertices))
link.enter().append("line")
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; })
link.exit().remove()
if(!simulate) force.stop()
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment