#pragma once #include #include "config.h" // --------------------------------------------------------------------------- // Logo polygon data — design canvas 107 × 125 units // --------------------------------------------------------------------------- static const int kGreenPts[][2] = { { 20, 92}, // left leg inner, mid-height { 20, 39}, // left leg inner, upper { 54, 66}, // centre V, bottom {107, 24}, // right leg, upper {107, 1}, // right leg, top { 54, 42}, // centre V, top peak { 1, 2}, // left leg, top-left { 1, 107}, // left leg, bottom-left // Z closes back to {20, 92} }; static const int kBluePts[][2] = { { 1, 125}, { 31, 125}, { 89, 79}, { 89, 107}, { 76, 107}, { 76, 125}, {107, 125}, {107, 42} }; static constexpr int LOGO_N = 8; static constexpr float LOGO_SCALE = 1.2f; static constexpr int LOGO_X_OFF = 55; static constexpr int LOGO_Y_OFF = 45; static inline int lsx(int x) { return LOGO_X_OFF + (int)(x * LOGO_SCALE); } static inline int lsy(int y) { return LOGO_Y_OFF + (int)(y * LOGO_SCALE); } // --------------------------------------------------------------------------- // RGB565 colour helpers // --------------------------------------------------------------------------- static inline uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) { return ((uint16_t)(r & 0xF8) << 8) | ((uint16_t)(g & 0xFC) << 3) | (b >> 3); } static inline uint16_t logoGreenAt(float t) { return rgb565(0, (uint8_t)(191.0f * t), (uint8_t)(99.0f * t)); } static inline uint16_t logoBlueAt(float t) { return rgb565((uint8_t)(17.0f * t), (uint8_t)(67.0f * t), (uint8_t)(120.0f * t)); } // --------------------------------------------------------------------------- // Scanline polygon fill — operates in screen coordinates // --------------------------------------------------------------------------- static void outlinePolygon(TFT_eSPI &tft, int pts[][2], int n, uint16_t colour) { for (int i = 0; i < n; i++) { int j = (i + 1) % n; tft.drawLine(pts[i][0], pts[i][1], pts[j][0], pts[j][1], colour); } } static void fillPolygon(TFT_eSPI &tft, int pts[][2], int n, uint16_t colour) { int yMin = pts[0][1], yMax = pts[0][1]; for (int i = 1; i < n; i++) { if (pts[i][1] < yMin) yMin = pts[i][1]; if (pts[i][1] > yMax) yMax = pts[i][1]; } int nodeX[16]; for (int y = yMin; y <= yMax; y++) { int nodes = 0; int j = n - 1; for (int i = 0; i < n; i++) { int yi = pts[i][1], yj = pts[j][1]; // Key fix: use strictly-one-side rule to avoid double-counting vertices if ((yi <= y && yj > y) || (yj <= y && yi > y)) { long num = (long)(y - yi) * (pts[j][0] - pts[i][0]); long dy = yj - yi; nodeX[nodes++] = pts[i][0] + (int)(num / dy); } j = i; } // Insertion sort for (int a = 1; a < nodes; a++) { int key = nodeX[a], b = a - 1; while (b >= 0 && nodeX[b] > key) { nodeX[b + 1] = nodeX[b--]; } nodeX[b + 1] = key; } for (int a = 0; a + 1 < nodes; a += 2) { tft.drawFastHLine(nodeX[a], y, nodeX[a + 1] - nodeX[a] + 1, colour); } } } // Scale design-space polygon into screen-space once static void buildScaledLogo(int greenOut[][2], int blueOut[][2]) { for (int i = 0; i < LOGO_N; i++) { greenOut[i][0] = lsx(kGreenPts[i][0]); greenOut[i][1] = lsy(kGreenPts[i][1]); blueOut[i][0] = lsx(kBluePts[i][0]); blueOut[i][1] = lsy(kBluePts[i][1]); } } // --------------------------------------------------------------------------- // Animated logo — total duration ~10 s // 0.0 – 1.0 s green shape fades in // 1.0 – 2.0 s blue shape fades in, green holds solid // 2.0 – 10.0 s logo remains on screen // --------------------------------------------------------------------------- inline void drawLogo(TFT_eSPI &tft) { static int sGreen[LOGO_N][2]; static int sBlue[LOGO_N][2]; static bool scaled = false; if (!scaled) { buildScaledLogo(sGreen, sBlue); scaled = true; } tft.fillScreen(TFT_BLACK); // Phase 1: green fade-in for (int i = 0; i <= LOGO_FADE_STEPS; i++) { outlinePolygon(tft, sGreen, LOGO_N, logoGreenAt(i / (float)LOGO_FADE_STEPS)); delay(LOGO_FADE_STEP_MS); } // Phase 2: blue fade-in (green redrawn solid to prevent bleed-through) for (int i = 0; i <= LOGO_FADE_STEPS; i++) { outlinePolygon(tft, sGreen, LOGO_N, logoGreenAt(1.0f)); fillPolygon(tft, sBlue, LOGO_N, logoBlueAt(i / (float)LOGO_FADE_STEPS)); delay(LOGO_FADE_STEP_MS); } // Phase 3: static hold delay(LOGO_STATIC_MS); }