feat(ui): implementar renderizado identico de la pantalla de 4 cuadrantes en la transicion con iconos PID, 20 bloques en arco, degradado de fondo y badge ECO

This commit is contained in:
Sebastián Nieto 2026-09-22 00:25:46 -03:00
parent bd62cb991d
commit be9687c845

View file

@ -1236,9 +1236,49 @@ void plotNoData(uint8_t cell) {
}
}
// Draw parameter icon into sprite slice with true alpha channel
inline void drawPidIconToSprite(TFT_eSprite &s, int ix, int iy, const String &pidStr, int slice_h) {
int w = 42, h = 42;
const unsigned short *colors = nullptr;
const unsigned char *alphas = nullptr;
if (pidStr == "0105") {
w = 38; h = 37;
colors = icon_temp_quad; alphas = icon_temp_quad_alpha;
} else if (pidStr == "010D") {
w = 42; h = 42;
colors = icon_speed_quad; alphas = icon_speed_quad_alpha;
} else if (pidStr == "010C") {
w = 42; h = 43;
colors = icon_rpm_quad; alphas = icon_rpm_quad_alpha;
} else if (pidStr == "0142" || pidStr == "ATRV") {
w = 46; h = 35;
colors = icon_battery_quad; alphas = icon_battery_quad_alpha;
}
if (!colors || !alphas) return;
int startJ = (iy < 0) ? -iy : 0;
int endJ = (iy + h > slice_h) ? (slice_h - iy) : h;
if (startJ >= endJ) return;
for (int j = startJ; j < endJ; j++) {
int curY = iy + j;
int rowOffset = j * w;
for (int i = 0; i < w; i++) {
int curX = ix + i;
if (curX < 0 || curX >= 320) continue;
uint8_t a = pgm_read_byte(&alphas[rowOffset + i]);
if (a >= 30) {
uint16_t fg = pgm_read_word(&colors[rowOffset + i]);
s.drawPixel(curX, curY, fg);
}
}
}
}
// Render Page 0 (4 Quadrants) into double-buffered Sprite slice
inline void renderPage0ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int slice_h) {
uint16_t bgCol = getThemeBgColor();
checkUpdateThemeGradients();
uint16_t divCol = getThemeDivColor();
uint16_t priCol = getThemePrimaryColor();
@ -1250,37 +1290,71 @@ inline void renderPage0ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s
if (qx + 160 <= 0 || qx >= 320 || ry + 119 <= 0 || ry >= slice_h) continue;
s.fillRect(rx, ry, 159, 119, bgCol);
// 1. Exact dark vignette gradient matching real arcMeter4
s.fillRect(rx, ry, 159, 119, TFT_BLACK);
int gradStartY = (ry < 0) ? -ry : 0;
int gradEndY = (ry + 82 > slice_h) ? (slice_h - ry) : 82;
for (int j = gradStartY; j < gradEndY; j++) {
int curY = ry + j;
if (curY >= 0 && curY < slice_h) {
s.drawFastHLine(rx, curY, 159, themeGradQuad[j]);
}
}
uint8_t pidIdx = pidInCell[c];
s.setTextColor(TFT_WHITE, bgCol);
s.drawString(pidConfig[pidIdx][0].c_str(), qx + 8, qy + 6, 2);
// 2. Real high-definition parameter icon (thermometer, speedometer, tachometer, battery)
drawPidIconToSprite(s, qx + 5, qy + 3, pidConfig[pidIdx][2], slice_h);
// 3. Exact 20 curved segmented gauge blocks along the arc
int warn = warningValue[pidIdx].toInt();
int min = pidConfig[pidIdx][4].toInt();
int max = pidConfig[pidIdx][5].toInt();
bool digit = pidConfig[pidIdx][7].toInt();
float val = last_valid_data[c];
int barY = qy + 28;
s.fillRoundRect(qx + 8, barY, 144, 6, 2, METER_INACTIVE_COLOR);
if (val > -900.0f) {
uint16_t col = getMeterColor(pidIdx, val, warn);
float ratio = (val - (float)min) / (float)(max - min);
if (ratio < 0.0f) ratio = 0.0f;
if (ratio > 1.0f) ratio = 1.0f;
int fillW = (int)(ratio * 144.0f);
if (fillW < 4) fillW = 4;
if (fillW > 144) fillW = 144;
s.fillRoundRect(qx + 8, barY, fillW, 6, 2, col);
int numActive = (val > -900.0f) ? calcMeterBlocks(val, min, max) : 0;
uint16_t activeCol = (val > -900.0f) ? getMeterColor(pidIdx, val, warn) : priCol;
for (uint8_t i = 0; i < NUM_BAR_BLOCKS; i++) {
int bx = qx + pgm_read_word(&quadBlockMinX[i]);
int by = qy + pgm_read_word(&quadBlockMinY[i]);
int bw = pgm_read_word(&quadBlockMaxX[i]) - pgm_read_word(&quadBlockMinX[i]) + 1;
int bh = pgm_read_word(&quadBlockMaxY[i]) - pgm_read_word(&quadBlockMinY[i]) + 1;
if (by + bh <= 0 || by >= slice_h || bx + bw <= 0 || bx >= 320) continue;
uint16_t bCol = (i < numActive) ? activeCol : METER_INACTIVE_COLOR;
s.fillRoundRect(bx, by, bw, bh, 2, bCol);
}
// 4. Digital value string
String numStr = (val <= -900.0f) ? "---" : String(val, digit);
s.setTextColor(priCol, bgCol);
s.setTextColor(priCol);
s.drawRightString(numStr.c_str(), qx + 152, qy + 74, 4);
}
// Crosshair dividers
// 5. Eco Badge if active
if (isEcoOptimal) {
int rpmCell = 2;
for (int c = 0; c < 4; c++) {
if (pidConfig[pidInCell[c]][3].toInt() == 3) {
rpmCell = c;
break;
}
}
int bx = cell4_x[rpmCell] + 100 + x_offset;
int by = cell4_y[rpmCell] + 4 - y_slice;
int bw = 48, bh = 18;
if (by + bh > 0 && by < slice_h && bx + bw > 0 && bx < 320) {
s.fillRoundRect(bx, by, bw, bh, 4, 0x0202);
s.drawRoundRect(bx, by, bw, bh, 4, 0x07E0);
s.setTextColor(TFT_WHITE, 0x0202);
s.drawCentreString("ECO", bx + (bw / 2), by + 1, 2);
}
}
// 6. Crosshair dividers
s.fillRect(159 + x_offset, 0, 2, slice_h, divCol);
int hLineY = 119 - y_slice;
if (hLineY >= 0 && hLineY < slice_h) {