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