Moiréning Face
Vibe turtling with Gemini
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;
}