Arcs & Lines and Corners & Sides

All Two-Part Combinations of Arcs From Corners and Sides & Straight, Not Straight and Broken Lines by Sol Lewitt

I saw this in a museum and immediately knew that I would recreate it in a program some day. His work is generally conducive to it.

Log in to post a comment.

const turtle = new Turtle();
const squareWidth = 14;

function square() {
    turtle.seth(0);
    for (let i = 0; i < 4; i++) {
        turtle.fd(squareWidth);
        turtle.rt(90);
    }
}

function moveToCorner(i) {
    turtle.penup();
    for (let j = 0; j < i; j++) {
        turtle.fd(squareWidth);
        turtle.rt(90);
    }
    turtle.pendown();
}

function cornerArc(i) {
    return () => {
        moveToCorner(i);
        turtle.seth(i * 90);
        turtle.circle(squareWidth, 90);
    };
}

function sideArc(i) {
    return () => {
        moveToCorner(i);
        turtle.penup();
        turtle.fd(squareWidth / 2);
        turtle.pendown();
        turtle.seth(i * 90 + 45);
        turtle.circle(Math.sqrt(2) * squareWidth / 2, 90);
    };
}

function straight(length) {
    turtle.fd(length);
}

// stolen
class Perlin {
    constructor() {
        // Quick and dirty permutation table
        this.perm = (() => {
            const tmp = Array.from({length: 256}, () => Math.floor(Math.random() * 256));
            return tmp.concat(tmp);
        })();
    }

    grad(i, x) {
        const h = i & 0xf;
        const grad = 1 + (h & 7);

        if ((h & 8) !== 0) {
            return -grad * x;
        }

        return grad * x;
    }

    getValue(x) {
        const i0 = Math.floor(x);
        const i1 = i0 + 1;

        const x0 = x - i0;
        const x1 = x0 - 1;

        let t0 = 1 - x0 * x0;
        t0 *= t0;

        let t1 = 1 - x1 * x1;
        t1 *= t1;

        const n0 = t0 * t0 * this.grad(this.perm[i0 & 0xff], x0);
        const n1 = t1 * t1 * this.grad(this.perm[i1 & 0xff], x1);

        return 0.395 * (n0 + n1); //Output is between -1 and 1.
    }
}

function notStraight(length) {
    function envelope(x, y) {
        const flatLength = Math.ceil(length / 5);
        // double-sided sigmoid envelope
        return Math.floor(y * ((1 / (1 + Math.pow(Math.E, flatLength - x))) + (1 / (1 + Math.pow(Math.E, x - (length - flatLength)))) - 1));
    }
    
    const range = length / 4;
    const perlin = new Perlin();
    const yCoords = Array.from({ length }, (_, x) => envelope(x, range * perlin.getValue(x * 0.1)));
    console.log(yCoords);
    
    let prevHeading = 0;
    let prevY = 0;
    for (let x = 0; x < yCoords.length; x++) {
        const currY = yCoords[x];
        const yDist = currY - prevY;
        const currHeading = Math.atan2(yDist, 1) * 180 / Math.PI;
        const dist = Math.sqrt(yDist * yDist + 1);
        turtle.lt(currHeading - prevHeading);
        turtle.fd(dist);
        
        prevY = currY;
        prevHeading = currHeading;
    }
}

function broken(length) {
    const dashLength = 2;
    const gapLength = 1;
    for (let i = 0; i < length; i++) {
      if (i % (dashLength + gapLength) < dashLength) {
          turtle.pendown();
      } else {
          turtle.penup();
      }
      turtle.fd(1);
    }
}

function line(drawLine) {
    return (i) => () => {
        turtle.penup();
        let length = 0;
        switch (i) {
            case 0:
                turtle.fd(squareWidth / 2);
                turtle.rt(90);
                length = squareWidth;
                break;
            case 1:
                turtle.rt(90);
                turtle.fd(squareWidth / 2);
                turtle.lt(90);
                length = squareWidth;
                break;
            case 2:
                turtle.fd(squareWidth);
                turtle.rt(135);
                length = Math.floor(i / 2) * Math.sqrt(2) * squareWidth;
                break;
            case 3:
                turtle.rt(45);
                length = Math.floor(i / 2) * Math.sqrt(2) * squareWidth;
                break;
        }
        turtle.pendown();
        drawLine(length);
    };
}

const types = [cornerArc, sideArc, line(straight), line(notStraight), line(broken)];
const singles = Array.from({length: 20}, (_, i) => types[Math.floor(i / 4)](i % 4));

function indices(i) {
    let j = 0;
    for (let single1 = 0; single1 < singles.length; single1++){
        for (let single2 = single1; single2 < singles.length; single2++) {
            if (single1 !== single2) {
                if (i === j) {
                    return [single1, single2];
                }
                j++;
            }
        }
    }
}

Canvas.setpenopacity(1);

function walk(i) {
    const rowWidth = 14;
    
    indicesToDraw = indices(i);
    console.log(i, indicesToDraw);
    
    for (let j = 0; j < indicesToDraw.length; j++) {
        turtle.penup();
        turtle.seth(0);
        turtle.setx((i % rowWidth) * (squareWidth) - 98);
        turtle.sety(Math.floor(i / rowWidth) * (squareWidth) - 98);
        turtle.pendown();
        
        singles[indicesToDraw[j]]();
    }

    return i < 189;
}