Onions have layers 🐸

twitter.com/mknol

#handdrawing

Log in to post a comment.

Canvas.setpenopacity(.85);

const grid = 6;
const linePrecision = 20; // higher is more precision
const totalPerOnion = 12;
const thickness = 26;
const shakyness = 1;
const posOffset = 15;

const size = 200 / grid; 
const turtle = new Turtle();
const shakeRandom = new Random(777);

const getPos = (val, rnd) => Math.min(Math.max(-100 + val * size + (-(posOffset/2) + rnd.get() * posOffset), -100), 66);

// The walk function will be called until it returns false.
function walk(i) {
    const gridX = i % grid;
    const gridY = (i / grid)|0;
    
    const currentRandom = new Random(i);
    const x1 = getPos(gridX, currentRandom);
    const y1 = getPos(gridY, currentRandom);
    
    const belowRandom = new Random(i + grid);
    const x2 = getPos(gridX, belowRandom);
    const y2 = getPos(gridY, belowRandom);
    
    const nextRandom = new Random(i + 1);
    const x3 = getPos(gridX, nextRandom);
    const y3 = getPos(gridY, nextRandom);
    const t = (gridX / grid);
    
    if (gridX!=0) onion(x1,y1, x2,y2+size, shake(thickness, thickness/2), shake(totalPerOnion, 4)|0);
    if (gridY!=0) onion(x1,y1, x3+size,y3, shake(thickness, thickness/2), shake(totalPerOnion, 4)|0);

    return i + 1 < grid * grid;
}

function onion(x1,y1, x2,y2, thickness, total) {
    const angle = Math.atan2(y2-y1, x2-x1) + Math.PI/2;
    for(let i=0; i<=total; i++) {
        const t = (-0.5 + i / total) * 2;
        const cx = lerp(x1, x2, 0.5) + Math.cos(angle) * (t * thickness);
        const cy = lerp(y1, y2, 0.5) + Math.sin(angle) * (t * thickness);
        curve(x1,y1, cx,cy, x2,y2);
    }
}

const lerp = (from,to, t) => from + (to - from) * t;
const smooth = (from,to, t) => {
	t = (-2.0 * t * t * t + 3.0 * t * t);
	return (to * t + from * (1.0 - t));
}
const quad = (from,control,to, t) => {
    const q = 1 - t;
    return (q * q) * from + 2 * q * t * control + (t * t) * to;
}

function shake(v,amount) {
    return v + -(amount * .5) + shakeRandom.get() * amount;
}

function curve(x1,y1, cx,cy, x2,y2) {
    turtle.penup();
    for(let i=0; i<=linePrecision; i++) {
        const t = i / linePrecision;
        const x = quad(x1, shake(cx, shakyness), x2, t);
        const y = quad(y1, shake(cy, shakyness), y2, t);
        if (i != 0) turtle.pendown();
        turtle.goto(x,y);
    }
}


// seeded random
function Random(seed) { this.state = (seed * 777) % 2147483647 }
Random.prototype.nextInt = function() { return this.state = (1103515245.0 * this.state + 12543) % 2147483647; }
Random.prototype.get = function() { return this.nextInt() / 2147483647; }