logo.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #pragma once
  2. #include <TFT_eSPI.h>
  3. #include "config.h"
  4. // ---------------------------------------------------------------------------
  5. // Logo polygon data — design canvas 107 × 125 units
  6. // ---------------------------------------------------------------------------
  7. static const int kGreenPts[][2] = {
  8. { 20, 92}, // left leg inner, mid-height
  9. { 20, 39}, // left leg inner, upper
  10. { 54, 66}, // centre V, bottom
  11. {107, 24}, // right leg, upper
  12. {107, 1}, // right leg, top
  13. { 54, 42}, // centre V, top peak
  14. { 1, 2}, // left leg, top-left
  15. { 1, 107}, // left leg, bottom-left
  16. // Z closes back to {20, 92}
  17. };
  18. static const int kBluePts[][2] = {
  19. { 1, 125}, { 31, 125}, { 89, 79}, { 89, 107},
  20. { 76, 107}, { 76, 125}, {107, 125}, {107, 42}
  21. };
  22. static constexpr int LOGO_N = 8;
  23. static constexpr float LOGO_SCALE = 1.2f;
  24. static constexpr int LOGO_X_OFF = 55;
  25. static constexpr int LOGO_Y_OFF = 45;
  26. static inline int lsx(int x) { return LOGO_X_OFF + (int)(x * LOGO_SCALE); }
  27. static inline int lsy(int y) { return LOGO_Y_OFF + (int)(y * LOGO_SCALE); }
  28. // ---------------------------------------------------------------------------
  29. // RGB565 colour helpers
  30. // ---------------------------------------------------------------------------
  31. static inline uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) {
  32. return ((uint16_t)(r & 0xF8) << 8) |
  33. ((uint16_t)(g & 0xFC) << 3) |
  34. (b >> 3);
  35. }
  36. static inline uint16_t logoGreenAt(float t) {
  37. return rgb565(0, (uint8_t)(191.0f * t), (uint8_t)(99.0f * t));
  38. }
  39. static inline uint16_t logoBlueAt(float t) {
  40. return rgb565((uint8_t)(17.0f * t), (uint8_t)(67.0f * t), (uint8_t)(120.0f * t));
  41. }
  42. // ---------------------------------------------------------------------------
  43. // Scanline polygon fill — operates in screen coordinates
  44. // ---------------------------------------------------------------------------
  45. static void outlinePolygon(TFT_eSPI &tft, int pts[][2], int n, uint16_t colour) {
  46. for (int i = 0; i < n; i++) {
  47. int j = (i + 1) % n;
  48. tft.drawLine(pts[i][0], pts[i][1], pts[j][0], pts[j][1], colour);
  49. }
  50. }
  51. static void fillPolygon(TFT_eSPI &tft, int pts[][2], int n, uint16_t colour) {
  52. int yMin = pts[0][1], yMax = pts[0][1];
  53. for (int i = 1; i < n; i++) {
  54. if (pts[i][1] < yMin) yMin = pts[i][1];
  55. if (pts[i][1] > yMax) yMax = pts[i][1];
  56. }
  57. int nodeX[16];
  58. for (int y = yMin; y <= yMax; y++) {
  59. int nodes = 0;
  60. int j = n - 1;
  61. for (int i = 0; i < n; i++) {
  62. int yi = pts[i][1], yj = pts[j][1];
  63. // Key fix: use strictly-one-side rule to avoid double-counting vertices
  64. if ((yi <= y && yj > y) || (yj <= y && yi > y)) {
  65. long num = (long)(y - yi) * (pts[j][0] - pts[i][0]);
  66. long dy = yj - yi;
  67. nodeX[nodes++] = pts[i][0] + (int)(num / dy);
  68. }
  69. j = i;
  70. }
  71. // Insertion sort
  72. for (int a = 1; a < nodes; a++) {
  73. int key = nodeX[a], b = a - 1;
  74. while (b >= 0 && nodeX[b] > key) { nodeX[b + 1] = nodeX[b--]; }
  75. nodeX[b + 1] = key;
  76. }
  77. for (int a = 0; a + 1 < nodes; a += 2) {
  78. tft.drawFastHLine(nodeX[a], y, nodeX[a + 1] - nodeX[a] + 1, colour);
  79. }
  80. }
  81. }
  82. // Scale design-space polygon into screen-space once
  83. static void buildScaledLogo(int greenOut[][2], int blueOut[][2]) {
  84. for (int i = 0; i < LOGO_N; i++) {
  85. greenOut[i][0] = lsx(kGreenPts[i][0]);
  86. greenOut[i][1] = lsy(kGreenPts[i][1]);
  87. blueOut[i][0] = lsx(kBluePts[i][0]);
  88. blueOut[i][1] = lsy(kBluePts[i][1]);
  89. }
  90. }
  91. // ---------------------------------------------------------------------------
  92. // Animated logo — total duration ~10 s
  93. // 0.0 – 1.0 s green shape fades in
  94. // 1.0 – 2.0 s blue shape fades in, green holds solid
  95. // 2.0 – 10.0 s logo remains on screen
  96. // ---------------------------------------------------------------------------
  97. inline void drawLogo(TFT_eSPI &tft) {
  98. static int sGreen[LOGO_N][2];
  99. static int sBlue[LOGO_N][2];
  100. static bool scaled = false;
  101. if (!scaled) {
  102. buildScaledLogo(sGreen, sBlue);
  103. scaled = true;
  104. }
  105. tft.fillScreen(TFT_BLACK);
  106. // Phase 1: green fade-in
  107. for (int i = 0; i <= LOGO_FADE_STEPS; i++) {
  108. outlinePolygon(tft, sGreen, LOGO_N, logoGreenAt(i / (float)LOGO_FADE_STEPS));
  109. delay(LOGO_FADE_STEP_MS);
  110. }
  111. // Phase 2: blue fade-in (green redrawn solid to prevent bleed-through)
  112. for (int i = 0; i <= LOGO_FADE_STEPS; i++) {
  113. outlinePolygon(tft, sGreen, LOGO_N, logoGreenAt(1.0f));
  114. fillPolygon(tft, sBlue, LOGO_N, logoBlueAt(i / (float)LOGO_FADE_STEPS));
  115. delay(LOGO_FADE_STEP_MS);
  116. }
  117. // Phase 3: static hold
  118. delay(LOGO_STATIC_MS);
  119. }