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