Updated README, committed two voronois.

master
Paco Hope 2017-03-26 15:24:54 +01:00
parent 4c679fbffc
commit a59dd0dcf6
2 changed files with 265 additions and 0 deletions

186
bounded-voronoi.html Normal file
View File

@ -0,0 +1,186 @@
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links {
stroke: #000;
stroke-opacity: 0;
}
.polygons {
stroke: #fff;
}
.polygons :first-child {
fill: #ff3d5a;
}
.sites {
fill: none;
stroke: none;
}
.sites :first-child {
fill: #fff;
}
.convex-hull {
fill: none;
stroke: #feccd5;
stroke-width: 5px;
}
</style>
<svg width="960" height="800"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script>
var sourcedata = [ 100, 100, 200, 130, 400, 150, 160, 170, 130, 900, 100, 1000, 200, 130, 40, 150, 160, 170, 130 ];
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
margin = 0.4; // 0 ≤ m < 0.5
var color = d3.scaleOrdinal(d3.schemePastel1),
line = d3.line().curve(d3.curveCatmullRomClosed);
function makeData (inputData) {
var outputData = [];
// First sum all the values. Then calculate a percentage of the whole.
var sum = inputData.reduce(function(acc, val) {
return acc + val;
}, 0);
// Now turn the single data point into a pair of length, angle
for (var i = 0; i < inputData.length; i++) {
// calc the percentage
var value = 5 * inputData[i] / sum;
// calculate length and angle from that percentage
var len = Math.min(width, height) / 2 *
(1 - margin) * Math.sqrt(value);
var angle = value * 2 * Math.PI;
// stick the length and angle into the output data
outputData[i] = [ width / 2 + len * Math.cos(angle),
height / 2 + len * Math.sin(angle) ];
}
return outputData;
}
var sites = makeData( sourcedata );
var voronoi = d3.voronoi()
.extent([[-1, -1], [width + 1, height + 1]]);
var convexhull = svg.append('path')
.attr('class', 'convex-hull');
var polygon = svg.append("g")
.attr("class", "polygons");
var link = svg.append("g")
.attr("class", "links");
var site = svg.append("g")
.attr("class", "sites");
redraw();
function moved() {
sites[0] = d3.mouse(this);
redraw();
}
function redraw() {
var links = voronoi.links(sites),
ext = Math.sqrt(d3.median(links, function(l) {
var dx = l.source[0] - l.target[0],
dy = l.source[1] - l.target[1];
return dx*dx + dy*dy;
}));
var convex = d3.polygonHull(sites);
convex.centroid = d3.polygonCentroid(convex);
convex = convex.map(function(p){
var dx = p[0] - convex.centroid[0],
dy = p[1] - convex.centroid[1],
angle = Math.atan2(dy, dx);
return [p[0] + Math.cos(angle) * ext, p[1] + Math.sin(angle) * ext];
});
var sites2 = sites.slice(); // clone
for (var i = 0; i < convex.length; i++) {
var n = convex[i], m = convex[i+1]||convex[0];
var dx = n[0] - m[0],
dy = n[1] - m[1],
dist = Math.sqrt(dx * dx + dy * dy);
var pts = 10 * Math.ceil(dist / 2 / ext);
for(var j=0; j <= pts; j++) {
var p = [m[0] + dx *j / pts, m[1] + dy * j / pts];
p.artificial = 1;
sites2.push(p);
}
}
var diagram = voronoi(sites2);
var p = polygon.selectAll("path")
.data(diagram.polygons());
p.enter().append("path").merge(p).call(redrawPolygon)
p.exit().remove();
var l = link
.selectAll("line")
.data(diagram.links().filter(function(l){
return !l.source.artificial && !l.target.artificial;
}));
l.enter()
.append("line")
.merge(l)
.call(redrawLink)
.exit()
.remove();
var s = site
.selectAll("circle")
.data(sites);
s.enter()
.append("circle")
.attr("r", 2.5)
.merge(s)
.call(redrawSite);
convexhull.attr('d', line(convex));
}
function redrawPolygon(polygon) {
polygon
.attr("fill", function(d, i) {
return i < sites.length ? color(i) : 'none';
})
.attr("stroke-width", function(d, i) {
return i < sites.length ? 2 : 0;
})
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; });
}
function redrawLink(link) {
link
.attr("x1", function(d) { return d.source[0]; })
.attr("y1", function(d) { return d.source[1]; })
.attr("x2", function(d) { return d.target[0]; })
.attr("y2", function(d) { return d.target[1]; });
}
function redrawSite(site) {
site
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; });
}
</script>

79
voronoi.html Normal file
View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<meta charset="utf-8">
<canvas width="960" height="500"></canvas>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var canvas = d3.select("canvas").on("touchmove mousemove", moved).node(),
context = canvas.getContext("2d"),
width = canvas.width,
height = canvas.height;
var sites = d3.range(100)
.map(function(d) { return [Math.random() * width, Math.random() * height]; });
var voronoi = d3.voronoi()
.extent([[-1, -1], [width + 1, height + 1]]);
redraw();
function moved() {
sites[0] = d3.mouse(this);
redraw();
}
function redraw() {
var diagram = voronoi(sites),
links = diagram.links(),
polygons = diagram.polygons();
context.clearRect(0, 0, width, height);
context.beginPath();
drawCell(polygons[0]);
context.fillStyle = "#f00";
context.fill();
context.beginPath();
for (var i = 0, n = polygons.length; i < n; ++i) drawCell(polygons[i]);
context.strokeStyle = "#000";
context.stroke();
context.beginPath();
for (var i = 0, n = links.length; i < n; ++i) drawLink(links[i]);
context.strokeStyle = "rgba(0,0,0,0.2)";
context.stroke();
context.beginPath();
drawSite(sites[0]);
context.fillStyle = "#fff";
context.fill();
context.beginPath();
for (var i = 1, n = sites.length; i < n; ++i) drawSite(sites[i]);
context.fillStyle = "#000";
context.fill();
context.strokeStyle = "#fff";
context.stroke();
}
function drawSite(site) {
context.moveTo(site[0] + 2.5, site[1]);
context.arc(site[0], site[1], 2.5, 0, 2 * Math.PI, false);
}
function drawLink(link) {
context.moveTo(link.source[0], link.source[1]);
context.lineTo(link.target[0], link.target[1]);
}
function drawCell(cell) {
if (!cell) return false;
context.moveTo(cell[0][0], cell[0][1]);
for (var j = 1, m = cell.length; j < m; ++j) {
context.lineTo(cell[j][0], cell[j][1]);
}
context.closePath();
return true;
}
</script>