Inspired by twitter.com/cmonmatt…/1249803268571660288
#escher
Log in to post a comment.
const grid = 7; // min=3, max=17, step=2 const shape = 6; // min=4, max=12, step=2 const scale = 400 / grid; const turtle = new Slowpoke(); const walk = i => { const x = (i % grid) - (grid/2|0); const y = (i/grid|0) - (grid/2|0); // hexagon let h = .75; let w = 3/8 * (h/(Math.sqrt(3)/4)); let center = [x*2*w + (y%2)*w, y*3/2*h]; let shapes = []; if (shape == 4) { // square shapes = [[x-.5, y+.5], [x-.5,y-.5], [x+.5,y-.5], [x+.5,y+.5]]; center = [x,y]; } else if (shape == 6) { // hexagon shapes = [[0,-h],[-w,-h/2],[-w,h/2],[0,h],[w,h/2],[w,-h/2]].map(p => add(center, p)); } else { // octagon-ish for(let o = 0; o < shape; o++) { let a = (o/shape + (1/ (shape*2))) * Math.PI*2; if (shape == 8) { h = 0.7033 w = 0.666 } else if (shape == 10) { w = 0.69 h = 0.655 } else if (shape >= 12) { w = h = 2/3 + 0.0056; } shapes.push(add(center, [Math.sin(a) * h, Math.cos(a) * w])); } } // triangles inside hexagon const triangles = shapes.map((p1, idx, s) => { const p2 = s[(idx + 1) % s.length]; return (idx % 2 == 0) ? [ center, p2, p1 ] : [ center, p1, p2 ]; }); const showPaths = 1; // min=0, max=1, step=1 if (showPaths) { // line paths const paths = [ // patches [[7,0], [8,1]], [[7,4], [7,5]], // stairs side [[3,3], [3,2], [4,2],[4,0], [3,0], [3,1],[2,1], [2,2]], [[10,5],[9,4], [8,4], [7,3],[7,4],[8,5],[9,5],[10,6]], // stairs [ [4,2], [3,2], [6,5], [7,5], [4,2] ], [ [3,3], [6,6], [6,5], [3,2], [3,3] ], [ [9,4], [9,1], [8,1], [8,4], [9,4] ], [ [9,4], [10,5], [10,2], [9,1], [9,4] ], ]; while (paths.length) { const path = paths.pop(); const shape = triangles.map(p => path.map(gridPos => getPosInTriangle(gridPos, p))); shape.forEach(pts => drawPointsScaled(scale, pts, turtle)); } } const showDots = 1; // min=0, max=1, step=1 if (showDots) { // dots inbetween const gridPoints = [ [0,0], [1,0], [1,1], [2,0], [8,0], [9,0],[10,0],[10,1], [7,6], [7,7], [8,6], [8,7], [8,8], [9,6], [9,7], [9,8], [9,9], [10,7], [10,8], [10,9], [10,10], ]; while (gridPoints.length) { const pos = gridPoints.pop(); triangles.forEach(p => { turtle.jump(scl(getPosInTriangle(pos, p), scale)); turtle.circle(0.11); }); } } // triangle lines const showTriangles = 0; // min=0, max=1, step=1 if (showTriangles) { triangles.forEach(triangle => { drawPointsScaled(scale, [...triangle, triangle[0]], turtle); }); } return i < grid * grid - 1; } function getPosInTriangle(pos, triangle) { const totalSegments = 10; const [p1,p2,p3] = triangle; const d1 = pos[0] / totalSegments; const d2 = pos[1] / totalSegments; const dx = lrp(p1, p3, shape>1 ? d1 : 1-d1); const dy = lrp(p1, p2, shape>1 ? 1-d2 : d2); return sub(add(dx, dy), p1); } function drawPointsScaled(scale, points, turtle) { drawPoints(points.map(p => scl(p,scale)), turtle); } function drawPoints(points, turtle) { if (points.length > 0) turtle.jump(points[0]); points.forEach((point, idx) => { if (idx > 0) turtle.goto(point);}); } // vec2 functions function vec2(a) { return [a,a]; } function scl(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 dot(a,b) { return a[0]*b[0] + a[1]*b[1]; } function len(a) { return Math.sqrt(a[0]**2 + a[1]**2); } function nrm(a) { return scl(a, 1/len(a)); } function lrp(a,b,f) { return [a[0]*f+b[0]*(1-f), a[1]*f+b[1]*(1-f)]; } function eql(a,b) { return a[0]==b[0] && a[1]==b[1] } //////////////////////////////////////////////////////////////// // 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(2)+'_'+p[0].toFixed(2)+o[1].toFixed(2)+'_'+p[1].toFixed(2); const h2 = p[0].toFixed(2)+'_'+o[0].toFixed(2)+p[1].toFixed(2)+'_'+o[1].toFixed(2); 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); }