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