diff --git a/sketch/obd2_gauge/meter.h b/sketch/obd2_gauge/meter.h index 1a5577a..b2cc95b 100644 --- a/sketch/obd2_gauge/meter.h +++ b/sketch/obd2_gauge/meter.h @@ -1276,6 +1276,59 @@ inline void drawPidIconToSprite(TFT_eSprite &s, int ix, int iy, const String &pi } } +// Draw an individual solid curved block into sprite slice with subpixel anti-aliasing +inline void drawQuadBarBlockToSprite(TFT_eSprite &s, int16_t qx, int16_t qy, uint8_t i, uint16_t color, int slice_h) { + int16_t minX = pgm_read_word(&quadBlockMinX[i]); + int16_t maxX = pgm_read_word(&quadBlockMaxX[i]); + int16_t minY = pgm_read_word(&quadBlockMinY[i]); + int16_t maxY = pgm_read_word(&quadBlockMaxY[i]); + + if (qx + maxX < 0 || qx + minX >= 320) return; + if (qy + maxY < 0 || qy + minY >= slice_h) return; + + int16_t startY = minY; + int16_t endY = maxY; + if (qy + startY < 0) startY = -qy; + if (qy + endY >= slice_h) endY = slice_h - 1 - qy; + + int16_t startX = minX; + int16_t endX = maxX; + if (qx + startX < 0) startX = -qx; + if (qx + endX >= 320) endX = 319 - qx; + + float nx[4], ny[4], c[4]; + for (uint8_t k = 0; k < 4; k++) { + nx[k] = pgm_read_float(&quadBlockNx[i][k]); + ny[k] = pgm_read_float(&quadBlockNy[i][k]); + c[k] = pgm_read_float(&quadBlockC[i][k]); + } + + for (int16_t y = startY; y <= endY; y++) { + float py = (float)y + 0.5f; + int16_t sy = qy + y; + for (int16_t x = startX; x <= endX; x++) { + float px = (float)x + 0.5f; + float min_d = 999.0f; + for (uint8_t k = 0; k < 4; k++) { + float d = px * nx[k] + py * ny[k] + c[k]; + if (d < min_d) min_d = d; + } + if (min_d <= -0.5f) continue; + int16_t sx = qx + x; + if (color == TFT_BLACK) { + s.drawPixel(sx, sy, TFT_BLACK); + } else { + if (min_d >= 0.5f) { + s.drawPixel(sx, sy, color); + } else { + uint8_t alpha = (uint8_t)((min_d + 0.5f) * 255.0f); + s.drawPixel(sx, sy, fastBlend(alpha, color, (uint16_t)TFT_BLACK)); + } + } + } + } +} + // 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) { checkUpdateThemeGradients(); @@ -1306,7 +1359,7 @@ inline void renderPage0ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s // 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 + // 3. Exact 20 curved segmented gauge blocks along the arc (true mathematical polygons) int warn = warningValue[pidIdx].toInt(); int min = pidConfig[pidIdx][4].toInt(); int max = pidConfig[pidIdx][5].toInt(); @@ -1317,15 +1370,8 @@ inline void renderPage0ToSprite(TFT_eSprite &s, int x_offset, int y_slice, int s 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); + drawQuadBarBlockToSprite(s, qx, qy, i, bCol, slice_h); } // 4. Digital value string