Skip to content

Instantly share code, notes, and snippets.

@lelandbatey
Last active December 18, 2016 03:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lelandbatey/7f44b69615ee19fe5c2a7daa8e26125d to your computer and use it in GitHub Desktop.
Save lelandbatey/7f44b69615ee19fe5c2a7daa8e26125d to your computer and use it in GitHub Desktop.
Poisson disk, implemented from the paper!
// 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();
});
<!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