Gilbert tessellation
en.wikipedia.org/wiki/gilbert_tessellation
#tessellation
Log in to post a comment.
// Gilbert tessellation. Created by Reinder Nijhoff 2023 - @reindernijhoff
//
// https://turtletoy.net/turtle/4823100ba2
//
const poissonRadius = 10; // min=5, max=50, step=1
const angle = 0; // min=0, max=3, step=1 (Free, ∟, ∠, ⬡)
const lines = [];
const disc = PoissonDisc([[0,0]], poissonRadius);
const points = disc.addPoints((300/poissonRadius) **2, 50, 0, 1);
class lineSegment {
constructor(center, delta, index) {
this.index = index;
this.center =[...center];
this.delta = [...delta];
this.end = add(center, scale(delta, 0.1));
this.turtle = new Turtle(center);
this.done = false;
}
grow(dt, lines) {
if (this.done) return;
let end = add(this.end, scale(this.delta, dt));
let done = Math.abs(end[0]) > 100 || Math.abs(end[1]) > 100;
lines.forEach( (line, index) => {
if (index === this.index) return;
const intersection = segment_intersect(this.end, end, line.start.end, line.end.end);
if (intersection) {
done = true;
end = intersection;
}
});
this.done = done;
this.turtle.goto(end);
this.end = end;
}
}
class line {
constructor(center, delta, index) {
this.start = new lineSegment(center, scale(delta, 1), index);
this.end = new lineSegment(center, scale(delta, -1), index);
}
grow(dt, lines) {
this.start.grow(dt, lines);
this.end.grow(dt, lines);
}
}
points.forEach( (point, i) => {
const r = angle == 0 ? Math.random() : angle == 1 ? (Math.random() * 4 | 0) / 4 : angle == 2 ? (Math.random() * 8 | 0) / 8 : (Math.random() * 6 | 0) / 6;
const a = r * Math.PI * 2;
lines.push(new line(point, [Math.sin(a), Math.cos(a)], i));
});
function walk(i) {
lines.forEach(line => line.grow(1, lines));
return lines.find(a => !a.start.done || !a.end.done);
}
////////////////////////////////////////////////////////////////
// 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);
}
////////////////////////////////////////////////////////////////
// 2D math functions
////////////////////////////////////////////////////////////////
function scale(a,b) { return [a[0]*b,a[1]*b]; }
function add(a,b) { return [a[0]+b[0],a[1]+b[1]]; }
function segment_intersect(l1p1, l1p2, l2p1, l2p2) {
const d = (l2p2[1] - l2p1[1]) * (l1p2[0] - l1p1[0]) - (l2p2[0] - l2p1[0]) * (l1p2[1] - l1p1[1]);
if (d === 0) return false;
const n_a = (l2p2[0] - l2p1[0]) * (l1p1[1] - l2p1[1]) - (l2p2[1] - l2p1[1]) * (l1p1[0] - l2p1[0]);
const n_b = (l1p2[0] - l1p1[0]) * (l1p1[1] - l2p1[1]) - (l1p2[1] - l1p1[1]) * (l1p1[0] - l2p1[0]);
const ua = n_a / d;
const ub = n_b / d;
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) {
return [l1p1[0] + ua * (l1p2[0] - l1p1[0]), l1p1[1] + ua * (l1p2[1] - l1p1[1])];
}
return false;
}