EDERA

Edera (revised) by romvise, November 16th, 2005
JS port by @ge1doot, February 2019
contextfreeart.org/gallery2/#design/165

Log in to post a comment.

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(-0.5);
// Global code will be evaluated once.
const turtle = new Turtle();
turtle.penup();
/////////////////////////////////////////////////////
const Mat2D = class {
    constructor (m) { 
        this.m = m;
        this.type = 0;
    }
    rotate (v) {
    	const rad = Math.PI * v / 180;
    	const cos = Math.cos(rad);
    	const sin = Math.sin(rad);
    	return new Mat2D([
    		cos * this.m[0] + sin * this.m[2],
    		cos * this.m[1] + sin * this.m[3],
    		cos * this.m[2] - sin * this.m[0],
    		cos * this.m[3] - sin * this.m[1],
    		this.m[4],
    		this.m[5]
    	]);
    } 
    translate (x, y = 0) {
    	return new Mat2D([
    		this.m[0],
    		this.m[1],
    		this.m[2],
    		this.m[3],
    		this.m[4] + x * this.m[0] + y * this.m[2],
    		this.m[5] + x * this.m[1] + y * this.m[3]
    	]);
    }
    scale (x = 1, y = x) {
        return new Mat2D([
    		this.m[0] * x,
    		this.m[1] * x,
    		this.m[2] * y,
    		this.m[3] * y,
    		this.m[4],
    		this.m[5]
    	]);
	}
	size () {
        const x = this.m[0] * this.m[0] + this.m[1] * this.m[1];
        const y = this.m[2] * this.m[2] + this.m[3] * this.m[3];
        return Math.sqrt(Math.max(x, y));
	}
    transform (x, y) {
		const m0 = this.m[0] * zoom;
		const m1 = this.m[1] * zoom;
		const m2 = this.m[2] * zoom;
		const m3 = this.m[3] * zoom;
		const m4 = this.m[4] * zoom - ox;
		const m5 = this.m[5] * zoom - oy;
		return [
            m0 * x + m2 * y + m4, 
            m1 * x + m3 * y + m5
		];
	}
	boundingBox (box) {
        const p0 = this.transform(0, 0);
        const p1 = this.transform(0.5, 0);
        const p2 = this.transform(0.5, 0.5);
        const p3 = this.transform(0, 0.5);
        const minx = Math.min(p0[0], p1[0], p2[0], p3[0]);
        const maxx = Math.max(p0[0], p1[0], p2[0], p3[0]);
        const miny = Math.min(p0[1], p1[1], p2[1], p3[1]);
        const maxy = Math.max(p0[1], p1[1], p2[1], p3[1]);
    	if (minx < box[0]) box[0] = minx; else if (maxx > box[2]) box[2] = maxx;
    	if (miny < box[1]) box[1] = miny; else if (maxy > box[3]) box[3] = maxy;
    }
};
///////////////////////////////////////////////////////
const minSize = 0.02;
const shapes =  [];
let zoom = 1, ox = 0, oy = 0;
const box = [0, 0, 0, 0];
const fillCircle = m => {
	m.boundingBox(box);
	m.type = 0;
	shapes.push(m);
};
const fillRect = m => {
	m.boundingBox(box);
	m.type = 2;
	shapes.push(m);
};
const rect = m => {
	m.boundingBox(box);
	m.type = 1;
	shapes.push(m);
};
const line = m => {
	m.boundingBox(box);
	m.type = 3;
	shapes.push(m);
};
const draw = shape => {
    let d, s;
     switch (shape.type) {
        case 0:
            // fillCircle
            s = 0.2 / shape.size();
            turtle.goto(shape.transform(0.5, 0));
            turtle.down();
            d = 1;
            for (let a = 0; a < Math.PI; a += s) {
                turtle.goto(shape.transform( 0.5 * Math.cos(a) * d,  0.5 * Math.sin(a) * d));
                turtle.goto(shape.transform(-0.5 * Math.cos(a) * d, -0.5 * Math.sin(a) * d));
                d = -d;
            }
            turtle.up();
            break;
        case 1:
            // strokeRect
            turtle.goto(shape.transform(-0.5, -0.5));
            turtle.down();
            turtle.goto(shape.transform( 0.5, -0.5));
            turtle.goto(shape.transform( 0.5,  0.5));
            turtle.goto(shape.transform(-0.5,  0.5));
            turtle.goto(shape.transform(-0.5, -0.5));
            turtle.up();
            break;
        case 2:
            // fillRect
            turtle.goto(shape.transform(-0.5, -0.5));
            turtle.down();
            d = 1;
            s = 0.08 / shape.size();
            for (let y = -0.5; y <= 0.5; y += s) {
                turtle.goto(shape.transform(-0.5 * d, y));
                turtle.goto(shape.transform( 0.5 * d, y));
                d = -d;
            }
            turtle.up();
            break;
        case 3:
            // line
            turtle.goto(shape.transform(-0.5, 0));
            turtle.down();
            turtle.goto(shape.transform( 0.5, 0));
            turtle.up();
            break;
     }
}
const scale = (margin = 0.95) => {
	zoom = Math.min(
		margin * 200 / (box[2] - box[0]),
		margin * 200 / (box[3] - box[1])
	);
	ox = (box[0] + box[2]) * 0.5 * zoom;
	oy = (box[3] + box[1]) * 0.5 * zoom;
};
//
//
//
//
/////////////////////////CFDG ///////////////////////////
// https://www.contextfreeart.org/gallery2/#design/165
const EDERA = m => {
    if (m.size() < minSize * 8) return;
    fillCircle(m.scale(5));
    CIGLIO(m.translate(0, -3));
    return EDERA(m.translate(-5, -1).scale(0.9));
}
const CIGLIO = m => {
    const r = Math.random() * 2.02;
    let weight = 0;
    switch (true) {
        case r <= (weight += 1):
            fillRect(m.rotate(90).scale(1, 0.6));
            line(m.rotate(5).scale(4, 1));
            return CIGLIO(m.translate(0, -1).rotate(0.5).scale(0.994));
        case r <= (weight += 1):
            fillRect(m.rotate(90).scale(1, 0.6));
            line(m.rotate(5).scale(4, 1));
            return CIGLIO(m.translate(0, -1).rotate(0.5).scale(-0.994, 0.994));
        default:
            fillRect(m.rotate(90).scale(1, 0.6));
            line(m.rotate(5).scale(4, 1));
            return RICCIOLO(m.translate(0, -1).scale(0.994));
    }
}
const RICCIOLO = m => {
    if (m.size() < minSize) return;
    const r = Math.random() * 1.005;
    let weight = 0;
    switch (true) {
        case r <= (weight += 1):
            fillRect(m.rotate(90).scale(1, 0.6));
            line(m.rotate(5).scale(4, 1));
            return RICCIOLO(m.translate(0, -1).rotate(3).scale(0.994));
        default:
            fillRect(m.rotate(90).scale(1, 0.6));
            line(m.rotate(5).scale(4, 1));
            return RICCIOLO(m.translate(0, -1).rotate(3).scale(-0.994, 0.994));
    }
}
/////////////////// render scene //////////////////////////////
//
//
//
//
EDERA(new Mat2D([1, 0, 0, -1, 0, 0]));
scale(0.95);

// The walk function will be called until it returns false.
function walk(i) {
	const m = shapes.pop();
	if (!m) return false;
	draw(m);
	return true;
}