// needed if using a build tool like browserify // var d3 = require('d3'); console.log('ready to get sampling') var width = 300 let height = 400 var sample = poissonDiscSampler(width, height, 3.45) let samples = [] let s while (s = sample()) samples.push(s) console.log(samples) var voronoi = d3.voronoi() .extent([[0, 0], [width, height]]) var canvas = d3.select('body').append('canvas') .attr('width', width) .attr('height', height) var context = canvas.node().getContext('2d') var image = new Image() image.src = 'tacocatlit.jpg' image.onload = start function start () { context.drawImage(image, 0, 0) image = context.getImageData(0, 0, width, height) var diagram = voronoi(samples) var links = diagram.links() polygons = diagram.polygons() console.log('diagram, ', diagram) context.fillStyle = 'green' // "#f00"; context.fill() for (var i = 0, n = polygons.length; i < n; ++i) { context.beginPath() drawCell(polygons[i]) var x = Math.floor(polygons[i].data[0]), y = Math.floor(polygons[i].data[1]), q = (y * width + x) << 2 var color = d3.rgb(image.data[q + 0], image.data[q + 1], image.data[q + 2]) + '' // console.log(color) context.fillStyle = color context.fill() } } // Based on https://www.jasondavies.com/poisson-disc/ function poissonDiscSampler (width, height, radius) { var k = 30, // maximum number of samples before rejection radius2 = radius * radius, R = 3 * radius2, cellSize = radius * Math.SQRT1_2, gridWidth = Math.ceil(width / cellSize), gridHeight = Math.ceil(height / cellSize), grid = new Array(gridWidth * gridHeight), queue = [], queueSize = 0, sampleSize = 0 return function () { if (!sampleSize) return sample(Math.random() * width, Math.random() * height) // Pick a random existing sample and remove it from the queue. while (queueSize) { var i = Math.random() * queueSize | 0, s = queue[i] // Make a new candidate between [radius, 2 * radius] from the existing sample. for (var j = 0; j < k; ++j) { var a = 2 * Math.PI * Math.random(), r = Math.sqrt(Math.random() * R + radius2), x = s[0] + r * Math.cos(a), y = s[1] + r * Math.sin(a) // Reject candidates that are outside the allowed extent, // or closer than 2 * radius to any existing sample. if (x >= 0 && x < width && y >= 0 && y < height && far(x, y)) return sample(x, y) } queue[i] = queue[--queueSize] queue.length = queueSize } } function far (x, y) { var i = x / cellSize | 0, j = y / cellSize | 0, i0 = Math.max(i - 2, 0), j0 = Math.max(j - 2, 0), i1 = Math.min(i + 3, gridWidth), j1 = Math.min(j + 3, gridHeight) for (j = j0; j < j1; ++j) { var o = j * gridWidth for (i = i0; i < i1; ++i) { if (s = grid[o + i]) { var s, dx = s[0] - x, dy = s[1] - y if (dx * dx + dy * dy < radius2) return false } } } return true } function sample (x, y) { var s = [x, y] queue.push(s) grid[gridWidth * (y / cellSize | 0) + (x / cellSize | 0)] = s ++sampleSize ++queueSize return s } } 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 }