fix: flicker-free delta redraws for pupils/lids; visible smile curve
Full-eye repaints on every pupil step and blink frame flickered badly on the 8-bit bus. Pupils now erase/redraw only themselves; lids paint incrementally (BG rect closing, sclera chords opening). Mouth curve deepened 20->32px and mood tiers raised so mood ~60 reads as an actual smile instead of a 6px bend. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+55
-11
@@ -87,16 +87,59 @@ void drawEyes(int8_t lid, int8_t pdx, int8_t pdy) {
|
|||||||
cur.lid = lid; cur.pdx = pdx; cur.pdy = 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) {
|
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) {
|
if (openR > 0) {
|
||||||
tft.fillCircle(MOUTH_X, MOUTH_Y, openR, PUPIL);
|
tft.fillCircle(MOUTH_X, MOUTH_Y, openR, PUPIL);
|
||||||
tft.drawCircle(MOUTH_X, MOUTH_Y, openR, SCLERA);
|
tft.drawCircle(MOUTH_X, MOUTH_Y, openR, SCLERA);
|
||||||
tft.drawCircle(MOUTH_X, MOUTH_Y, openR + 1, SCLERA);
|
tft.drawCircle(MOUTH_X, MOUTH_Y, openR + 1, SCLERA);
|
||||||
} else {
|
} else {
|
||||||
for (int x = -MOUTH_HW; x <= MOUTH_HW; x += 4) {
|
for (int x = -MOUTH_HW; x <= MOUTH_HW; x += 4) {
|
||||||
int y = MOUTH_Y - (int32_t)curve * (20 - (x * x) / 180) / 100;
|
int y = MOUTH_Y - (int32_t)curve * (32 - (x * x) / 113) / 100;
|
||||||
tft.fillCircle(MOUTH_X + x, y, 4, SCLERA);
|
tft.fillCircle(MOUTH_X + x, y, 5, SCLERA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cur.curve = curve; cur.openR = openR;
|
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);
|
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); }
|
int8_t moodLid() { return pet.mood > 66 ? 0 : (pet.mood > 33 ? 10 : 30); }
|
||||||
|
|
||||||
void clearZs() { tft.fillRect(380, 20, 100, 90, BG); }
|
void clearZs() { tft.fillRect(380, 20, 100, 90, BG); }
|
||||||
@@ -182,9 +225,9 @@ void bumpMood(int8_t d) {
|
|||||||
|
|
||||||
void blinkOnce() {
|
void blinkOnce() {
|
||||||
int8_t base = moodLid();
|
int8_t base = moodLid();
|
||||||
const int8_t seq[3] = {60, 100, 60};
|
setLids(100);
|
||||||
for (uint8_t i = 0; i < 3; i++) { drawEyes(seq[i], cur.pdx, cur.pdy); delay(35); }
|
delay(90);
|
||||||
drawEyes(base, cur.pdx, cur.pdy);
|
setLids(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wakeAnim() {
|
void wakeAnim() {
|
||||||
@@ -214,7 +257,7 @@ void dizzyAnim() {
|
|||||||
drawMouth(0, 10);
|
drawMouth(0, 10);
|
||||||
for (int a = 0; a < 720; a += 40) {
|
for (int a = 0; a < 720; a += 40) {
|
||||||
float r = a * 0.01745;
|
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);
|
delay(28);
|
||||||
}
|
}
|
||||||
drawEyes(30, 0, 8); drawMouth(-30, 0); delay(500);
|
drawEyes(30, 0, 8); drawMouth(-30, 0); delay(500);
|
||||||
@@ -223,9 +266,10 @@ void dizzyAnim() {
|
|||||||
|
|
||||||
void yawnAnim() {
|
void yawnAnim() {
|
||||||
Serial.println(F("yawn"));
|
Serial.println(F("yawn"));
|
||||||
|
setLids(50);
|
||||||
const int8_t seq[8] = {8, 14, 20, 24, 24, 20, 12, 0};
|
const int8_t seq[8] = {8, 14, 20, 24, 24, 20, 12, 0};
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
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);
|
setFace(moodLid(), cur.pdx, cur.pdy, moodCurve(), 0, false);
|
||||||
}
|
}
|
||||||
@@ -251,7 +295,7 @@ void flyAnim() {
|
|||||||
tft.fillCircle(fx, fy, 3, FLYCLR);
|
tft.fillCircle(fx, fy, 3, FLYCLR);
|
||||||
px = fx; py = fy;
|
px = fx; py = fy;
|
||||||
int8_t pdx = constrain((fx - MOUTH_X) / 8, -PUPIL_RANGE, PUPIL_RANGE);
|
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);
|
delay(30);
|
||||||
if (touched()) break; // a poke scares the fly off
|
if (touched()) break; // a poke scares the fly off
|
||||||
}
|
}
|
||||||
@@ -352,7 +396,7 @@ void loop() {
|
|||||||
} else {
|
} else {
|
||||||
int8_t sx = constrain(targPdx - cur.pdx, -6, 6);
|
int8_t sx = constrain(targPdx - cur.pdx, -6, 6);
|
||||||
int8_t sy = constrain(targPdy - cur.pdy, -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;
|
nextWanderMs = now + 60;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user