Wanted Loom 🧶

Wanted: en.wikipedia.org/wiki/Wanted_(2008_film)
(If you don't see the effect, hit "Compile & Run" a couple of times on the same mode while staring at the image (in rounded mode you still might need to search)).
Mode:
0 = Rounded
1 = Cornered
2 = Classic
3 = Random

Log in to post a comment.

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(.7);

const mode = 2; // min=0, max=3, step=1
const tileSize = 15; // min=2, max=40, step=1
const threadSize = .55; // min=0, max=.9, step=.05

class Grid {
    constructor(tileSize) {
        this.size = tileSize;
        this.half = this.size / 2;
        this.centers = [];

        let nTiles = Math.ceil(100 / this.size);
        for(let i = Math.floor((nTiles) * -this.size); i <= Math.floor((nTiles) * this.size); i += this.size) {
            this.centers.push(i);
        }
        this.gridSize = this.centers.length;
    }
    draw(t) {
        for(let i = 0; i < this.gridSize; i++) {
            turtle.jump(this.centers[i] - this.half, -110);
            turtle.goto(this.centers[i] - this.half, 110);
            turtle.jump(-110, this.centers[i] - this.half);
            turtle.goto(110, this.centers[i] - this.half);
        }        
    }
    getCenterFor(column, row) {
        return [this.centers[column], this.centers[row]];
    }
}

// Global code will be evaluated once.
const turtle = new Turtle();

const grid = new Grid(tileSize);
//grid.draw(turtle);
const delta = (tileSize * threadSize) / 2

// The walk function will be called until it returns false.
function walk(i) {
    let col = i % grid.gridSize;
    let row = i / grid.gridSize | 0;

    let tile = grid.getCenterFor(col, row);
    turtle.jump(tile[0], tile[1] - 2);
    
    switch(mode) {
        case 0:
            roundedThread(col, row, grid, tile, delta);
            break;
        case 1:
            cornerThread(col, row, grid, tile, delta);
            break;
        case 2:
            loom(col, row, grid, tile, delta);
            break;
        case 3:
            random(col, row, grid, tile, delta);
            break;
    }
    
    return i < (grid.gridSize * grid.gridSize);
}

const mistakeRow = Math.ceil(Math.random() * (grid.gridSize - 4)) + 2;
const mistakeCol = Math.ceil(Math.random() * (grid.gridSize - 4)) + 2;


function cornerThread(col, row, grid, tile, delta) {
    if(col % 2 == 0) {
        if(row % 2 == 0) {
            crossVerticalOverHorizontal(grid, tile, delta, mistakeCol == col && mistakeRow == row);
        } else {
            diagonalBottomLeftToTopRight(grid, tile, delta, mistakeCol == col && mistakeRow == row);
        }
    } else if(row % 2 == 0) {
            diagonalBottomRightToTopLeft(grid, tile, delta, mistakeCol == col && mistakeRow == row);
    } else {
            crossVerticalOverHorizontal(grid, tile, delta, mistakeCol == col && mistakeRow == row);
    }
}

function roundedThread(col, row, grid, tile, delta) {
    if(col % 2 == 0) {
        if(row % 2 == 0) {
            crossVerticalOverHorizontal(grid, tile, delta, mistakeCol == col && mistakeRow == row);
        } else {
            roundedBottomLeftToTopRight(grid, tile, delta, mistakeCol == col && mistakeRow == row);
        }
    } else if(row % 2 == 0) {
            roundedBottomRightToTopLeft(grid, tile, delta, mistakeCol == col && mistakeRow == row);
    } else {
            crossVerticalOverHorizontal(grid, tile, delta, mistakeCol == col && mistakeRow == row);
    }
}

function loom(col, row, grid, tile, delta) {
    if(((col % 2) + row) % 2 == 0) {
        crossHorizontalOverVertical(grid, tile, delta, mistakeCol == col && mistakeRow == row);
    } else {
        crossVerticalOverHorizontal(grid, tile, delta, mistakeCol == col && mistakeRow == row);
    }
}

function random(col, row, grid, tile, delta) {
    switch(Math.ceil(Math.random() * 6)) {
        case 1:
            crossVerticalOverHorizontal(grid, tile, delta);
            break;
        case 2:
            diagonalBottomLeftToTopRight(grid, tile, delta);
            break;
        case 3:
            diagonalBottomRightToTopLeft(grid, tile, delta);
            break;
        case 4:
            crossHorizontalOverVertical(grid, tile, delta);
            break;
        case 5:
            roundedBottomLeftToTopRight(grid, tile, delta);
            break;
        case 6:
            roundedBottomRightToTopLeft(grid, tile, delta);
            break;
    }
}

function crossVerticalOverHorizontal(grid, tile, delta, mistake = false) {
    if(mistake) { return crossHorizontalOverVertical(grid, tile, delta); }
    
    //vertical
    turtle.jump(tile[0] - delta, tile[1] - grid.half);
    turtle.goto(tile[0] - delta, tile[1] + grid.half);
    turtle.jump(tile[0] + delta, tile[1] - grid.half);
    turtle.goto(tile[0] + delta, tile[1] + grid.half);
    
    //horizontal
    turtle.jump(tile[0] - grid.half, tile[1] - delta);
    turtle.goto(tile[0] - delta, tile[1] - delta);
    turtle.jump(tile[0] + grid.half, tile[1] - delta);
    turtle.goto(tile[0] + delta, tile[1] - delta);
    turtle.jump(tile[0] - grid.half, tile[1] + delta);
    turtle.goto(tile[0] - delta, tile[1] + delta);
    turtle.jump(tile[0] + grid.half, tile[1] + delta);
    turtle.goto(tile[0] + delta, tile[1] + delta);
}
function crossHorizontalOverVertical(grid, tile, delta, mistake = false) {
    if(mistake) { return crossVerticalOverHorizontal(grid, tile, delta); }
    
    //horizontal
    turtle.jump(tile[0] - grid.half, tile[1] - delta);
    turtle.goto(tile[0] + grid.half, tile[1] - delta);
    turtle.jump(tile[0] - grid.half, tile[1] + delta);
    turtle.goto(tile[0] + grid.half, tile[1] + delta);
    
    //vertical
    turtle.jump(tile[0] - delta, tile[1] - grid.half);
    turtle.goto(tile[0] - delta, tile[1] - delta);
    turtle.jump(tile[0] - delta, tile[1] + grid.half);
    turtle.goto(tile[0] - delta, tile[1] + delta);
    turtle.jump(tile[0] + delta, tile[1] - grid.half);
    turtle.goto(tile[0] + delta, tile[1] - delta);
    turtle.jump(tile[0] + delta, tile[1] + grid.half);
    turtle.goto(tile[0] + delta, tile[1] + delta);
}
function diagonalBottomLeftToTopRight(grid, tile, delta, mistake = false) {
    if(mistake) { return diagonalBottomRightToTopLeft(grid, tile, delta); }
    //inside
    turtle.jump(tile[0] - grid.half, tile[1] - delta);
    turtle.goto(tile[0] - delta, tile[1] - grid.half);
    turtle.jump(tile[0] + grid.half, tile[1] + delta);
    turtle.goto(tile[0] + delta, tile[1] + grid.half);
    
    //outside-top
    turtle.jump(tile[0] - grid.half, tile[1] + delta);
    turtle.goto(tile[0] - grid.half + (delta / 2), tile[1] + delta);
    turtle.goto(tile[0] + delta, tile[1] - grid.half + (delta / 2));
    turtle.goto(tile[0] + delta, tile[1] - grid.half);
    
    let maxx = tile[0] + delta;
    let maxy = tile[1] + delta;
    //outside-bottom
    turtle.jump(tile[0] + grid.half, tile[1] - delta);
    if(tile[0] + grid.half - (delta / 2) < maxx) {
        turtle.goto(maxx, tile[1] - delta);
        turtle.jump(tile[0] - delta, maxy);
    } else {
        turtle.goto(tile[0] + grid.half - (delta / 2), tile[1] - delta);
        turtle.goto(tile[0] - delta, tile[1] + grid.half - (delta / 2));
    }
    turtle.goto(tile[0] - delta, tile[1] + grid.half);
}

function diagonalBottomRightToTopLeft(grid, tile, delta, mistake = false) {
    if(mistake) { return diagonalBottomLeftToTopRight(grid, tile, delta); }
    //inside
    turtle.jump(tile[0] + grid.half, tile[1] - delta);
    turtle.goto(tile[0] + delta, tile[1] - grid.half);
    turtle.jump(tile[0] - grid.half, tile[1] + delta);
    turtle.goto(tile[0] - delta, tile[1] + grid.half);
    
    //outside-bottom
    turtle.jump(tile[0] - grid.half, tile[1] - delta);
    turtle.goto(tile[0] - grid.half + (delta / 2), tile[1] - delta);
    turtle.goto(tile[0] + delta, tile[1] + grid.half - (delta / 2));
    turtle.goto(tile[0] + delta, tile[1] + grid.half);

    let maxx = tile[0] + delta;
    let miny = tile[1] - delta;
    //outside-top
    turtle.jump(tile[0] + grid.half, tile[1] + delta);
    if(tile[0] + grid.half - (delta / 2) < maxx) {
        turtle.goto(maxx, tile[1] + delta);
        turtle.jump(tile[0] - delta, miny);
    } else {
        turtle.goto(tile[0] + grid.half - (delta / 2), tile[1] + delta);
        turtle.goto(tile[0] - delta, tile[1] - grid.half + (delta / 2));
    }
    turtle.goto(tile[0] - delta, tile[1] - grid.half);
}

var angles = [90];
var r = grid.half + delta;
if(Math.sqrt(grid.size * grid.size * 2) < r * 2) {
    angles = [];
    let aa = (180 / Math.PI) * Math.acos(Math.sqrt(grid.size * grid.size * 2) / (r*2));
    angles.push(45 - aa);
    angles.push(aa * 2);
    angles.push(45 - aa);
}

function roundedBottomLeftToTopRight(grid, tile, delta, mistake = false) {
    if(mistake) { return roundedBottomRightToTopLeft(grid, tile, delta); }
    //inside
    turtle.jump(tile[0] - grid.half, tile[1] - delta);
    turtle.setheading(0);
    turtle.circle(-(grid.half - delta), 90)//tile[0] - delta, tile[1] - grid.half);
    turtle.jump(tile[0] + grid.half, tile[1] + delta);
    turtle.setheading(180);
    turtle.circle(-(grid.half - delta), 90)//tile[0] - delta, tile[1] - grid.half);

    //outside-top
    turtle.jump(tile[0] - grid.half, tile[1] + delta);
    turtle.setheading(0);
    turtle.circle(-r, 90)//tile[0] - delta, tile[1] - grid.half);
    //outside-bottom
    turtle.jump(tile[0] + grid.half, tile[1] - delta);
    turtle.setheading(180);
    for(let i = 0; i < angles.length; i++) {
        if(i%2 == 0) { turtle.pendown(); } else { turtle.penup(); }
        turtle.circle(-r, angles[i])//tile[0] - delta, tile[1] - grid.half);
    }
}

function roundedBottomRightToTopLeft(grid, tile, delta, mistake = false) {
    if(mistake) { return roundedBottomLeftToTopRight(grid, tile, delta); }
    //inside
    turtle.jump(tile[0] + grid.half, tile[1] - delta);
    turtle.setheading(180);
    turtle.circle((grid.half - delta), 90)//tile[0] - delta, tile[1] - grid.half);
    turtle.jump(tile[0] - grid.half, tile[1] + delta);
    turtle.setheading(0);
    turtle.circle((grid.half - delta), 90)//tile[0] - delta, tile[1] - grid.half);

    //outside-top
    turtle.jump(tile[0] + grid.half, tile[1] + delta);
    turtle.setheading(180);
    turtle.circle(r, 90)//tile[0] - delta, tile[1] - grid.half);
    //outside-bottom
    turtle.jump(tile[0] - grid.half, tile[1] - delta);
    turtle.setheading(0);
    for(let i = 0; i < angles.length; i++) {
        if(i%2 == 0) { turtle.pendown(); } else { turtle.penup(); }
        turtle.circle(r, angles[i])//tile[0] - delta, tile[1] - grid.half);
    }
}