Based on line art by Regolo Bizzi - behance.net/regolo
Log in to post a comment.
// Amsterdam. Created by Reinder Nijhoff 2021 - @reindernijhoff // // https://turtletoy.net/turtle/d30c1379c9# const turtle = new Slowpoke(); const shape = 1; // min=0, max=2, step=1 (Hexagon, Square, Triangle) const recursion = 5; // min=1, max=10, step=1 const rotMode = 0; // min=0, max=4, step=1 (Mode 1, Mode 2, Mode 3, Mode 4, Mode 5) const rotDist = 2; // min=1, max=5, step=0.01 const rotFalloff = .85; // min=0.5, max=1, step=0.01 if (rotMode === 4) { Canvas.setpenopacity(.5); } function* drawPolygons() { const ts = Math.sin(Math.PI * 2 / 3); const tss = 4/3; // too much magic code here const def = shape == 0 ? { rotations: 6, step: [.5, 0], polygons: [[[0,-4/3*ts],[-1, 2/3*ts],[1, 2/3*ts]],[[0,2/3*ts],[1,-4/3*ts],[-1,-4/3*ts]]] } : shape == 1 ? { rotations: 4, step: [1, 0], polygons: [[[-1,-1], [1,-1], [1,1], [-1,1]]] } : { rotations: 4, step: [1, 0], polygons: [[[-2,1],[2,1],[0,-1]], [[-2,-1],[0,1],[2,-1]]] }; for (let r = 0; r<recursion; r++) { const s = Math.pow(0.5, r) * 50; // size of base shape for recursion level const n = 1 + 4 * (Math.pow(2, r)-1); // number of shapes const min_dist = rotDist * Math.pow(rotFalloff, r); // start position of first shape of this recursion level const start = shape == 0 ? sub(scale([-.5,ts], 100 - 2 * s), [0,-2/3*ts*s]) : shape == 1 ? scale([-1,1], 100 - 1.5 * s) : sub(scale([-1,1], 100 - 2 * s), [0,-.5*s]); let f = rotMode ? 1 : r % 2 == 0 ? 1 : -1; for (let j=0; j<def.rotations; j++) { if (rotMode >= 2) { f = rotMode == 2 ? 1 : -1; } const mat = rot(j * Math.PI * 2 / def.rotations); // draw all shapes for recursion level and rotate for (let i=0; i<n; i++) { const polygon = def.polygons[i % def.polygons.length]; const center = add(start, scale(def.step, i*s)); drawPoly(polygon.map(c => trans(mat, add(center, scale(c, .5 * s)))), min_dist, f = -f); rotMode === 4 && drawPoly(polygon.map(c => trans(mat, add(center, scale(c, .5 * s)))), min_dist, f = -f); yield true; } } } } const iterator = drawPolygons(); function walk(i) { return !iterator.next().done; } function drawPoly(p, min_distance, rot) { for(let r=0;r<100;r++) { // draw polygon turtle.jump(p[p.length-1]); p.forEach(c => turtle.goto(c)); // rotate polygon const c = p; p = []; for (let i=0; i<c.length; i++) { const c0 = c[i]; const c1 = c[(i+rot+c.length) % c.length]; const d = dist(c0, c1); if (d <= min_distance) return; p[i] = lerp(c0, c1, min_distance / d); } } } // // 2D Vector math // function rot(a) { return [Math.cos(a), -Math.sin(a), Math.sin(a), Math.cos(a)]; } function trans(m, a) { return [m[0]*a[0]+m[2]*a[1], m[1]*a[0]+m[3]*a[1]]; } 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 sub(a,b) { return [a[0]-b[0],a[1]-b[1]]; } function dist(a,b) { return Math.hypot(...sub(a,b)); } function lerp(a,b,t) { return [a[0]*(1-t)+b[0]*t,a[1]*(1-t)+b[1]*t]; } //////////////////////////////////////////////////////////////// // Slowpoke utility code. Created by Reinder Nijhoff 2019 // https://turtletoy.net/turtle/cfe9091ad8 //////////////////////////////////////////////////////////////// function Slowpoke(x, y) { const linesDrawn = {}; class Slowpoke extends Turtle { goto(x, y) { const p = Array.isArray(x) ? [...x] : [x, y]; if (this.isdown()) { const o = [this.x(), this.y()]; const h1 = o[0].toFixed(8)+'_'+p[0].toFixed(8)+o[1].toFixed(8)+'_'+p[1].toFixed(8); const h2 = p[0].toFixed(8)+'_'+o[0].toFixed(8)+p[1].toFixed(8)+'_'+o[1].toFixed(8); if (linesDrawn[h1] || linesDrawn[h2]) { super.up(); super.goto(p); super.down(); return; } linesDrawn[h1] = linesDrawn[h2] = true; } super.goto(p); } } return new Slowpoke(x,y); }