diff --git a/googly/googly.ino b/googly/googly.ino index a03500d..91e1f36 100644 --- a/googly/googly.ino +++ b/googly/googly.ino @@ -87,16 +87,59 @@ void drawEyes(int8_t lid, int8_t pdx, int8_t pdy) { cur.lid = lid; cur.pdx = pdx; cur.pdy = pdy; } +// Delta redraw: erase old pupil with sclera, draw new. No full-eye repaint, +// no flicker. Pupil range + radius never reaches the sclera edge, so the +// erase circle can't bleed outside the eye. +void movePupils(int8_t pdx, int8_t pdy) { + const int cxs[2] = {EYE_LX, EYE_RX}; + for (uint8_t i = 0; i < 2; i++) { + int cx = cxs[i]; + tft.fillCircle(cx + cur.pdx, EYE_Y + cur.pdy, PUPIL_R + 2, SCLERA); + tft.fillCircle(cx + pdx, EYE_Y + pdy, PUPIL_R, PUPIL); + tft.fillCircle(cx + pdx - 8, EYE_Y + pdy - 8, 7, SCLERA); + if (cur.lid > 0) { + int h = (int32_t)cur.lid * 2 * EYE_R / 100; + tft.fillRect(cx - EYE_R, EYE_Y - EYE_R, 2 * EYE_R + 1, h, BG); + } + } + cur.pdx = pdx; cur.pdy = pdy; +} + +// Incremental lids: closing = paint one BG rect over the top of the eye; +// opening = reveal the exposed band with sclera chords, then restore pupil. +void setLids(int8_t pct) { + int newH = (int32_t)pct * 2 * EYE_R / 100; + int oldH = (int32_t)cur.lid * 2 * EYE_R / 100; + const int cxs[2] = {EYE_LX, EYE_RX}; + for (uint8_t i = 0; i < 2; i++) { + int cx = cxs[i]; + if (newH > oldH) { + tft.fillRect(cx - EYE_R, EYE_Y - EYE_R + oldH, 2 * EYE_R + 1, newH - oldH, BG); + } else if (newH < oldH) { + for (int y = EYE_Y - EYE_R + newH; y < EYE_Y - EYE_R + oldH; y++) { + int dy = y - EYE_Y; + int w = (int)sqrt((float)(EYE_R * EYE_R - dy * dy)); + tft.drawFastHLine(cx - w, y, 2 * w + 1, SCLERA); + } + tft.fillCircle(cx + cur.pdx, EYE_Y + cur.pdy, PUPIL_R, PUPIL); + tft.fillCircle(cx + cur.pdx - 8, EYE_Y + cur.pdy - 8, 7, SCLERA); + if (newH > 0) + tft.fillRect(cx - EYE_R, EYE_Y - EYE_R, 2 * EYE_R + 1, newH, BG); + } + } + cur.lid = pct; +} + void drawMouth(int8_t curve, int8_t openR) { - tft.fillRect(MOUTH_X - MOUTH_HW - 8, MOUTH_Y - 30, 2 * (MOUTH_HW + 8), 64, BG); + tft.fillRect(MOUTH_X - MOUTH_HW - 8, MOUTH_Y - 38, 2 * (MOUTH_HW + 8), 80, BG); if (openR > 0) { tft.fillCircle(MOUTH_X, MOUTH_Y, openR, PUPIL); tft.drawCircle(MOUTH_X, MOUTH_Y, openR, SCLERA); tft.drawCircle(MOUTH_X, MOUTH_Y, openR + 1, SCLERA); } else { for (int x = -MOUTH_HW; x <= MOUTH_HW; x += 4) { - int y = MOUTH_Y - (int32_t)curve * (20 - (x * x) / 180) / 100; - tft.fillCircle(MOUTH_X + x, y, 4, SCLERA); + int y = MOUTH_Y - (int32_t)curve * (32 - (x * x) / 113) / 100; + tft.fillCircle(MOUTH_X + x, y, 5, SCLERA); } } cur.curve = curve; cur.openR = openR; @@ -133,7 +176,7 @@ void setFace(int8_t lid, int8_t pdx, int8_t pdy, int8_t curve, int8_t openR, boo if (blush != cur.blush) drawBlush(blush); } -int8_t moodCurve() { return pet.mood > 66 ? 80 : (pet.mood > 33 ? 30 : -40); } +int8_t moodCurve() { return pet.mood > 66 ? 100 : (pet.mood > 33 ? 70 : -50); } int8_t moodLid() { return pet.mood > 66 ? 0 : (pet.mood > 33 ? 10 : 30); } void clearZs() { tft.fillRect(380, 20, 100, 90, BG); } @@ -182,9 +225,9 @@ void bumpMood(int8_t d) { void blinkOnce() { int8_t base = moodLid(); - const int8_t seq[3] = {60, 100, 60}; - for (uint8_t i = 0; i < 3; i++) { drawEyes(seq[i], cur.pdx, cur.pdy); delay(35); } - drawEyes(base, cur.pdx, cur.pdy); + setLids(100); + delay(90); + setLids(base); } void wakeAnim() { @@ -214,7 +257,7 @@ void dizzyAnim() { drawMouth(0, 10); for (int a = 0; a < 720; a += 40) { float r = a * 0.01745; - drawEyes(0, (int8_t)(PUPIL_RANGE * cos(r)), (int8_t)(PUPIL_RANGE * sin(r))); + movePupils((int8_t)(PUPIL_RANGE * cos(r)), (int8_t)(PUPIL_RANGE * sin(r))); delay(28); } drawEyes(30, 0, 8); drawMouth(-30, 0); delay(500); @@ -223,9 +266,10 @@ void dizzyAnim() { void yawnAnim() { Serial.println(F("yawn")); + setLids(50); const int8_t seq[8] = {8, 14, 20, 24, 24, 20, 12, 0}; for (uint8_t i = 0; i < 8; i++) { - drawMouth(0, seq[i]); drawEyes(50, cur.pdx, cur.pdy); delay(140); + drawMouth(0, seq[i]); delay(140); } setFace(moodLid(), cur.pdx, cur.pdy, moodCurve(), 0, false); } @@ -251,7 +295,7 @@ void flyAnim() { tft.fillCircle(fx, fy, 3, FLYCLR); px = fx; py = fy; int8_t pdx = constrain((fx - MOUTH_X) / 8, -PUPIL_RANGE, PUPIL_RANGE); - if (pdx != cur.pdx) drawEyes(0, pdx, -14); + if (pdx != cur.pdx) movePupils(pdx, -14); delay(30); if (touched()) break; // a poke scares the fly off } @@ -352,7 +396,7 @@ void loop() { } else { int8_t sx = constrain(targPdx - cur.pdx, -6, 6); int8_t sy = constrain(targPdy - cur.pdy, -6, 6); - drawEyes(cur.lid, cur.pdx + sx, cur.pdy + sy); + movePupils(cur.pdx + sx, cur.pdy + sy); nextWanderMs = now + 60; } }