<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.min.js"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js"></script>
    <script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.min.js"></script>
    <link rel="stylesheet" href="http://mbostock.github.com/d3/ex/colorbrewer.css">
    <style type="text/css">
        circle {
            stroke: black;
            stroke-opacity: .5;
            fill: white;
        }
        line {
            stroke: #999;
            pointer-events: none
        }
        path{
            fill:white;
            stroke: black
        }
    </style>
</head>
<body>
<div id="chart">
</div>
<script type="text/javascript">
    var w = 960,
        h = 500,
        links = [],
        voronoiVertices = [];

    var vertices = d3.range(100).map(function(d) {
        lx = Math.random() * w
        ly = Math.random() * h
        return {x: lx, y: ly, index: parseInt(lx + ly)};
    })

    var svg = d3.select("#chart")
            .append("svg")
            .attr("width", w)
            .attr("height", h)
            .attr("class", "Purples")

    var force = self.force = d3.layout.force()
            .gravity(.01)
            .size([w, h])
            .on("tick", update);

    force.nodes(vertices).start();

    svg.selectAll("path")
            .data(d3.geom.voronoi(vertices.map(function(o){return [o.x,  o.y]})))
            .enter().append("path")
            .attr("class", function(d, i) { return i ? "q" + (i % 9) + "-9" : null; })
            .attr("d", function(d) { return "M" + d.join("L") + "Z"; });


    svg.selectAll("circle")
            .data(vertices)
            .enter().append("circle")
            .call(force.drag)
            .attr("r", 10);

    var link = svg.selectAll("line")

    function update(e) {
        voronoiVertices = vertices.map(function(o){return [o.x,  o.y, o]})

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

        svg.selectAll("path")
                .data(d3.geom.voronoi(voronoiVertices))
                .map(function(d) { return "M" + d.join("L") + "Z"; })
                .filter(function(d) { return this.getAttribute("d") != d; })
                .attr("d", function(d) { return d; });

        links = []
        d3.geom.delaunay(voronoiVertices).forEach(function(d) {
            links.push(edge(d[0], d[1]));
            links.push(edge(d[1], d[2]));
            links.push(edge(d[2], d[0]));
        });

        link = link.data(links)
        link.enter().append("line")
        link.attr("x1", function(d) { return d.source[2].x; })
            .attr("y1", function(d) { return d.source[2].y; })
            .attr("x2", function(d) { return d.target[2].x; })
            .attr("y2", function(d) { return d.target[2].y; })
        link.exit().remove()
    }

    function edge(a, b) {
        return {
            source: a,
            target: b
        };
    }
</script>
</body>
</html>