From 681ead5e331b1609c65e882f6173378ab32c4eb7 Mon Sep 17 00:00:00 2001 From: Mike Parks Date: Fri, 17 May 2024 15:37:37 -0700 Subject: [PATCH] Button layout rectangle rotation (#1015) Added rotation to rectangles using the angleStart property. Renamed GP_SHAPE_DIAMOND to GP_SHAPE_LINE now that rectangles can rotate. --- headers/buttonlayouts.h | 8 +-- headers/display/GPGFX.h | 2 +- headers/interfaces/i2c/displaybase.h | 2 +- headers/interfaces/i2c/ssd1306/obd_ssd1306.h | 2 +- headers/interfaces/i2c/ssd1306/tiny_ssd1306.h | 2 +- proto/enums.proto | 2 +- src/display/GPGFX.cpp | 4 +- src/display/ui/elements/GPButton.cpp | 22 ++----- src/display/ui/elements/GPShape.cpp | 10 +-- src/interfaces/i2c/ssd1306/obd_ssd1306.cpp | 2 +- src/interfaces/i2c/ssd1306/tiny_ssd1306.cpp | 65 ++++++++++++++++--- 11 files changed, 75 insertions(+), 46 deletions(-) diff --git a/headers/buttonlayouts.h b/headers/buttonlayouts.h index 13fe98f44..4b7bee0dc 100644 --- a/headers/buttonlayouts.h +++ b/headers/buttonlayouts.h @@ -100,10 +100,10 @@ } #define BUTTON_GROUP_KEYBOARD_ANGLED {\ - {GP_ELEMENT_DIR_BUTTON, {8, 37, 7, 7, 1, 1, GAMEPAD_MASK_LEFT, GP_SHAPE_DIAMOND}},\ - {GP_ELEMENT_DIR_BUTTON, {23, 24, 7, 7, 1, 1, GAMEPAD_MASK_UP, GP_SHAPE_DIAMOND}},\ - {GP_ELEMENT_DIR_BUTTON, {23, 50, 7, 7, 1, 1, GAMEPAD_MASK_DOWN, GP_SHAPE_DIAMOND}},\ - {GP_ELEMENT_DIR_BUTTON, {37, 37, 7, 7, 1, 1, GAMEPAD_MASK_RIGHT, GP_SHAPE_DIAMOND}}\ + {GP_ELEMENT_DIR_BUTTON, {8, 37, 16, 45, 1, 1, GAMEPAD_MASK_LEFT, GP_SHAPE_SQUARE, 45}},\ + {GP_ELEMENT_DIR_BUTTON, {23, 24, 31, 32, 1, 1, GAMEPAD_MASK_UP, GP_SHAPE_SQUARE, 45}},\ + {GP_ELEMENT_DIR_BUTTON, {23, 50, 31, 58, 1, 1, GAMEPAD_MASK_DOWN, GP_SHAPE_SQUARE, 45}},\ + {GP_ELEMENT_DIR_BUTTON, {37, 37, 45, 45, 1, 1, GAMEPAD_MASK_RIGHT, GP_SHAPE_SQUARE, 45}}\ } #define BUTTON_GROUP_VEWLIX {\ diff --git a/headers/display/GPGFX.h b/headers/display/GPGFX.h index c944bd89f..94a9cd8e3 100644 --- a/headers/display/GPGFX.h +++ b/headers/display/GPGFX.h @@ -23,7 +23,7 @@ class GPGFX { void drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint32_t color, uint8_t filled); void drawArc(uint16_t x, uint16_t y, uint32_t radiusX, uint32_t radiusY, uint32_t color, uint8_t filled, double startAngle, double endAngle, uint8_t closed); void drawEllipse(uint16_t x, uint16_t y, uint32_t radiusX, uint32_t radiusY, uint32_t color, uint8_t filled); - void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled); + void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled, double rotationAngle = 0); void drawPolygon(uint16_t x, uint16_t y, uint16_t radius, uint16_t sides, uint32_t color, uint8_t filled, double rotation = 0); void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority); private: diff --git a/headers/interfaces/i2c/displaybase.h b/headers/interfaces/i2c/displaybase.h index e8d86ddad..9be03fd79 100644 --- a/headers/interfaces/i2c/displaybase.h +++ b/headers/interfaces/i2c/displaybase.h @@ -24,7 +24,7 @@ class GPGFX_DisplayBase { virtual void drawEllipse(uint16_t x, uint16_t y, uint32_t radiusX, uint32_t radiusY, uint32_t color, uint8_t filled) {} - virtual void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled) {} + virtual void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled, double rotationAngle = 0) {} virtual void drawPolygon(uint16_t x, uint16_t y, uint16_t radius, uint16_t sides, uint32_t color, uint8_t filled, double rotation = 0) {} diff --git a/headers/interfaces/i2c/ssd1306/obd_ssd1306.h b/headers/interfaces/i2c/ssd1306/obd_ssd1306.h index 796a3ea2a..ee8f80aac 100644 --- a/headers/interfaces/i2c/ssd1306/obd_ssd1306.h +++ b/headers/interfaces/i2c/ssd1306/obd_ssd1306.h @@ -22,7 +22,7 @@ class GPGFX_OBD_SSD1306 : public GPGFX_DisplayBase { void drawEllipse(uint16_t x, uint16_t y, uint32_t radiusX, uint32_t radiusY, uint32_t color, uint8_t filled); - void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled); + void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled, double rotationAngle = 0); void drawSprite(uint8_t* spriteData, uint16_t width, uint16_t height, uint16_t pitch, uint16_t x, uint16_t y, uint8_t priority); diff --git a/headers/interfaces/i2c/ssd1306/tiny_ssd1306.h b/headers/interfaces/i2c/ssd1306/tiny_ssd1306.h index 31479a37a..ff5b57279 100644 --- a/headers/interfaces/i2c/ssd1306/tiny_ssd1306.h +++ b/headers/interfaces/i2c/ssd1306/tiny_ssd1306.h @@ -23,7 +23,7 @@ class GPGFX_TinySSD1306 : public GPGFX_DisplayBase { void drawEllipse(uint16_t x, uint16_t y, uint32_t radiusX, uint32_t radiusY, uint32_t color, uint8_t filled); - void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled); + void drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled, double rotationAngle = 0); void drawPolygon(uint16_t x, uint16_t y, uint16_t radius, uint16_t sides, uint32_t color, uint8_t filled, double rotation = 0); diff --git a/proto/enums.proto b/proto/enums.proto index 45904a9e6..648e57dcf 100644 --- a/proto/enums.proto +++ b/proto/enums.proto @@ -352,7 +352,7 @@ enum GPShape_Type GP_SHAPE_ELLIPSE = 0; GP_SHAPE_SQUARE = 1; - GP_SHAPE_DIAMOND = 2; + GP_SHAPE_LINE = 2; GP_SHAPE_POLYGON = 3; GP_SHAPE_ARC = 4; }; diff --git a/src/display/GPGFX.cpp b/src/display/GPGFX.cpp index cc6122ebc..0373c5a2c 100644 --- a/src/display/GPGFX.cpp +++ b/src/display/GPGFX.cpp @@ -63,8 +63,8 @@ void GPGFX::drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint32_ this->displayDriver->drawLine(x1, y1, x2, y2, color, filled); } -void GPGFX::drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled) { - this->displayDriver->drawRectangle(x, y, width, height, color, filled); +void GPGFX::drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled, double rotationAngle) { + this->displayDriver->drawRectangle(x, y, width, height, color, filled, rotationAngle); } void GPGFX::drawPolygon(uint16_t x, uint16_t y, uint16_t radius, uint16_t sides, uint32_t color, uint8_t filled, double rotation) { diff --git a/src/display/ui/elements/GPButton.cpp b/src/display/ui/elements/GPButton.cpp index 05bb7dd98..e0ae6c5e6 100644 --- a/src/display/ui/elements/GPButton.cpp +++ b/src/display/ui/elements/GPButton.cpp @@ -128,26 +128,12 @@ void GPButton::draw() { uint16_t turboX = baseX + (width - turboW) / 2; uint16_t turboY = baseY + (height - turboH) / 2; - getRenderer()->drawRectangle(baseX, baseY, sizeX+offsetX, sizeY, this->strokeColor, state); - if (this->_inputType == GP_ELEMENT_BTN_BUTTON && (getGamepad()->turboState.buttons & this->_inputMask)) { - getRenderer()->drawRectangle(turboX, turboY, turboX+turboW, turboY+turboH, 1, 0); - } - } else if (this->_shape == GP_SHAPE_DIAMOND) { - uint16_t size = (this->_sizeX) * scaleX + this->getViewport().left; - if (state) { - int i; - for (i = 0; i < size; i++) { - getRenderer()->drawLine(baseX - i, baseY - size + i, baseX + i, baseY - size + i, this->strokeColor, 0); - getRenderer()->drawLine(baseX - i, baseY + size - i, baseX + i, baseY + size - i, this->strokeColor, 0); - } - getRenderer()->drawLine(baseX - size, baseY, baseX + size, baseY, this->strokeColor, 0); // Fill in the middle - } - getRenderer()->drawLine(baseX - size, baseY, baseX, baseY - size, this->strokeColor, 0); - getRenderer()->drawLine(baseX, baseY - size, baseX + size, baseY, this->strokeColor, 0); - getRenderer()->drawLine(baseX + size, baseY, baseX, baseY + size, this->strokeColor, 0); - getRenderer()->drawLine(baseX, baseY + size, baseX - size, baseY, this->strokeColor, 0); + getRenderer()->drawRectangle(baseX, baseY, sizeX+offsetX, sizeY, this->strokeColor, state, this->_angle); if (this->_inputType == GP_ELEMENT_BTN_BUTTON && (getGamepad()->turboState.buttons & this->_inputMask)) { + getRenderer()->drawRectangle(turboX, turboY, turboX+turboW, turboY+turboH, 1, 0, this->_angle); } + } else if (this->_shape == GP_SHAPE_LINE) { + getRenderer()->drawLine(baseX, baseY, this->_sizeX, this->_sizeY, this->strokeColor, 0); } else if (this->_shape == GP_SHAPE_POLYGON) { uint16_t scaledSize = (uint16_t)((double)this->_sizeX * scaleX); uint16_t baseRadius = (uint16_t)scaledSize; diff --git a/src/display/ui/elements/GPShape.cpp b/src/display/ui/elements/GPShape.cpp index fb7d7b342..2973689f2 100644 --- a/src/display/ui/elements/GPShape.cpp +++ b/src/display/ui/elements/GPShape.cpp @@ -38,13 +38,9 @@ void GPShape::draw() { uint16_t width = this->_sizeX - baseX; uint16_t height = this->_sizeY - baseY; - getRenderer()->drawRectangle(baseX, baseY, sizeX+offsetX, sizeY, this->strokeColor, this->fillColor); - } else if (this->_shape == GP_SHAPE_DIAMOND) { - uint16_t size = (this->_sizeX) * scaleX + this->getViewport().left; - getRenderer()->drawLine(baseX - size, baseY, baseX, baseY - size, this->strokeColor, 0); - getRenderer()->drawLine(baseX, baseY - size, baseX + size, baseY, this->strokeColor, 0); - getRenderer()->drawLine(baseX + size, baseY, baseX, baseY + size, this->strokeColor, 0); - getRenderer()->drawLine(baseX, baseY + size, baseX - size, baseY, this->strokeColor, 0); + getRenderer()->drawRectangle(baseX, baseY, sizeX+offsetX, sizeY, this->strokeColor, this->fillColor, this->_angle); + } else if (this->_shape == GP_SHAPE_LINE) { + getRenderer()->drawLine(baseX, baseY, this->_sizeX, this->_sizeY, this->strokeColor, 0); } else if (this->_shape == GP_SHAPE_POLYGON) { uint16_t scaledSize = (uint16_t)((double)this->_sizeX * scaleX); uint16_t baseRadius = (uint16_t)scaledSize; diff --git a/src/interfaces/i2c/ssd1306/obd_ssd1306.cpp b/src/interfaces/i2c/ssd1306/obd_ssd1306.cpp index 6b1ffc35b..759353c6e 100644 --- a/src/interfaces/i2c/ssd1306/obd_ssd1306.cpp +++ b/src/interfaces/i2c/ssd1306/obd_ssd1306.cpp @@ -58,7 +58,7 @@ void GPGFX_OBD_SSD1306::drawEllipse(uint16_t x, uint16_t y, uint32_t radiusX, ui obdPreciseEllipse(&obd, x, y, radiusX, radiusY, color, filled); } -void GPGFX_OBD_SSD1306::drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled) { +void GPGFX_OBD_SSD1306::drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled, double rotationAngle) { obdRectangle(&obd, x, y, width, height, color, filled); } diff --git a/src/interfaces/i2c/ssd1306/tiny_ssd1306.cpp b/src/interfaces/i2c/ssd1306/tiny_ssd1306.cpp index 65e8ce50d..e16ad5ba7 100644 --- a/src/interfaces/i2c/ssd1306/tiny_ssd1306.cpp +++ b/src/interfaces/i2c/ssd1306/tiny_ssd1306.cpp @@ -274,17 +274,64 @@ void GPGFX_TinySSD1306::drawEllipse(uint16_t x, uint16_t y, uint32_t radiusX, ui } } -void GPGFX_TinySSD1306::drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled) { - //printf("Rect %d, %d, %d, %d, %d, %d\n", x, y, width, height, color, filled); - drawLine(x, y, x, height, color, filled); - drawLine(x, y, width, y, color, filled); - drawLine(width, height, x, height, color, filled); - drawLine(width, height, width, y, color, filled); +void GPGFX_TinySSD1306::drawRectangle(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color, uint8_t filled, double rotationAngle) { + // Calculate center point of the rectangle + double centerX = (x + width) / 2.0; + double centerY = (y + height) / 2.0; + + // Calculate half width and half height for easier calculations + double halfWidth = (width - x) / 2.0; + double halfHeight = (height - y) / 2.0; + + // Convert rotation angle to radians + double angleRad = rotationAngle * M_PI / 180.0; + + // Pre-calculate sine and cosine of the rotation angle + double cosA = cos(angleRad); + double sinA = sin(angleRad); + + // Calculate rotated coordinates for each corner of the rectangle + double x0 = centerX + cosA * (-halfWidth) - sinA * (-halfHeight); + double y0 = centerY + sinA * (-halfWidth) + cosA * (-halfHeight); + + double x1 = centerX + cosA * (halfWidth) - sinA * (-halfHeight); + double y1 = centerY + sinA * (halfWidth) + cosA * (-halfHeight); + + double x2 = centerX + cosA * (halfWidth) - sinA * (halfHeight); + double y2 = centerY + sinA * (halfWidth) + cosA * (halfHeight); + + double x3 = centerX + cosA * (-halfWidth) - sinA * (halfHeight); + double y3 = centerY + sinA * (-halfWidth) + cosA * (halfHeight); + + // Round coordinates to nearest integer + uint16_t x0_rounded = (uint16_t)round(x0); + uint16_t y0_rounded = (uint16_t)round(y0); + uint16_t x1_rounded = (uint16_t)round(x1); + uint16_t y1_rounded = (uint16_t)round(y1); + uint16_t x2_rounded = (uint16_t)round(x2); + uint16_t y2_rounded = (uint16_t)round(y2); + uint16_t x3_rounded = (uint16_t)round(x3); + uint16_t y3_rounded = (uint16_t)round(y3); + + // Draw lines between rotated coordinates + drawLine(x0_rounded, y0_rounded, x1_rounded, y1_rounded, color, filled); + drawLine(x1_rounded, y1_rounded, x2_rounded, y2_rounded, color, filled); + drawLine(x2_rounded, y2_rounded, x3_rounded, y3_rounded, color, filled); + drawLine(x3_rounded, y3_rounded, x0_rounded, y0_rounded, color, filled); if (filled) { - for (uint8_t i = 1; i < (height-y); i++) { - drawLine(x, y+i, width, y+i, color, filled); - } + // Calculate the number of lines needed for the filling + uint16_t numLines = (uint16_t)round(sqrt(halfWidth * halfWidth + halfHeight * halfHeight) * 2); + + for (uint16_t i = 0; i <= numLines; i++) { + double t = (double)i / numLines; + double xStart = (1 - t) * x0 + t * x3; + double yStart = (1 - t) * y0 + t * y3; + double xEnd = (1 - t) * x1 + t * x2; + double yEnd = (1 - t) * y1 + t * y2; + + drawLine((uint16_t)round(xStart), (uint16_t)round(yStart), (uint16_t)round(xEnd), (uint16_t)round(yEnd), color, filled); + } } }