Log in to post a comment.

Canvas.setpenopacity(-1);

// Title: Moiréning Face

let Spacing = 6.5;       // min=2, max=15, step=.5
let Rings = 14;        // min=5, max=50, step=1
let Shift = 22.5;        // min=0, max=50, step=.5
let NoseY = 35;        // min=0, max=60, step=2
let NoseWidth = 17;    // min=4, max=25, step=1
let NoseLength = 19;   // min=5, max=30, step=1
let NoseFill = 9;      // min=1, max=15, step=1
let NoseFlip = -1;      // min=-1, max=1, step=2 (1 for point up, -1 for point down)
let MouthY = 60;       // min=10, max=100, step=2
let MouthWidth = 39;   // min=5, max=50, step=2
let MouthThickness = 3;// min=1, max=6, step=1

const totalRings = Rings * 2;
const resolution = 80;
const ringSteps = totalRings * resolution;

const noseOutlineSteps = 20;
const noseFillLines = NoseFill;
const noseStepsPerFill = 20;
const noseTotalSteps = noseOutlineSteps + (noseFillLines * noseStepsPerFill);

const mouthLines = MouthThickness;
const mouthStepsPerLine = 30;
const mouthTotalSteps = mouthLines * mouthStepsPerLine;

const totalSteps = ringSteps + noseTotalSteps + mouthTotalSteps;

const turtle = new Turtle();

function walk(i) {
    if (i >= totalSteps) return false;

    // 1. Draw Moiré Eye Rings
    if (i < ringSteps) {
        const ringStep = Math.floor(i / resolution);
        const seg = i % resolution;

        const setIndex = Math.floor(ringStep / Rings);
        const ring = ringStep % Rings;
        const radius = (ring + 1) * Spacing;

        let cx = (setIndex === 0) ? -Shift : Shift;
        let cy = 0;

        const angle1 = (seg / resolution) * Math.PI * 2;
        const angle2 = ((seg + 1) / resolution) * Math.PI * 2;

        const x1 = cx + Math.cos(angle1) * radius;
        const y1 = cy + Math.sin(angle1) * radius;
        const x2 = cx + Math.cos(angle2) * radius;
        const y2 = cy + Math.sin(angle2) * radius;

        if (seg === 0) {
            turtle.penup();
            turtle.goto(x1, y1);
            turtle.pendown();
        } else {
            turtle.goto(x2, y2);
        }
        return true;
    }

    // 2. Draw Filled Nose with Length Control
    let noseIdx = i - ringSteps;
    if (noseIdx < noseTotalSteps) {
        const halfW = NoseWidth / 2;
        const tipY = NoseY + (NoseLength * NoseFlip);
        const baseY = NoseY;

        // Outline the triangle
        if (noseIdx < noseOutlineSteps) {
            const t = noseIdx / noseOutlineSteps;
            let nx, ny;

            if (t < 0.33) {
                let subT = t * 3;
                nx = 0 * (1 - subT) + halfW * subT;
                ny = tipY * (1 - subT) + baseY * subT;
            } else if (t < 0.66) {
                let subT = (t - 0.33) * 3;
                nx = halfW * (1 - subT) + (-halfW) * subT;
                ny = baseY;
            } else {
                let subT = (t - 0.66) * 3;
                nx = (-halfW) * (1 - subT) + 0 * subT;
                ny = baseY * (1 - subT) + tipY * subT;
            }

            if (noseIdx === 0) {
                turtle.penup();
                turtle.goto(nx, ny);
                turtle.pendown();
            } else {
                turtle.goto(nx, ny);
            }
            return true;
        }
        
        // Fill the triangle proportionally based on length and orientation
        let fillIdx = noseIdx - noseOutlineSteps;
        const lineIndex = Math.floor(fillIdx / noseStepsPerFill);
        const stepInLine = fillIdx % noseStepsPerFill;
        
        if (lineIndex < noseFillLines) {
            const t = stepInLine / noseStepsPerFill;
            
            // Distribute lines between baseY and tipY using NoseLength
            const yLevel = baseY + (lineIndex * ((NoseLength * NoseFlip) / Math.max(1, noseFillLines - 1)));
            
            // Calculate proportional width at this height
            const currentHalfW = halfW * (Math.abs(tipY - yLevel) / Math.max(1, NoseLength));

            const xLeft = -currentHalfW;
            const xRight = currentHalfW;

            if (stepInLine === 0) {
                turtle.penup();
                turtle.goto(xLeft, yLevel);
                turtle.pendown();
            } else {
                turtle.goto(xRight, yLevel);
            }
        }
        return true;
    }

    // 3. Draw Mouth
    let mouthIdx = i - ringSteps - noseTotalSteps;
    if (mouthIdx < mouthTotalSteps) {
        const lineIndex = Math.floor(mouthIdx / mouthStepsPerLine);
        const stepInLine = mouthIdx % mouthStepsPerLine;
        const t = stepInLine / mouthStepsPerLine;

        const angle = t * Math.PI; 
        const mx = -MouthWidth / 2 + (t * MouthWidth);
        const my = MouthY - (lineIndex * 2) + Math.sin(angle) * 6;

        if (stepInLine === 0) {
            turtle.penup();
            turtle.goto(mx, my);
            turtle.pendown();
        } else {
            turtle.goto(mx, my);
        }
        return true;
    }

    return false;
}