Grow pattern based on Poisson-Disc Sampling. Based on work by @FogleBird: medium.com/@fogleman…-basics-ec0407ab5929.
#possion-disc
Log in to post a comment.
// Poisson-Disc Grow Patterns. Created by Reinder Nijhoff 2019 - @reindernijhoff
// The MIT License
//
// https://turtletoy.net/turtle/b5510898dc
//
// Grow pattern based on Poisson-disc Sampling. Based on work by @FogleBird:
// https://medium.com/@fogleman/pen-plotter-programming-the-basics-ec0407ab5929
//
const growFromCenter = 1; // min=0, max=1, step=1 (No, Yes)
const randomGrowOrder = 0.1; // min=0, max=1, step=0.0001
const loosePacking = 0.5; // min=0, max=1, step=0.0001
const maxGrowIterations = 35000; // min=1, max=100000, step=1
//
// Code
//
Canvas.setpenopacity(.75);
const turtle = new Turtle();
const startPoints = [];
if (growFromCenter) {
startPoints.push([0,0]);
} else {
// const radius = 45; // spawn growpoints on circle
// for(let a=0; a<2*Math.PI; a+=1/radius) {
// startPoints.push([radius*Math.cos(a), radius*Math.sin(a)]);
// }
for(let i=0; i<200; i++) {
startPoints.push([Math.random()*200-100, Math.random()*200-100]);
}
}
const disc = new PoissonDisc(startPoints, .75);
let index = 0;
function walk(i) {
const points = disc.addPoints(1, 32, loosePacking, randomGrowOrder);
while (index<points.length) {
if (points[index][2]) {
turtle.jump(points[index]);
turtle.goto(points[index][2]);
}
index++;
}
return i < maxGrowIterations;
}
////////////////////////////////////////////////////////////////
// Poisson-Disc utility code. Created by Reinder Nijhoff 2019
// https://turtletoy.net/turtle/b5510898dc
////////////////////////////////////////////////////////////////
function PoissonDisc(startPoints, radius) {
class PoissonDiscGrid {
constructor(sp, radius) {
this.cellSize = 1/Math.sqrt(2)/radius;
this.radius2 = radius*radius;
this.cells = [];
sp.forEach( p => this.insert(p) );
}
insert(p) {
const x = p[0]*this.cellSize|0, y=p[1]*this.cellSize|0;
for (let xi = x-1; xi<=x+1; xi++) {
for (let yi = y-1; yi<=y+1; yi++) {
const ps = this.cell(xi,yi);
for (let i=0; i<ps.length; i++) {
if ((ps[i][0]-p[0])**2 + (ps[i][1]-p[1])**2 < this.radius2) {
return false;
}
}
}
}
this.cell(x, y).push(p);
return true;
}
cell(x,y) {
const c = this.cells;
return (c[x]?c[x]:c[x]=[])[y]?c[x][y]:c[x][y]=[];
}
}
class PoissonDisc {
constructor(sp, radius) {
this.result = [...sp];
this.active = [...sp];
this.grid = new PoissonDiscGrid(sp, radius);
}
addPoints(count, maxTries=16, loosePacking=0, randomGrowOrder=0) {
mainLoop: while (this.active.length > 0 && count > 0) {
const index = (Math.random() * this.active.length * randomGrowOrder) | 0;
const point = this.active[index];
for (let i=0; i < maxTries; i++) {
const a = Math.random() * 2 * Math.PI;
const d = (Math.random()*loosePacking + 1) * radius;
const p = [point[0] + Math.cos(a)*d, point[1] + Math.sin(a)*d, point];
if (this.grid.insert(p)) {
this.result.push(p);
this.active.push(p);
count--;
continue mainLoop;
}
}
this.active.splice(index, 1);
}
return this.result;
}
}
return new PoissonDisc(startPoints, radius);
}