Last active
December 18, 2016 03:41
-
-
Save lelandbatey/7f44b69615ee19fe5c2a7daa8e26125d to your computer and use it in GitHub Desktop.
Poisson disk, implemented from the paper!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// I implemented poisson disk selection from this paper: http://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf | |
// This was very fun, I highly recommend it to anybody! | |
function create_draw(ctx) { | |
return { | |
line: (start, end) => { | |
let [sx, sy] = start; | |
let [ex, ey] = end; | |
ctx.beginPath(); | |
ctx.strokeStyle = 'black'; | |
ctx.lineWidth = 1; | |
ctx.moveTo(sx, sy); | |
ctx.lineTo(ex, ey); | |
ctx.stroke(); | |
}, | |
circle: (center, radius, colr='blue') => { | |
ctx.save(); | |
ctx.beginPath(); | |
ctx.strokeStyle = colr; | |
ctx.fillStyle = colr; | |
ctx.lineWidth = 5; | |
let [x, y] = center; | |
ctx.arc(x, y, radius, Math.PI, Math.PI-.0001, 0); | |
ctx.fill(); | |
ctx.stroke(); | |
ctx.restore(); | |
} | |
} | |
} | |
function assemble_grid(radius) { | |
var canv = document.getElementById('canvas'); | |
let size = canv.height; | |
let factor = radius / Math.sqrt(2); | |
let rv = []; | |
for (a = 0; a < Math.trunc(size/factor); a++) { | |
let cur = []; | |
for (b = 0; b < Math.trunc(size/factor); b++) { | |
cur.push([]); | |
} | |
rv.push(cur); | |
} | |
return rv; | |
} | |
function make_get_cell(grid) { | |
var canv = document.getElementById('canvas'); | |
var pxsize = canv.height; | |
var factor = pxsize/grid.length; | |
// console.log(pxsize, factor, grid.length); | |
return function get_cell(point){ | |
let [x, y] = point; | |
return [Math.trunc(x/factor), Math.trunc(y/factor)]; | |
} | |
} | |
function getRandomArbitrary(min, max) { | |
return Math.random() * (max - min) + min; | |
} | |
function distance(a, b) { | |
let [ax, ay] = a; | |
let [bx, by] = b; | |
return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); | |
} | |
console.log(distance([1.0, 1.0], [3.0, 3.0])); | |
// Check the 'nearby' cells to see if theres any points in nearby cells that're | |
// too close. Returns 'true' if we are NOT within the radius, 'false' if we're | |
// within a radius. | |
function are_we_far_enough(point, grid, radius, draw) { | |
let get_cell = make_get_cell(grid); | |
let [cx, cy] = get_cell(point); | |
let max = grid.length; | |
for (x = cx - 3; x < cx+3; x++){ | |
if (x < 0 || x >= max) { | |
continue; | |
} | |
for (y = cy -3; y < cy+3; y++) { | |
if (y < 0 || y >= max) { | |
continue; | |
} | |
cmp = grid[x][y]; | |
if (cmp.length < 2) { | |
continue; | |
} | |
let dist = Math.abs(distance(point, cmp)); | |
if (dist <= radius) { | |
//console.log(["Cell is too close:", point, " to ", cmp]); | |
return false; | |
} else { | |
//console.log(["We're far enough from", point, " to ", cmp, "with ", dist]); | |
} | |
} | |
} | |
return true; | |
} | |
function in_annulus(a, b, radius) { | |
if (Math.abs(distance(a, b)) > radius) { | |
if (Math.abs(distance(a, b) < (2*radius))) { | |
return true; | |
} | |
} | |
return false; | |
} | |
function draw_lines(ctx, grid) { | |
var canv = document.getElementById('canvas'); | |
var draw = create_draw(ctx); | |
for (x = 0; x < canv.width; x += Math.trunc(canv.width/grid.length)){ | |
draw.line([x, 0], [x, canv.height]); | |
draw.line([0, x], [canv.width, x]); | |
} | |
} | |
function pickRandomProperty(obj) { | |
var keys = Object.keys(obj) | |
return obj[keys[ keys.length * Math.random() << 0]]; | |
} | |
function sleep(ms) { | |
return new Promise((resolve) => setTimeout(resolve, ms)) | |
} | |
$(document).ready(() => { | |
var canv = document.getElementById('canvas'); | |
var ctx = canv.getContext('2d'); | |
var draw = create_draw(ctx); | |
// Your other code goes here! | |
let randPix = () => { | |
return [Math.trunc(getRandomArbitrary(0, canv.width)), Math.trunc(getRandomArbitrary(0, canv.height))]; | |
} | |
let radius = 20; | |
let grid = assemble_grid(radius); | |
let active = {}; | |
//draw_lines(ctx, grid); | |
let get_cell = make_get_cell(grid); | |
let insertGrid = (point) => { | |
let cell = get_cell(point); | |
//draw.circle(point, 2); | |
// console.log(point, cell); | |
let thing = grid[cell[0]][cell[1]]; | |
if (thing.length > 0) { | |
console.log(["We found something when we shouldn't have:", thing]) | |
} | |
grid[cell[0]][cell[1]] = point; | |
} | |
let sample = randPix(); | |
let cell = get_cell(sample); | |
ctx.strokeStyle = 'red'; | |
insertGrid(sample); | |
ctx.strokeStyle = 'blue'; | |
active[cell] = sample; | |
console.log(active); | |
let curpoint = sample; | |
let doDraw = (curpoint) => { | |
ctx.clearRect(0, 0, canv.width, canv.height); | |
//draw_lines(ctx, grid); | |
_.forEach(grid, (row) => { | |
_.forEach(row, (point) => { | |
if (point.length > 0) { | |
draw.circle(point, 2) | |
} | |
}); | |
}); | |
_.forEach(active, (point, cell) => { | |
draw.circle(point, 3, "red"); | |
}); | |
//draw.circle(curpoint, 14, "green"); | |
} | |
let propagate = () => { | |
curpoint = pickRandomProperty(active); | |
var point = randPix(); | |
var sel = 0; | |
for (sel = 0; sel < 30; sel++){ | |
point = randPix(); | |
while (1) { | |
if (in_annulus(curpoint, point, radius)){ | |
break; | |
} | |
point = randPix(); | |
} | |
if (are_we_far_enough(point, grid, radius, draw)){ | |
break; | |
} | |
} | |
// If we didn't find a valid point, exit. Otherwise, add it! | |
if (sel == 30) { | |
//console.log("We couldn't find a valid point..."); | |
let cell = get_cell(curpoint); | |
delete active[cell]; | |
if (_.isEmpty(active)) { | |
doDraw(curpoint); | |
return | |
} | |
} else { | |
insertGrid(point); | |
let cell = get_cell(point); | |
active[cell] = point; | |
} | |
doDraw(curpoint); | |
window.requestAnimationFrame(propagate); | |
} | |
propagate(); | |
}); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.js"></script> | |
<body> | |
<canvas id='canvas' width='1000' height='1000'></canvas> | |
</body> | |
<script src="distribution.js"></script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment