Adapted from a CFDG program contextfreeart.org/gallery2/#design/489
ballet by tchibo, December 16th, 2006
Log in to post a comment.
// You can find the Turtle API reference here: https://turtletoy.net/syntax Canvas.setpenopacity(1); // Global code will be evaluated once. const turtle = new Turtle(); turtle.penup(); turtle.goto(-80, -90); turtle.down(); turtle.goto(-90, -90); turtle.goto(-90, -80); turtle.up(); turtle.goto(80, -90); turtle.down(); turtle.goto(90, -90); turtle.goto(90, -80); turtle.up(); turtle.goto(-80, 90); turtle.down(); turtle.goto(-90, 90); turtle.goto(-90, 80); turtle.up(); turtle.goto(80, 90); turtle.down(); turtle.goto(90, 90); turtle.goto(90, 80); turtle.up(); ///////////////////////////////////////////////////// const Mat2D = class { constructor (m) { this.m = m; } 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] ]); } tooSmall () { 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 x < minSize || y < minSize; } 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) { if (this.m[4] - 0.5 * this.m[0] < box[0]) box[0] = this.m[4] - 0.5 * this.m[0]; else if (this.m[4] + 0.5 * this.m[0] > box[2]) box[2] = this.m[4] + 0.5 * this.m[0]; if (this.m[5] + 0.5 * this.m[3] < box[1]) box[1] = this.m[5] + 0.5 * this.m[3]; else if (this.m[5] - 0.5 * this.m[3] > box[3]) box[3] = this.m[5] - 0.5 * this.m[3]; } }; /////////////////////////////////////////////////////// const minSize = 0.15; const shapes = []; let zoom = 0, ox = 0, oy = 0; const box = [100, 100, -100, -100]; const circle = m => { m.boundingBox(box); shapes.push(m); }; const draw = m => { turtle.goto(m.transform(0.5, 0)); turtle.down(); for (let a = 0; a < 2 * Math.PI; a += 2 * Math.PI / 36) { turtle.goto(m.transform(0.5 * Math.cos(a), 0.5 * Math.sin(a))); } turtle.up(); }; const scale = (w, h, margin = 0.9) => { zoom = Math.min( margin * w / (box[2] - box[0]), margin * h / (box[3] - box[1]) ); ox = (box[0] + box[2]) * 0.5 * zoom; oy = (box[3] + box[1]) * 0.5 * zoom; }; //////////////////////////////////////////////////// const human = m => { circle(m.translate(-0.5, 1).scale(3, 4)); //belly right circle(m.translate(0.5, 1).scale(3, 4)); //belly left circle(m.scale(5, 3)); //belly low circle(m.translate(1.4, -0.5).scale(2.5)); //hip left circle(m.translate(-1.4, -0.5).scale(2.5)); //hip right lumbar(m.translate(0, 1)); //continue spine thigh(m.translate(1.4, -0.5).rotate(45)); //left leg thigh(m.translate(-1.4, -0.5).rotate(-45).scale(-1, 1)); //right leg } const thigh = m => { const r = Math.random() * 13; switch (true) { case r <= 1: circle(m.translate(0, -4).scale(2.5, 8)); circle(m.translate(-0.4, -2.5).scale(2, 6).rotate(10)); circle(m.translate(0.5, -1.5).scale(2, 5)); circle(m.translate(0, -7).scale(2, 3.5)); //knee up circle(m.translate(0, -7.7).scale(2)); //knee shin(m.translate(0, -7.7)); //leg below the knee return; case r <= 6: return thigh(m.rotate(6)); case r <= 13: return thigh(m.rotate(-6)); } } const shin = m => { const r = Math.random() * 6; switch (true) { case r <= 1: circle(m.translate(0, -3.5).rotate(-5).scale(1.8, 7.2)); circle(m.translate(-0.3, -2.5).rotate(10).scale(1.6, 5.5)); circle(m.translate(-0.6, -6).scale(1, 4.4)); circle(m.translate(-0.3, -7.6).scale(1.8)); //ankle foot(m.translate(-0.3, -7.6)); return; case r <= 6: return shin(m.rotate(-6)); } } const foot = m => { const r = Math.random() * 8; switch (true) { case r <= 1: circle(m.translate(0.8, 0.2).rotate(-30).scale(2.5, 1)); circle(m.translate(1.2, -0.4).scale(4, 1)); circle(m.translate(3.1, -0.4).rotate(6).scale(1, 0.6)); return; case r <= 3: return foot(m.rotate(3)); case r <= 8: return foot(m.rotate(-15)); } } const arm = m => { const r = Math.random() * 21; switch (true) { case r <= 1: circle(m.translate(0, -2).rotate(-8).scale(1.8, 5)); circle(m.translate(-0.2, -3).scale(1.2, 4).rotate(6)); circle(m.translate(0.4, -3).scale(0.6, 4).rotate(-6)); circle(m.translate(0, -4.6)); //elbow forearm(m.translate(0, -4.6)); return; case r <= 11: return arm(m.rotate(9)); case r <= 21: return arm(m.rotate(-9)); } } const forearm = m => { const r = Math.random() * 6; switch (true) { case r <= 1: circle(m.translate(0, -2).rotate(-6).scale(1.2, 4.8)); circle(m.translate(-0.2, -2.5).rotate(4).scale(1, 4.8)); circle(m.translate(0, -4.7).scale(0.8)); //wrist hand(m.translate(0, -4.7)); return; case r <= 6: return forearm(m.rotate(12)); } } const hand = m => { const r = Math.random() * 21; switch (true) { case r <= 1: circle(m.translate(0, -0.7).scale(1, 1.9)); for (let i = 0; i < 4; i++) { finger(m.rotate(10 * i).scale(0.85).translate(-0.36, -1.6)); } finger(m.translate(-0.4, -0.4).rotate(-45).scale(-1, 1)); //thumb return; case r <= 11: return hand(m.rotate(6)); case r <= 21: return hand(m.rotate(-6)); } } const finger = m => { if (m.tooSmall() === true) return; const r = Math.random() * 1.1; switch (true) { case r <= 0.1: circle(m.scale(0.4, 1)); finger(m.translate(0, -0.5).rotate(-20).scale(0.8)); return default: circle(m.scale(0.4, 1)); finger(m.translate(0, -0.5).rotate(-3).scale(0.8)); return; } } const lumbar = m => { const r = Math.random() * 11; switch (true) { case r <= 1: circle(m.translate(0, 2).scale(3.8)); thoracic(m.translate(0, 2)); return; case r <= 6: return lumbar(m.rotate(6)); case r <= 11: return lumbar(m.rotate(-6)); } } const thoracic = m => { const r = Math.random() * 11; switch (true) { case r <= 1: circle(m.translate(0, 2).scale(5, 6.4)); //chest circle(m.translate(1, 2.8).scale(3.5, 4)); //left breast circle(m.translate(-1, 2.8).scale(3.5, 4)); //right breast scapula(m.translate(0.35, 4.8)); //left shoulder circle(m.translate(1.2, 4.7).rotate(-15).scale(2, 0.75)); //left clavicle scapula(m.translate(-0.35, 4.8).scale(-1, 1)); //right shoulder circle(m.translate(-1.2, 4.7).scale(2, 0.75).rotate(15)); //right clavicle neck(m.translate(0, 5)); return; case r <= 6: return thoracic(m.rotate(6)); case r <= 11: return thoracic(m.rotate(-6)); } } const scapula = m => { circle(m.translate(2, -0.7).scale(2)); //shoulder arm(m.translate(2, -0.7).rotate(30)); } const neck = m => { const r = Math.random() * 11; switch (true) { case r <= 1: circle(m.scale(1.5, 2)); skull(m.translate(0, 1.6)); return; case r <= 6: return neck(m.rotate(6)); case r <= 11: return neck(m.rotate(-6)); } } const skull = m => { circle(m.translate(0, 0.8).scale(0.3, 4.3)); circle(m.translate(0, 0.8).scale(3, 4.3)); circle(m.translate(0, 0.35).scale(0.7, 1)); //nose circle(m.translate(0, -0.4).scale(1.3, 0.3)); circle(m.translate(-1.6, 0.5).rotate(15).scale(0.6, 1.3)); //left ear circle(m.translate(1.6, 0.5).rotate(-15).scale(0.6, 1.3)); //right ear circle(m.translate(0, 1).scale(2.4, 1.4)); }; /////////////////////////////////////////////////////////// //human(new Mat2D([1, 0, 0, -1, 0, 0])); human(new Mat2D([1, 0, 0, -1, 0, 0])); scale(200, 200, 0.8); // The walk function will be called until it returns false. function walk(i) { const m = shapes.shift(); if (!m) return false; draw(m); return true; }