|
// 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 |
|
} |