diff --git a/include/Arduino_Canvas_Graphics2D.h b/include/Arduino_Canvas_Graphics2D.h index fb7214ff0..50230ede3 100644 --- a/include/Arduino_Canvas_Graphics2D.h +++ b/include/Arduino_Canvas_Graphics2D.h @@ -3,6 +3,7 @@ #include #include +#include "config_defaults.h" class Arduino_Canvas_Graphics2D : public Graphics2DPrint { public: @@ -22,12 +23,23 @@ class Arduino_Canvas_Graphics2D : public Graphics2DPrint { * we have copy-pasted this utility together... */ - void begin(int32_t speed = GFX_NOT_DEFINED); - void writePixelPreclipped(int16_t x, int16_t y, uint16_t color); - void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); - void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); void flush(); + inline void begin(int32_t speed = GFX_NOT_DEFINED) { + _output->begin(speed); + // _output->fillScreen(BLACK); + } + + inline void writePixelPreclipped(int16_t x, int16_t y, uint16_t color) { + this->drawPixel(x, y, color); + } + inline void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { + this->drawVLine(x, y, h, color); + } + inline void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { + this->drawHLine(x, y, w, color); + } + protected: Arduino_G* _output; int16_t _output_x, _output_y; diff --git a/include/OswAppV2.h b/include/OswAppV2.h index 744086f44..5dabe583b 100644 --- a/include/OswAppV2.h +++ b/include/OswAppV2.h @@ -56,7 +56,7 @@ class OswAppV2 { }; // We intentionally do not provide an operation to implicitly convert to OswHal* to prevent accidental use of the wrong instance }; - OswHalProxy hal; // You guys are needing that anyways (but you often cache incorrectly), so it is now given to you <3 + OswHalProxy hal; // You guys are needing that anyway (but you often cache incorrectly), so it is now given to you <3 class OswUiProxy { public: OswUI* operator->() { diff --git a/include/apps/games/brick_breaker.h b/include/apps/games/brick_breaker.h index b0f3047d1..4f74f0b08 100644 --- a/include/apps/games/brick_breaker.h +++ b/include/apps/games/brick_breaker.h @@ -29,8 +29,8 @@ class OswAppBrickBreaker : public OswApp { const int playerY = 184; const int gridW = 8; const int gridH = 4; - const float xSensitivity = 0.75; - const float ySensitivity = 0.75; + const float xSensitivity = 0.75f; + const float ySensitivity = 0.75f; const bool newGrid[4][8] = { {0, 0, 1, 1, 1, 1, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1} }; @@ -41,20 +41,20 @@ class OswAppBrickBreaker : public OswApp { unsigned long lastmove = 0; int lastpos = 0; - double ballPosx = 160; - double ballPosy = 120; - double ballSpdx = -2; - double ballSpdy = 4; - double absspd = 0; - double angleVar = 0; - double angleout = 0; - double angleout2 = 0; - double pHitPosition = 0; - double pHitAngle = 0; - double posAngle = 0; - double wallPosx = 0; - double wallPosy = 0; - double playerSpd = 0; + float ballPosx = 160; + float ballPosy = 120; + float ballSpdx = -2; + float ballSpdy = 4; + float absspd = 0; + float angleVar = 0; + float angleout = 0; + float angleout2 = 0; + float pHitPosition = 0; + float pHitAngle = 0; + float posAngle = 0; + float wallPosx = 0; + float wallPosy = 0; + float playerSpd = 0; bool scoreUpdated = false; bool grid[4][8] = {}; @@ -62,7 +62,7 @@ class OswAppBrickBreaker : public OswApp { int previousTime = 0; int gameStart = 0; - double spd = 1; + float spd = 1; float deltaSeconds = 0; diff --git a/include/apps/games/snake_game.h b/include/apps/games/snake_game.h index b421b971a..43b4cc629 100644 --- a/include/apps/games/snake_game.h +++ b/include/apps/games/snake_game.h @@ -25,8 +25,8 @@ class OswAppSnakeGame : public OswApp { //#define demo 1 // Change these values if sensitivity is too much/low - const float xSensitivity = 0.75; - const float ySensitivity = 0.75; + const float xSensitivity = 0.75f; + const float ySensitivity = 0.75f; int score = 1; int snake[snakeLength][2] = {{10, 10}}; diff --git a/include/apps/watchfaces/OswAppWatchfaceFitnessAnalog.h b/include/apps/watchfaces/OswAppWatchfaceFitnessAnalog.h new file mode 100644 index 000000000..0284d70a9 --- /dev/null +++ b/include/apps/watchfaces/OswAppWatchfaceFitnessAnalog.h @@ -0,0 +1,46 @@ +#include +#include +#include + +// if you want a cool background image, enable the following define +//#define GIF_BG + +#ifdef GIF_BG +class OswAppGifPlayer; +#endif +class OswAppWatchfaceFitnessAnalog : public OswAppV2 +{ +public: + constexpr static const char *APP_ID = "osw.wf.afit"; + + const char *getAppId() override; + const char *getAppName() override; + + void onStart(); + void onLoop() override; + void onDraw() override; + void onButton(Button id, bool up, ButtonStateNames state) override; + void onStop() override; + + static uint32_t calculateDistance(uint32_t steps); + + void timeDisplay(OswHal *hal, uint32_t hour, uint32_t minute, uint32_t second); + void timeDisplay(OswHal *hal, uint32_t hour, uint32_t minute, uint32_t second, bool afterNoon); + void dateDisplay(OswHal *hal, uint32_t hour, uint32_t minute, uint32_t second, bool afterNoon); + + void test(); + + ~OswAppWatchfaceFitnessAnalog() {} + +private: + time_t lastTime = 0; + unsigned screen = 0; + + void showFitnessTracking(OswHal *hal); + void drawWatchFace(OswHal *hal, uint32_t hour, uint32_t minute, uint32_t second, bool afterNoon); + void drawDateFace(OswHal *hal, uint32_t hour, uint32_t minute, uint32_t second, bool afterNoon); + +#ifdef GIF_BG + OswAppGifPlayer* bgGif = nullptr; +#endif +}; diff --git a/include/config_defaults.h b/include/config_defaults.h index 8b6531eab..778284075 100644 --- a/include/config_defaults.h +++ b/include/config_defaults.h @@ -21,9 +21,11 @@ #define DISP_H 240 #endif -// !! IMPORTANT: DISP_H must be divisible by DISP_CHUNK_H !! -#ifndef DISP_CHUNK_H -#define DISP_CHUNK_H 8 +// !! IMPORTANT: DISP_H must be divisible by (1<= width || y >= height || x < 0 || y < 0) { - return; - } - if (maskEnabled && color == maskColor) { - return; - } - - // if we have a pixel callback, there is now buffer - // draw with the callback and return.. - if (drawPixelCallback != NULL) { - drawPixelCallback->drawPixel(x, y, color); - return; - } - - uint8_t chunkId = y / chunkHeight; - int16_t chunkY = y - chunkId * chunkHeight; - - // - if (alphaEnabled) { - if (isRound && isInsideChunk(x, y)) { - int16_t chunkX = x - chunkXOffsets[chunkId]; - color = blend(buffer[chunkId][chunkX + chunkY * chunkWidths[chunkId]], color, alpha); - } else { - color = blend(buffer[chunkId][x + chunkY * width], color, alpha); - } - } + /** + * @brief Draw a pixel of color 'color' a x-y position and blend it with alpha. + * + * @param x x axis coordinate + * @param y y axis coordinate + * @param color color code of the pixel + * @param alpha alpha value to blend with color + */ + void drawPixelAA(int32_t x, int32_t y, uint16_t color, uint8_t alpha) { + uint16_t old_color = getPixel(x, y); - if (isRound && isInsideChunk(x, y)) { - int16_t chunkX = x - chunkXOffsets[chunkId]; - buffer[chunkId][chunkX + chunkY * chunkWidths[chunkId]] = color; - } else if (!isRound) { // fix for round module - buffer[chunkId][x + chunkY * width] = color; - } + uint16_t new_color = blend(old_color, color, alpha); + drawPixelClipped(x, y, new_color); } - uint16_t getPixel(uint16_t x, uint16_t y) { - if (x >= width || y >= height) { - return 0; - } - uint8_t chunkId = y / chunkHeight; - uint16_t chunkY = y - chunkId * chunkHeight; - // printf("chunkid %d, offetY %d for y=%d and chunkHeight=%d\n", chunkId, chunkY, y, chunkHeight); - if (isRound) { - // TODO: check if inside chunk - if (isInsideChunk(x, y)) { - uint16_t chunkX = x - chunkXOffsets[chunkId]; - return buffer[chunkId][chunkX + chunkY * chunkWidths[chunkId]]; - } else { - return missingPixelColor; - } - } else { - return buffer[chunkId][x + chunkY * width]; - } - } + void drawPixelClipped(int32_t x, int32_t y, uint16_t color); - bool isInsideChunk(uint16_t x, uint16_t y) { - uint8_t chunkId = y / chunkHeight; - // uint16_t chunkY = y - chunkId * chunkHeight; - uint16_t chunkOffset = chunkXOffsets[chunkId]; - uint16_t chunkWidth = chunkWidths[chunkId]; - bool xFit = chunkOffset < x && x < chunkOffset + chunkWidth; - // y always fits, because we chunk in rows - return xFit; - } + uint16_t getPixel(uint16_t x, uint16_t y); - /** - * @brief Draw an horizontal line from the point (x,y) to an other horizontal point at h pixels - * - * @param x x-axis of the start point - * @param y y-axis of the start point - * @param w width of the horizontal line - * @param color color code of the line - */ - void drawHLine(int32_t x, int32_t y, uint16_t w, uint16_t color) { - for (uint16_t i = 0; i < w; i++) { - drawPixel(x + i, y, color); - } - } + bool isInsideChunk(uint16_t x, uint16_t y); - /** - * @brief Draw a vertical line from the bottom point (x,y) to an other vertical point at h pixels - * - * @param x x-axis of the start point - * @param y y-axis of the start point - * @param h height of the vertical line - * @param color color code of the line - */ - void drawVLine(int32_t x, int32_t y, uint16_t h, uint16_t color) { - for (uint16_t i = 0; i < h; i++) { - drawPixel(x, y + i, color); - } - } + void drawHLine(int32_t x, int32_t y, uint16_t w, uint16_t color); - void drawFrame(int32_t x, int32_t y, uint16_t w, uint16_t h, uint16_t color) { - drawHLine(x, y, w, color); - drawHLine(x, y + h, w, color); - drawVLine(x, y, h, color); - drawVLine(x + w, y, h, color); - } + void drawVLine(int32_t x, int32_t y, uint16_t h, uint16_t color); - void fillFrame(int32_t x0, int32_t y0, uint16_t w, uint16_t h, uint16_t color) { - for (uint16_t y = y0; y < y0 + h; y++) { - drawHLine(x0, y, w, color); - } - } + void drawFrame(int32_t x, int32_t y, uint16_t w, uint16_t h, uint16_t color); + + void fillFrame(int32_t x0, int32_t y0, uint16_t w, uint16_t h, uint16_t color); /** * Draw line from (x1,y1) to (x2,y2) point with color @@ -291,69 +137,18 @@ class Graphics2D { * @param y2 * @param color */ - void drawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t color) { // see p3dt_gfx_2d_license.txt - // printf("\ndrawLine(%d, %d, %d, %d)",x1,y1,x2,y2); - // see p3dt_gfx_2d_license.txt - int32_t tmp; - int32_t x, y; - int32_t dx, dy; - int32_t err; - int32_t ystep; - - uint8_t swapxy = 0; - - /* no intersection check at the moment, should be added... */ - - if (x1 > x2) - dx = x1 - x2; - else - dx = x2 - x1; - if (y1 > y2) - dy = y1 - y2; - else - dy = y2 - y1; - - if (dy > dx) { - swapxy = 1; - tmp = dx; - dx = dy; - dy = tmp; - tmp = x1; - x1 = y1; - y1 = tmp; - tmp = x2; - x2 = y2; - y2 = tmp; - } - if (x1 > x2) { - tmp = x1; - x1 = x2; - x2 = tmp; - tmp = y1; - y1 = y2; - y2 = tmp; - } - err = dx >> 1; - if (y2 > y1) - ystep = 1; - else - ystep = -1; - y = y1; - - if (x2 == 0xffff) x2--; + void drawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t color); - for (x = x1; x <= x2; x++) { - if (swapxy == 0) - drawPixel(x, y, color); - else - drawPixel(y, x, color); - err -= abs(dy); - if (err < 0) { - y += ystep; - err += dx; - } - } - } + /** + * Draw an anti-aliased line from (x1,y1) to (x2,y2) point with color + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param color + */ + void drawLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const uint16_t color); /** * @brief Draw a line between (x1,y1) and (x2,y2) with a thick of radius and with specific color @@ -368,126 +163,39 @@ class Graphics2D { * @param color color code use to draw the line. * @param highQuality */ - void drawThickLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint8_t radius, uint16_t color, - bool highQuality = false) { // see p3dt_gfx_2d_license.txt - - // see p3dt_gfx_2d_license.txt - int32_t tmp; - int32_t x, y; - int32_t dx, dy; - int32_t err; - int32_t ystep; - - uint8_t swapxy = 0; - - /* no intersection check at the moment, should be added... */ - - if (x1 > x2) - dx = x1 - x2; - else - dx = x2 - x1; - if (y1 > y2) - dy = y1 - y2; - else - dy = y2 - y1; - - if (dy > dx) { - swapxy = 1; - tmp = dx; - dx = dy; - dy = tmp; - tmp = x1; - x1 = y1; - y1 = tmp; - tmp = x2; - x2 = y2; - y2 = tmp; - } - if (x1 > x2) { - tmp = x1; - x1 = x2; - x2 = tmp; - tmp = y1; - y1 = y2; - y2 = tmp; - } - err = dx >> 1; - if (y2 > y1) - ystep = 1; - else - ystep = -1; - y = y1; - - if (x2 == 0xffff) x2--; + void drawThickLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint8_t radius, uint16_t color, bool highQuality = false); - for (x = x1; x <= x2; x++) { - if (swapxy == 0) { - if (highQuality) { - fillCircle(x, y, radius, color); - } else { - drawCircle(x, y, radius, color); - if (radius > 2) { - drawCircle(x, y, radius - 1, color); - } - if (radius > 3) { - drawCircle(x, y, radius - 2, color); - } - } - } else { - if (highQuality) { - fillCircle(y, x, radius, color); - } else { - drawCircle(y, x, radius, color); - if (radius > 2) { - drawCircle(y, x, radius - 1, color); - } - if (radius > 3) { - drawCircle(y, x, radius - 2, color); - } - } - } + /** + * @brief Draw an anti-aliased line between (x1,y1) and (x2,y2) with a thicknes of line_width and with specific color + * + * @param x1 x-axis of the start point + * @param y1 y-axis of the start point + * @param x2 x-axis of the end point + * @param y2 y-axis of the end point + * @param line_width thickness of the line + * @param color color code use to draw the line. + */ + void drawThickLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t line_width, const uint16_t color, LINE_END_OPT eol = ROUND_END); - err -= (uint8_t)dy; - if (err < 0) { - y += (uint16_t)ystep; - err += (uint16_t)dx; - } - } - } + void drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color); + void drawFilledTriangle(int32_t xa, int32_t ya, int32_t xb, int32_t yb, int32_t xc, int32_t yc, uint16_t color); - void drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { - drawLine(x0, y0, x1, y1, color); - drawLine(x1, y1, x2, y2, color); - drawLine(x2, y2, x0, y0, color); - } + /** + * @brief Draw a horizontal/vertical box (x1,y1) and (x2,y2) with a specific color + * + * @param x1 x-axis of the start point + * @param y1 y-axis of the start point + * @param x2 x-axis of the end point + * @param y2 y-axis of the end point + * @param color color code use to fill the box. + */ + void fillBoxHV(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const uint16_t color); /* * "Complex" Stuff: */ - void _drawCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, - CIRC_OPT option) { // see p3dt_gfx_2d_license.txt - - if (option == DRAW_UPPER_RIGHT || option == DRAW_ALL) { - drawPixel(x0 + x, y0 - y, color); - drawPixel(x0 + y, y0 - x, color); - } - - if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { - drawPixel(x0 - x, y0 - y, color); - drawPixel(x0 - y, y0 - x, color); - } - - if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { - drawPixel(x0 + x, y0 + y, color); - drawPixel(x0 + y, y0 + x, color); - } - - if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { - drawPixel(x0 - x, y0 + y, color); - drawPixel(x0 - y, y0 + x, color); - } - } + void _drawCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, CIRC_OPT option); /** * @brief Draw a circle @@ -498,476 +206,79 @@ class Graphics2D { * @param color color code of the circle * @param option */ - void drawCircle(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color, - CIRC_OPT option = DRAW_ALL) { // see p3dt_gfx_2d_license.txt - - float f; - float ddFx; - float ddFy; - float x; - float y; - - f = 1; - f -= rad; - ddFx = 1; - ddFy = 0; - ddFy -= rad; - ddFy *= 2; - x = 0; - y = rad; - - _drawCircleSection(x, y, x0, y0, color, option); - - while (x < y) { - if (f >= 0) { - y--; - ddFy += 2; - f += ddFy; - } - x++; - ddFx += 2; - f += ddFx; + void drawCircle(int16_t x0, int16_t y0, int16_t rad, uint16_t color, CIRC_OPT option = DRAW_ALL); - _drawCircleSection(x, y, x0, y0, color, option); - } - } - - void _fillCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, - CIRC_OPT option) { // see p3dt_gfx_2d_license.txt - - if (option == DRAW_UPPER_RIGHT || option == DRAW_ALL) { - drawVLine(x0 + x, y0 - y, y + 1, color); - drawVLine(x0 + y, y0 - x, x + 1, color); - } - - if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { - drawVLine(x0 - x, y0 - y, y + 1, color); - drawVLine(x0 - y, y0 - x, x + 1, color); - } - - if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { - drawVLine(x0 + x, y0, y + 1, color); - drawVLine(x0 + y, y0, x + 1, color); - } - - if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { - drawVLine(x0 - x, y0, y + 1, color); - drawVLine(x0 - y, y0, x + 1, color); - } - } - - void fillCircle(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color, - CIRC_OPT option = DRAW_ALL) { // see p3dt_gfx_2d_license.txt - - float f; - float ddFx; - float ddFy; - float x; - float y; - - f = 1; - f -= rad; - ddFx = 1; - ddFy = 0; - ddFy -= rad; - ddFy *= 2; - x = 0; - y = rad; - - _fillCircleSection(x, y, x0, y0, color, option); - - while (x < y) { - if (f >= 0) { - y--; - ddFy += 2; - f += ddFy; - } - x++; - ddFx += 2; - f += ddFx; - - _fillCircleSection(x, y, x0, y0, color, option); - } - } - - void _drawEllipseSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, - CIRC_OPT option = DRAW_ALL) { // see p3dt_gfx_2d_license.txt - - /* upper right */ - if (option == DRAW_UPPER_RIGHT || option == DRAW_ALL) { - drawPixel(x0 + x, y0 - y, color); - } - - /* upper left */ - if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { - drawPixel(x0 - x, y0 - y, color); - } - - /* lower right */ - if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { - drawPixel(x0 + x, y0 + y, color); - } + /** + * @brief Draw an anti-aliased circle with thicknes + * + * @param x0 x-axis of the center of the circle + * @param y0 y-axis of the center of the circle + * @param r radius of the circle + * @param bw thickness of th circle + * @param color color code of the circle + */ + void drawCircleAA(int16_t off_x, int16_t off_y, int16_t r, int16_t bw, uint16_t color, + int16_t start_angle = -1, int16_t end_angle = -1); - /* lower left */ - if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { - drawPixel(x0 - x, y0 + y, color); - } + inline void fillCircleAA(int16_t off_x, int16_t off_y, int16_t r, uint16_t color){ + drawCircleAA(off_x, off_y, r, r-1, color); } - void drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color, - CIRC_OPT option = DRAW_ALL) { // see p3dt_gfx_2d_license.txt - - float x; - float y; - float xchg; - float ychg; - float err; - float rxrx2; - float ryry2; - float stopx; - float stopy; + void _fillCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, CIRC_OPT option); + void fillCircle(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color, CIRC_OPT option = DRAW_ALL); + void _drawEllipseSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, CIRC_OPT option = DRAW_ALL); + void drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color, CIRC_OPT option = DRAW_ALL); + void _fillEllipseSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, CIRC_OPT option = DRAW_ALL); - rxrx2 = rx; - rxrx2 *= rx; - rxrx2 *= 2; + void fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color, CIRC_OPT option = DRAW_ALL); + void drawRFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t r, uint16_t color); + void fillRFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t r, uint16_t color); - ryry2 = ry; - ryry2 *= ry; - ryry2 *= 2; - - x = rx; - y = 0; - - xchg = 1; - xchg -= rx; - xchg -= rx; - xchg *= ry; - xchg *= ry; - - ychg = rx; - ychg *= rx; - - err = 0; - - stopx = ryry2; - stopx *= rx; - stopy = 0; - - while (stopx >= stopy) { - _drawEllipseSection(x, y, x0, y0, color, option); - y++; - stopy += rxrx2; - err += ychg; - ychg += rxrx2; - if (2 * err + xchg > 0) { - x--; - stopx -= ryry2; - err += xchg; - xchg += ryry2; - } - } - - x = 0; - y = ry; - - xchg = ry; - xchg *= ry; - - ychg = 1; - ychg -= ry; - ychg -= ry; - ychg *= rx; - ychg *= rx; - - err = 0; - - stopx = 0; - - stopy = rxrx2; - stopy *= ry; - - while (stopx <= stopy) { - _drawEllipseSection(x, y, x0, y0, color, option); - x++; - stopx += ryry2; - err += xchg; - xchg += ryry2; - if (2 * err + ychg > 0) { - y--; - stopy -= rxrx2; - err += ychg; - ychg += rxrx2; - } - } + inline void drawTick(int16_t cx, int16_t cy, int16_t r1, int16_t r2, float angle, uint16_t color) { + drawLine(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), color); } - void _fillEllipseSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, - CIRC_OPT option = DRAW_ALL) { // see p3dt_gfx_2d_license.txt - - /* upper right */ - if (option == DRAW_UPPER_RIGHT || option == DRAW_ALL) { - drawVLine(x0 + x, y0 - y, y + 1, color); - } - - /* upper left */ - if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { - drawVLine(x0 - x, y0 - y, y + 1, color); - } - - /* lower right */ - if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { - drawVLine(x0 + x, y0, y + 1, color); - } - - /* lower left */ - if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { - drawVLine(x0 - x, y0, y + 1, color); - } + inline void drawTick(int16_t cx, int16_t cy, int16_t r1, int16_t r2, int angle, uint16_t color) { + drawLine(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), color); } - void fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color, - CIRC_OPT option = DRAW_ALL) { // see p3dt_gfx_2d_license.txt - - float x; - float y; - float xchg; - float ychg; - float err; - float rxrx2; - float ryry2; - float stopx; - float stopy; - - rxrx2 = rx; - rxrx2 *= rx; - rxrx2 *= 2; - - ryry2 = ry; - ryry2 *= ry; - ryry2 *= 2; - - x = rx; - y = 0; - - xchg = 1; - xchg -= rx; - xchg -= rx; - xchg *= ry; - xchg *= ry; - - ychg = rx; - ychg *= rx; - - err = 0; - - stopx = ryry2; - stopx *= rx; - stopy = 0; - - while (stopx >= stopy) { - _fillEllipseSection(x, y, x0, y0, color, option); - y++; - stopy += rxrx2; - err += ychg; - ychg += rxrx2; - if (2 * err + xchg > 0) { - x--; - stopx -= ryry2; - err += xchg; - xchg += ryry2; - } - } - - x = 0; - y = ry; - - xchg = ry; - xchg *= ry; - - ychg = 1; - ychg -= ry; - ychg -= ry; - ychg *= rx; - ychg *= rx; - - err = 0; - - stopx = 0; - - stopy = rxrx2; - stopy *= ry; - - while (stopx <= stopy) { - _fillEllipseSection(x, y, x0, y0, color, option); - x++; - stopx += ryry2; - err += xchg; - xchg += ryry2; - if (2 * err + ychg > 0) { - y--; - stopy -= rxrx2; - err += ychg; - ychg += rxrx2; - } - } + inline void drawTickAA(int16_t cx, int16_t cy, int16_t r1, int16_t r2, float angle, uint16_t color) { + drawLineAA(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), color); } - void drawRFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t r, - uint16_t color) { // see p3dt_gfx_2d_license.txt - - uint16_t xl; - uint16_t yu; - - xl = x; - xl += r; - yu = y; - yu += r; - - { - uint16_t yl; - uint16_t xr; - - xr = x; - xr += w; - xr -= r; - xr -= 1; - - yl = y; - yl += h; - yl -= r; - yl -= 1; - - drawCircle(xl, yu, r, color, DRAW_UPPER_LEFT); - drawCircle(xr, yu, r, color, DRAW_UPPER_RIGHT); - drawCircle(xl, yl, r, color, DRAW_LOWER_LEFT); - drawCircle(xr, yl, r, color, DRAW_LOWER_RIGHT); - } - - { - uint16_t ww; - uint16_t hh; - - ww = w; - ww -= r; - ww -= r; - hh = h; - hh -= r; - hh -= r; - - xl++; - yu++; - - if (ww >= 3) { - ww -= 2; - h--; - drawHLine(xl, y, ww, color); - drawHLine(xl, y + h, ww, color); - } - - if (hh >= 3) { - hh -= 2; - w--; - drawVLine(x, yu, hh, color); - drawVLine(x + w, yu, hh, color); - } - } + inline void drawTickAA(int16_t cx, int16_t cy, int16_t r1, int16_t r2, int32_t angle, uint16_t color) { + drawLineAA(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), color); } - void fillRFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t r, - uint16_t color) { // see p3dt_gfx_2d_license.txt - //Prevent infinite looping - if(h < 2 * r) return; - - uint16_t xl; - uint16_t yu; - uint16_t yl; - uint16_t xr; - - xl = x; - xl += r; - yu = y; - yu += r; - - xr = x; - xr += w; - xr -= r; - xr -= 1; - - yl = y; - yl += h; - yl -= r; - yl -= 1; - fillCircle(xl, yu, r, color, DRAW_UPPER_LEFT); - fillCircle(xr, yu, r, color, DRAW_UPPER_RIGHT); - fillCircle(xl, yl, r, color, DRAW_LOWER_LEFT); - fillCircle(xr, yl, r, color, DRAW_LOWER_RIGHT); - - { - uint16_t ww; - uint16_t hh; - - ww = w; - ww -= r; - ww -= r; - xl++; - yu++; - - if (ww >= 3) { - ww -= 2; - fillFrame(xl, y, ww, r + 1, color); - fillFrame(xl, yl, ww, r + 1, color); - } - - hh = h; - hh -= r; - hh -= r; - // h--; - if (hh >= 3) { - hh -= 2; - fillFrame(x, yu, w, hh, color); - } - } - } - - void drawTick(uint8_t cx, uint8_t cy, uint8_t r1, uint8_t r2, float angle, uint16_t color) { - drawLine(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), color); + inline void drawThickTick(int16_t cx, int16_t cy, int16_t r1, int16_t r2, float angle, int16_t radius, uint16_t color, + bool highQuality = false, LINE_END_OPT eol = ROUND_END) { + if (highQuality) + drawThickLineAA(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), radius, color, eol); + else + drawThickLine(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), radius, color, + highQuality); } - void drawThickTick(uint8_t cx, uint8_t cy, uint8_t r1, uint8_t r2, float angle, uint8_t radius, uint16_t color, - bool highQuality = false) { - drawThickLine(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), radius, color, - highQuality); - } + void drawNTicks(int16_t cx, int16_t cy, int16_t r1, int16_t r2, int16_t nTicks, uint16_t color , int16_t skip_every_nth = 361); + void drawNTicksAA(int16_t cx, int16_t cy, int16_t r1, int16_t r2, int16_t nTicks, uint16_t color, int16_t skip_every_nth = 361); /** - * @brief Draw N ticks around the clock to visualize the hours. + * @brief Draw the ticks around the clock to visualize the hours. * - * @param cx center x axis - * @param cy center y axis - * @param r1 radius from the begin of the tick. - * @param r2 radius from the end of the tick. - * @param nTicks number of ticks to draw - * @param color color code - */ - void drawNTicks(uint8_t cx, uint8_t cy, uint8_t r1, uint8_t r2, uint8_t nTicks, uint16_t color) { - const float deltaAngle = 360.0 / nTicks; - for (uint16_t h = 0; h < nTicks; h++) { - drawTick(cx, cy, r1, r2, h * deltaAngle, color); - } - } - - /** - * @brief Draw 12 ticks around the clock to visualize the hours. + * 12 ticks will be drawn around the clock. * * @param cx center x axis * @param cy center y axis - * @param r1 radius from the begin of the tick. - * @param r2 radius from the end of the tick. + * @param r1 rayon + * @param r2 * @param color color code */ - void drawHourTicks(uint8_t cx, uint8_t cy, uint8_t r1, uint8_t r2, uint16_t color) { - drawNTicks(cx, cy, r1, r2, 12, color); + inline void drawHourTicks(int16_t cx, int16_t cy, int16_t r1, int16_t r2, uint16_t color, bool anti_alias = true) { + if (anti_alias) + drawNTicksAA(cx, cy, r1, r2, 12, color); + else + drawNTicks(cx, cy, r1, r2, 12, color); } /** @@ -981,199 +292,50 @@ class Graphics2D { * @param r2 * @param color color code */ - void drawMinuteTicks(uint8_t cx, uint8_t cy, uint8_t r1, uint8_t r2, uint16_t color) { - drawNTicks(cx, cy, r1, r2, 60, color); - } - - void drawArc(int16_t x, int16_t y, int16_t r1, int16_t r2, float start, float end, uint16_t color) { - this->drawArc(x, y, start, end, 1, r1, r2-r1, color); // This _should_ be the equivalent call... + inline void drawMinuteTicks(int16_t cx, int16_t cy, int16_t r1, int16_t r2, uint16_t color, bool anti_alias = true) { + if (anti_alias) + drawNTicksAA(cx, cy, r1, r2, 60, color, 5); + else + drawNTicks(cx, cy, r1, r2, 60, color, 5); } - /** - * Draw an arc - * - * @param cx Arc X center coordinates - * @param cy Arc Y center coordinates - * @param start Beginning angle of the arc (in deg). O° is equivalent to 12AM - * @param stop End angle of the arc (in deg). - * @param steps Number of lines that will compose the arc. - * @param radius Radius of the arc from the cx/cy center - * @param lineRadius Radius of the line. Example : radius = 1 give a line of 4 px of diameter, radius 2 -> 8px, - * etc.... - * @param color Color code of the arc - * @param highQuality - */ - void drawArc(int32_t cx, int32_t cy, float start, float stop, uint16_t steps, uint16_t radius, uint8_t lineRadius, - uint16_t color, bool highQuality = false) { - int32_t x1 = rpx(cx, radius, start); - int32_t y1 = rpy(cy, radius, start); - // printf("\ndraw from %f,%f in %d steps", start, stop, steps); - - float arcLength = stop - start; - - for (uint16_t i = 1; i <= steps; i++) { - float segmentLength = i * (arcLength / steps); - // printf("\n rpx(%d, %d, %f + %f)", cx, radius, start, segmentLength); - - int32_t x2 = rpx(cx, radius, start + segmentLength); - int32_t y2 = rpy(cy, radius, start + segmentLength); - // printf("\n gfx2d.drawLine(%d, %d, %d, %d, color);", x1, y1, x2, y2); - drawThickLine(x1, y1, x2, y2, lineRadius, color, highQuality); - x1 = x2; - y1 = y2; - } + inline void drawArc(int16_t x, int16_t y, int16_t r1, int16_t r2, float start, float end, uint16_t color, bool anti_alias = true) { + this->drawArc(x, y, start, end, 1, r1, r2-r1, color, anti_alias); // This _should_ be the equivalent call... } - void drawBWBitmap(uint16_t x0, uint16_t y0, uint16_t cnt, uint16_t h, uint8_t* bitmap, uint16_t color, - uint16_t bgColor = 0, bool drawBackground = false) { - // cnt: Number of bytes of the bitmap in horizontal direction. The width of the bitmap is cnt*8. - // h: Height of the bitmap. + void drawArc(int16_t cx, int16_t cy, float start, float stop, int16_t steps, int16_t radius, int16_t lineRadius, + uint16_t color, bool highQuality = false, bool anti_alias = true); - for (uint16_t x = 0; x < cnt; x++) { - for (uint16_t y = 0; y < h; y++) { - uint8_t bits = bitmap[x + y * cnt]; - for (uint8_t b = 1; b <= 8; b++) { - if (bits & (1 << (8 - b))) { - drawPixel(x * 8 + x0 + b, y + y0, color); - } else if (drawBackground) { - drawPixel(x * 8 + x0 + b, y + y0, bgColor); - } - } - } - } - } + void drawBWBitmap(int16_t x0, int16_t y0, int16_t cnt, int16_t h, uint8_t* bitmap, uint16_t color, + uint16_t bgColor = 0, bool drawBackground = false); - /** - * @brief Fill all the display with a color. - * - * Used for initialisation of the display with a background color. - * - * @param color Color code - */ - void fill(uint16_t color) { - for (uint16_t x = 0; x < width; x++) { - for (uint16_t y = 0; y < height; y++) { - drawPixel(x, y, color); - } - } - } + void fill(uint16_t color); - void dim(uint8_t amount) { - for (uint16_t x = 0; x < width; x++) { - for (uint16_t y = 0; y < height; y++) { - drawPixel(x, y, dimColor(getPixel(x, y), amount)); - } - } - } + void dim(uint8_t amount); + void drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source); - void drawGraphics2D(int32_t offsetX, int32_t offsetY, Graphics2D* source) { - for (int32_t y = 0; y < source->getHeight(); y++) { - for (int32_t x = 0; x < source->getWidth(); x++) { - drawPixel(x + offsetX, y + offsetY, source->getPixel(x, y)); - } - } - } - - void drawGraphics2D(int32_t offsetX, int32_t offsetY, Graphics2D* source, int32_t sourceOffsetX, - int32_t sourceOffsetY, uint16_t sourceWidth, uint16_t sourceHeight) { - for (int32_t x = 0; x < sourceWidth; x++) { - for (int32_t y = 0; y < sourceHeight; y++) { - drawPixel(x + offsetX, y + offsetY, source->getPixel(x + sourceOffsetX, y + sourceOffsetY)); - } - } - } + void drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source, int16_t sourceOffsetX, + int16_t sourceOffsetY, int16_t sourceWidth, int16_t sourceHeight); // draw scaled by 2x - void drawGraphics2D_2x(int32_t offsetX, int32_t offsetY, Graphics2D* source) { - for (int32_t x = 0; x < source->getWidth() * 2; x++) { - for (int32_t y = 0; y < source->getHeight() * 2; y++) { - drawPixel(x + offsetX, y + offsetY, source->getPixel(x / 2, y / 2)); - } - } - } + void drawGraphics2D_2x(int16_t offsetX, int16_t offsetY, Graphics2D* source); // draw section scaled by 2x - void drawGraphics2D_2x(uint16_t offsetX, uint16_t offsetY, Graphics2D* source, uint16_t sourceOffsetX, - uint16_t sourceOffsetY, uint16_t sourceWidth, uint16_t sourceHeight) { - for (uint16_t x = 0; x < sourceWidth * 2; x++) { - for (uint16_t y = 0; y < sourceHeight * 2; y++) { - drawPixel(x + offsetX, y + offsetY, source->getPixel(sourceOffsetX + x / 2, sourceOffsetY + y / 2)); - } - } - } + void drawGraphics2D_2x(int16_t offsetX, int16_t offsetY, Graphics2D* source, int16_t sourceOffsetX, + int16_t sourceOffsetY, int16_t sourceWidth, int16_t sourceHeight); #ifdef ROTATE_LEGACY // this rotate function is faster, but it has artifacts void drawGraphics2D_rotatedLegacy(uint16_t offsetX, uint16_t offsetY, Graphics2D* source, uint16_t rotationX, - uint16_t rotationY, float angle) { - float cosA = cos(angle); - float sinA = sin(angle); - for (uint16_t x = 0; x < source->getWidth(); x++) { - for (uint16_t y = 0; y < source->getHeight(); y++) { - int32_t newX = (x - rotationX) * cosA + (y - rotationY) * sinA; - int32_t newY = (y - rotationY) * cosA - (x - rotationX) * sinA; - drawPixel(newX + offsetX, newY + offsetY, source->getPixel(x, y)); - } - } - } + uint16_t rotationY, float angle); #endif - void drawGraphics2D_rotated(uint16_t offsetX, uint16_t offsetY, Graphics2D* source, uint16_t rx, uint16_t ry, - float angle) { - float cosA = cos(angle); - float sinA = sin(angle); - // rotateX = (x - rx) * cos(angle) + (y - ry) * sin(angle); - // rotateY = (y - ry) * cos(angle) - (x - rx) * sin(angle); - - // first calculate the bounding box of the new image - // // top left - int32_t tl_x = rotateX(0, 0, rx, ry, cosA, sinA); - int32_t tl_y = rotateY(0, 0, rx, ry, cosA, sinA); - // // top right - int32_t tr_x = rotateX(source->getWidth() - 1, 0, rx, ry, cosA, sinA); - int32_t tr_y = rotateY(source->getWidth() - 1, 0, rx, ry, cosA, sinA); - - // // bottom left - int32_t bl_x = rotateX(0, source->getHeight() - 1, rx, ry, cosA, sinA); - int32_t bl_y = rotateY(0, source->getHeight() - 1, rx, ry, cosA, sinA); + void drawGraphics2D_rotated(int16_t offsetX, int16_t offsetY, Graphics2D* source, int16_t rx, int16_t ry, + float angle); - // // bottom right - int32_t br_x = rotateX(source->getWidth(), source->getHeight(), rx, ry, cosA, sinA); - int32_t br_y = rotateY(source->getWidth(), source->getHeight(), rx, ry, cosA, sinA); - - // debug: draw rotated image - // this->drawLine(offsetX + tl_x, offsetY + tl_y, offsetX + tr_x, offsetY + tr_y, rgb565(255, 0, 0)); - // this->drawLine(offsetX + tr_x, offsetY + tr_y, offsetX + br_x, offsetY + br_y, rgb565(255, 0, 0)); - // this->drawLine(offsetX + bl_x, offsetY + bl_y, offsetX + br_x, offsetY + br_y, rgb565(255, 0, 0)); - // this->drawLine(offsetX + bl_x, offsetY + bl_y, offsetX + tl_x, offsetY + tl_y, rgb565(255, 0, 0)); - - // determine bounding box - int32_t boxX = min(tl_x, min(tr_x, min(bl_x, br_x))); - int32_t boxY = min(tl_y, min(tr_y, min(bl_y, br_y))); - int32_t boxW = max(tl_x, max(tr_x, max(bl_x, br_x))) - boxX; - int32_t boxH = max(tl_y, max(tr_y, max(bl_y, br_y))) - boxY; - - // debug: draw bounding box - // this->drawFrame(boxX + offsetX, boxY + offsetY, boxW, boxH, rgb565(0, 255, 0)); - cosA = cos(-angle); - sinA = sin(-angle); - for (int16_t x = boxX; x < boxX + boxW; x++) { - for (int16_t y = boxY; y < boxY + boxH; y++) { - if (pointInsideTriangle(x, y, tl_x, tl_y, tr_x, tr_y, br_x, br_y)) { - int16_t origX = rotateX(x, y, 0, 0, cosA, sinA); - int16_t origY = rotateY(x, y, 0, 0, cosA, sinA); - drawPixel(x + offsetX, y + offsetY, source->getPixel(origX + rx, origY + ry)); - } else if (pointInsideTriangle(x, y, tl_x, tl_y, bl_x, bl_y, br_x, br_y)) { - int16_t origX = rotateX(x, y, 0, 0, cosA, sinA); - int16_t origY = rotateY(x, y, 0, 0, cosA, sinA); - drawPixel(x + offsetX, y + offsetY, source->getPixel(origX + rx, origY + ry)); - } - } - } - } - - protected: +protected: uint16_t** buffer; + uint16_t numChunks; DrawPixel* drawPixelCallback; uint16_t* chunkXOffsets; uint16_t* chunkWidths; @@ -1181,21 +343,28 @@ class Graphics2D { /** * @brief Width (in pixels) of the display frame */ - uint16_t width; + int32_t width; /** * @brief Height (in pixels) of the display frame. */ - uint16_t height; + int32_t height; uint16_t maskColor; uint16_t missingPixelColor; bool maskEnabled; - uint8_t chunkHeight; + uint8_t chunkHeightLd; // Height of a chunk is 2^chunkHeightLd bool isRound; bool alphaEnabled; bool allocatePsram; float alpha; + + // for caching of isPixelMasked + void isPixelMaskedByAnglesInit(int32_t off_x, int32_t off_y, int32_t sa, int32_t ea); + bool isPixelMaskedByAngles(int32_t x, int32_t y); + int32_t ox, oy; + int32_t start_angle, end_angle; + float tan_sa, tan_ea; }; #endif diff --git a/include/gfx_util.h b/include/gfx_util.h index a2227bd54..9776e05f0 100644 --- a/include/gfx_util.h +++ b/include/gfx_util.h @@ -17,6 +17,53 @@ uint8_t rgb888_red(uint32_t rgb888); uint8_t rgb888_green(uint32_t rgb888); uint8_t rgb888_blue(uint32_t rgb888); +inline uint16_t rgb565(uint8_t red, uint8_t green, uint8_t blue) { + return ((red & 0b00011111000) << 8) | ((green & 0b00011111100) << 3) | (blue >> 3); +} + +inline uint32_t rgb888(uint8_t red, uint8_t green, uint8_t blue) { + return ((uint32_t)red << 16) | ((uint32_t)green << 8) | (uint32_t)blue; +} + +inline uint16_t rgb888to565(uint32_t rgb888) { + return rgb565(rgb888_red(rgb888), rgb888_green(rgb888), rgb888_blue(rgb888)); +} + +inline uint32_t rgb565to888(uint16_t rgb565) { + return rgb888(rgb565_red(rgb565), rgb565_green(rgb565), rgb565_blue(rgb565)); +} + +inline uint8_t rgb565_red(uint16_t rgb565) { + // |rrrrrggg|gggbbbbb| + return (rgb565 >> 8) & 0b11111000; +} + +inline uint8_t rgb565_green(uint16_t rgb565) { + // |rrrrrggg|gggbbbbb| + return (rgb565 >> 3) & 0b11111100; +} + +inline uint8_t rgb565_blue(uint16_t rgb565) { + // |rrrrrggg|gggbbbbb| + return (rgb565 << 3); +} + +inline uint8_t rgb888_red(uint32_t rgb888) { + // |rrrrrrrr|gggggggg|bbbbbbbb| + return rgb888 >> 16; +} + +inline uint8_t rgb888_green(uint32_t rgb888) { + // |rrrrrrrr|gggggggg|bbbbbbbb| + return rgb888 >> 8; +} + +inline uint8_t rgb888_blue(uint32_t rgb888) { + // |rrrrrrrr|gggggggg|bbbbbbbb| + return rgb888; +} + +uint16_t blend(uint16_t target, uint16_t source, uint8_t alpha); uint16_t blend(uint16_t target, uint16_t source, float alpha); uint16_t dimColor(uint16_t oc, uint8_t amount); uint16_t changeColor(uint16_t oc, float amount); diff --git a/include/locales/de-DE.h b/include/locales/de-DE.h index 7abaa28b7..ccd2aaaaa 100644 --- a/include/locales/de-DE.h +++ b/include/locales/de-DE.h @@ -31,9 +31,9 @@ #define LANG_SETTINGS "Einstellungen" #define LANG_GAMES "Spiele" #define LANG_MIX "Mixtur" -#define LANG_MIX "Zweifach" #define LANG_BINARY "Binaer" #define LANG_MONO "Einfach" +#define LANG_AFIT "Analog+Schritt" // App: Time from web #define LANG_TFW_UPDATE "Abfragen" diff --git a/include/locales/en-US.h b/include/locales/en-US.h index 0d9dcf46c..a25b29834 100644 --- a/include/locales/en-US.h +++ b/include/locales/en-US.h @@ -112,6 +112,9 @@ #ifndef LANG_NUMERALS #define LANG_NUMERALS "Numerals" #endif +#ifndef LANG_AFIT +#define LANG_AFIT "Analog+Fit" +#endif // App: Time from web #ifndef LANG_TFW_UPDATE diff --git a/include/math_angles.h b/include/math_angles.h index 2153111d2..296404cc1 100644 --- a/include/math_angles.h +++ b/include/math_angles.h @@ -1,40 +1,77 @@ #ifndef GFX_ANGLES_H #define GFX_ANGLES_H +#include #include +extern const float sinDeg[]; + /** * @brief Find the x-axis point which is at a distance r and an angle d of a point C(cx,cy). * * 0 degrees is 12 o'clock * - * This function can be used to find coordonnates of the extremity of the clock hand from the center + * This function can be used to find coordinates of the extremity of the clock hand from the center * * @param cx x value of the initial point * @param r radius - * @param d angle in degrees (0° is 12 o'clock) - * @return float + * @param deg angle in degrees (0° is 12 o'clock) + * @return int32_t */ -float rpx(float cx, float x, float r); +inline int32_t rpx(int32_t cx, int32_t r, float deg) { + return cx + (float) r * cosf((deg - 90) * (float) PI / 180) + 0.5f; +} +// integer version for deg +inline int32_t rpx(int32_t cx, int32_t r, int32_t deg) { + deg -= 90; + deg -= 90; // to use sine + while (deg < 0) + deg += 360; + while (deg > 360) + deg -= 360; + return cx + (float) r * sinDeg[deg] + 0.5f; +} /** * Find the y-axis of a point which is at a distance r and an angle d of a point C(cx,cy). * * 0 degrees is 12 o'clock - * This function can be used to find coordonnates of the extremity of the clock hand from the center + * This function can be used to find coordinates of the extremity of the clock hand from the center * * @param cy y value of the initial point * @param r radius - * @param d angle in degrees (0° is 12 o'clock) + * @param deg angle in degrees (0° is 12 o'clock) * @return float */ -float rpy(float cy, float y, float r); +inline int32_t rpy(int32_t cy, int32_t r, float deg) { + return cy + (float) r * sinf((deg - 90) * (float) PI / 180) + 0.5f; +} + +// integer version for deg +inline int32_t rpy(int32_t cy, int32_t r, int32_t deg) { + deg -= 90; + while (deg < 0) + deg += 360; + while (deg > 360) + deg -= 360; + return cy + (float) r * sinDeg[deg] + 0.5f; +} + +// rotate a point around a point +inline int32_t rotateX(int32_t x, int32_t y, int32_t rx, int32_t ry, float cosA, float sinA) { + return (x - rx) * cosA + (y - ry) * sinA; +} +inline int32_t rotateY(int32_t x, int32_t y, int32_t rx, int32_t ry, float cosA, float sinA) { + return (y - ry) * cosA - (x - rx) * sinA; +} -int32_t rotateX(int32_t x, int32_t y, int32_t rx, int32_t ry, float cosA, float sinA); -int32_t rotateY(int32_t x, int32_t y, int32_t rx, int32_t ry, float cosA, float sinA); -int32_t rotateX(int32_t x, int32_t y, int32_t rx, int32_t ry, float a); -int32_t rotateY(int32_t x, int32_t y, int32_t rx, int32_t ry, float a); +inline int32_t rotateX(int32_t x, int32_t y, int32_t rx, int32_t ry, float a) { + return (x - rx) * cosf(a) + (y - ry) * sinf(a); +} +inline int32_t rotateY(int32_t x, int32_t y, int32_t rx, int32_t ry, float a) { + return (y - ry) * cosf(a) - (x - rx) * sinf(a); +} /** * Convert seconds in degrees. @@ -53,4 +90,4 @@ float m2d(long seconds); float h2d(long seconds); bool pointInsideTriangle(float px, float py, float x1, float y1, float x2, float y2, float x3, float y3); -#endif \ No newline at end of file +#endif diff --git a/platformio.ini b/platformio.ini index 02960c9fe..3486d3ef5 100755 --- a/platformio.ini +++ b/platformio.ini @@ -12,7 +12,7 @@ default_envs = LIGHT_EDITION_V3_3 [env] -platform = espressif32@^6.3.2 +platform = espressif32@^6.5.0 ; for USE_ULP: use a special branch and git submodule add https://github.com/boarchuz/HULP.git ; platform_packages = ; framework-arduinoespressif32 @ https://github.com/marcovannoord/arduino-esp32.git#idf-release/v4.2 @@ -65,6 +65,7 @@ build_flags = -D OSW_SERVICE_CONSOLE -D OSW_FEATURE_WIFI -D LUA_C89_NUMBERS ; Required by OSW_FEATURE_LUA + -Wdouble-promotion extra_scripts = pre:scripts/build/prebuild_info.py pre:scripts/build/prebuild_assets.py diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 000000000..483bc0cfc --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,6 @@ +# This file was automatically generated for projects +# without default 'CMakeLists.txt' file. + +FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) + +idf_component_register(SRCS ${app_sources}) diff --git a/src/OswAppV2.cpp b/src/OswAppV2.cpp index bf6fa11ff..93ada596d 100644 --- a/src/OswAppV2.cpp +++ b/src/OswAppV2.cpp @@ -55,7 +55,7 @@ void OswAppV2::onLoop() { const unsigned short longPressTime = OswConfigAllKeys::oswAppV2ButtonLongPress.get(); const unsigned short veryLongPressTime = OswConfigAllKeys::oswAppV2ButtonVeryLongPress.get(); - const unsigned long indicatorMinTime = minPressTime + (longPressTime * 0.2); + const unsigned long indicatorMinTime = minPressTime + (longPressTime * 0.2f); for(char i = 0; i < BTN_NUMBER; i++) { if(hal->btnIsDownSince((Button) i) > 0) { if(buttonDownSince[i] == 0) { @@ -110,11 +110,11 @@ void OswAppV2::onLoop() { // Now update the indicator-levels for the buttons if(hal->btnIsDownFor((Button) i) > indicatorMinTime) { float progress = (hal->btnIsDownFor((Button) i) - indicatorMinTime) / (float) longPressTime; - if(progress > 1.0) { + if(progress > 1.0f) { // Oh! The button is down for a very long time! - progress = 1.0 + (hal->btnIsDownFor((Button) i) - indicatorMinTime - longPressTime) / (float) (veryLongPressTime - longPressTime); - if(progress > 2.0) - progress = 2.0; + progress = 1.0f + (hal->btnIsDownFor((Button) i) - indicatorMinTime - longPressTime) / (float) (veryLongPressTime - longPressTime); + if(progress > 2.0f) + progress = 2.0f; } if(this->buttonIndicatorProgress[i] != progress) { this->buttonIndicatorProgress[i] = progress; @@ -124,8 +124,8 @@ void OswAppV2::onLoop() { this->needsRedraw = true; } } - } else if(this->buttonIndicatorProgress[i] != 0.0) { - this->buttonIndicatorProgress[i] = 0.0; + } else if(this->buttonIndicatorProgress[i] != 0.0f) { + this->buttonIndicatorProgress[i] = 0.0f; this->needsRedraw = true; } } @@ -142,7 +142,7 @@ void OswAppV2::onDrawOverlay() { if(this->knownButtonStates[i] == 0) continue; // If the button is not down, skip it - if(this->buttonIndicatorProgress[i] == 0.0) + if(this->buttonIndicatorProgress[i] == 0.0f) continue; int16_t btnX = 0; @@ -160,11 +160,11 @@ void OswAppV2::onDrawOverlay() { const int16_t longRad = 24; const int16_t veryLongRad = 14; - if(this->knownButtonStates[i] & ButtonStateNames::VERY_LONG_PRESS and this->buttonIndicatorProgress[i] > 1.0) { + if(this->knownButtonStates[i] & ButtonStateNames::VERY_LONG_PRESS and this->buttonIndicatorProgress[i] > 1.0f) { // Very long-press circle - const float overcut = 0.2; - const float secondRad = (1.0 + overcut) * veryLongRad; - hal->gfx()->fillCircle(btnX, btnY, (1.0 - overcut) * longRad + (this->buttonIndicatorProgress[i] - 1.0) * secondRad, OswUI::getInstance()->getWarningColor()); + const float overcut = 0.2f; + const float secondRad = (1.0f + overcut) * veryLongRad; + hal->gfx()->fillCircle(btnX, btnY, (1.0f - overcut) * longRad + (this->buttonIndicatorProgress[i] - 1.0f) * secondRad, OswUI::getInstance()->getWarningColor()); } if(this->knownButtonStates[i] & ButtonStateNames::LONG_PRESS) { // Long-press circle diff --git a/src/animations/anim_firework.cpp b/src/animations/anim_firework.cpp index 19cde56c3..38bb71576 100644 --- a/src/animations/anim_firework.cpp +++ b/src/animations/anim_firework.cpp @@ -6,19 +6,19 @@ void Particle::tick(long ms, float friction, float gravity) { // update position - locationX += speedX * (ms / 1000.0); - locationY += speedY * (ms / 1000.0); + locationX += speedX * (ms / 1000.0f); + locationY += speedY * (ms / 1000.0f); // update velocity - speedX = speedX - friction * (ms / 1000.0); - speedY = speedY + gravity * (ms / 1000.0); + speedX = speedX - friction * (ms / 1000.0f); + speedY = speedY + gravity * (ms / 1000.0f); // printf("particle at %d/%d\n", locationX, locationY); } void Firework::init(uint16_t color_, uint8_t radius, uint8_t rings, // uint16_t screenWidth, uint16_t screenHeight) { height = 0; - explHeight = random((float)screenHeight * 0.2, (float)screenHeight * 0.8); + explHeight = random((float)screenHeight * 0.2f, (float)screenHeight * 0.8f); age = 0; color = color_; @@ -26,7 +26,7 @@ void Firework::init(uint16_t color_, uint8_t radius, uint8_t rings, // // precalculate particle starting points float pointsOnRing = ((float)numParticles / (float)rings); uint8_t ring = (i / pointsOnRing) + 1; - float angle = (360.0 / pointsOnRing) * i; + float angle = (360.0f / pointsOnRing) * i; particles[i].locationX = rpx(0, ring * radius, angle); particles[i].locationY = rpy(0, ring * radius, angle); @@ -39,10 +39,10 @@ void Firework::init(uint16_t color_, uint8_t radius, uint8_t rings, // void Firework::tick(long ms, uint8_t launchSpeed) { if (height < explHeight) { - height += launchSpeed * (ms / 100.0); + height += launchSpeed * (ms / 100.0f); } else { for (uint8_t i = 0; i < numParticles; i++) { - particles[i].tick(ms, .1, 9.8); + particles[i].tick(ms, 0.1f, 9.8f); } color = dimColor(color, age / 1000); diff --git a/src/animations/demos/mix-face.cpp.inactive b/src/animations/demos/mix-face.cpp.inactive index ed2e832e4..2d903f265 100644 --- a/src/animations/demos/mix-face.cpp.inactive +++ b/src/animations/demos/mix-face.cpp.inactive @@ -112,11 +112,11 @@ class MixFace : public SDLWindowRGB565 { gfx2d_print.setTextLeftAligned(); gfx2d_print.setTextCursor(120 + alp, 90); - gfx2d_print.printDecimal((int)(x*0.2)%31+1, 2); + gfx2d_print.printDecimal((int)(x*0.2f)%31+1, 2); gfx2d_print.print("."); - gfx2d_print.printDecimal((int)(x*0.6)% 12+1, 2); + gfx2d_print.printDecimal((int)(x*0.6f)% 12+1, 2); gfx2d_print.print("."); - gfx2d_print.printDecimal((int)(x*0.9)%100,2); // Full year-name is overflowing + gfx2d_print.printDecimal((int)(x*0.9f)%100,2); // Full year-name is overflowing ////time gfx2d_print.setTextSize(3); @@ -124,9 +124,9 @@ class MixFace : public SDLWindowRGB565 { gfx2d_print.setTextLeftAligned(); gfx2d_print.setTextCursor(120 + alp, 120); - gfx2d_print.printDecimal((int)(x * 0.4) % 12, 2); + gfx2d_print.printDecimal((int)(x * 0.4f) % 12, 2); gfx2d_print.print(":"); - gfx2d_print.printDecimal((int)(x * 0.8) % 60, 2); + gfx2d_print.printDecimal((int)(x * 0.8f) % 60, 2); gfx2d_print.setTextSize(1); gfx2d_print.setTextMiddleAligned(); @@ -146,9 +146,9 @@ class MixFace : public SDLWindowRGB565 { boxHeight = boxHeight < 2 ? 0 : boxHeight; // step bars - gfx2d_print.fillFrame(((240 / 2) - 8 * 3.5) + i * 8, 180 + (32 - boxHeight), 8, boxHeight, rgb888to565(rgb888(32, 156, 238))); + gfx2d_print.fillFrame(((240 / 2) - 8 * 3.5f) + i * 8, 180 + (32 - boxHeight), 8, boxHeight, rgb888to565(rgb888(32, 156, 238))); - gfx2d_print.drawRFrame(((240 / 2) - 8 * 3.5) + i * 8, 180, 8, 32, 2, rgb888to565(rgb888(122, 122, 122))); + gfx2d_print.drawRFrame(((240 / 2) - 8 * 3.5f) + i * 8, 180, 8, 32, 2, rgb888to565(rgb888(122, 122, 122))); } // labels @@ -156,7 +156,7 @@ class MixFace : public SDLWindowRGB565 { gfx2d_print.setTextBottomAligned(); gfx2d_print.setTextSize(1); gfx2d_print.setTextCursor(240 / 2, 180 - 1); - gfx2d_print.print((int)(x * 0.04)); // today step counter + gfx2d_print.print((int)(x * 0.04f)); // today step counter gfx2d_print.setTextCursor(240 / 2, 180 + 1 + 8 + 8 * 4); gfx2d_print.print(x); // total step counter } diff --git a/src/animations/demos/rotation/main.cpp.inactive b/src/animations/demos/rotation/main.cpp.inactive index 3e94c9c72..cc76ac979 100644 --- a/src/animations/demos/rotation/main.cpp.inactive +++ b/src/animations/demos/rotation/main.cpp.inactive @@ -94,7 +94,7 @@ class RotationExampleWindow : public SDLWindowRGB565 { waterBackground.drawGraphics2D_rotated(lx2, ly2, &leaf1, 16, 16, -counter / 50.0); waterBackground.drawGraphics2D_rotated(lx3, ly3, &leaf2, 16, 16, counter / 50.0); waterBackground.drawGraphics2D_rotated(lx4, ly4, &leaf3, 16, 16, -counter / 50.0); - // gfx2d.disableAplha(); + // gfx2d.disableAlpha(); calcWater(wbuf1, wbuf2, WATER_W, WATER_H, .9); mapWater(wbuf1, WATER_W, WATER_H, &waterBackground, &waterScreenBuffer, 0, 0); diff --git a/src/apps/_experiments/autumn.cpp b/src/apps/_experiments/autumn.cpp index 4d5d6f305..97cbedddf 100644 --- a/src/apps/_experiments/autumn.cpp +++ b/src/apps/_experiments/autumn.cpp @@ -122,7 +122,7 @@ void OswAppAutumn::loop() { waterBackground->drawGraphics2D_rotated(lx2, ly2, leaf1, 16, 16, -counter / 50.0); waterBackground->drawGraphics2D_rotated(lx3, ly3, leaf2, 16, 16, counter / 50.0); waterBackground->drawGraphics2D_rotated(lx4, ly4, leaf3, 16, 16, -counter / 50.0); - // gfx2d.disableAplha(); + // gfx2d.disableAlpha(); calcWater(wbuf1, wbuf2, WATER_W, WATER_H, .9); mapWater(wbuf1, WATER_W, WATER_H, waterBackground, waterScreenBuffer, 0, 0); diff --git a/src/apps/_experiments/show_display_size.cpp b/src/apps/_experiments/show_display_size.cpp index 3e81c7932..79162bbc5 100644 --- a/src/apps/_experiments/show_display_size.cpp +++ b/src/apps/_experiments/show_display_size.cpp @@ -23,8 +23,8 @@ void OswAppShowDisplaySize::loop() { uint16_t height = gfx->getHeight(); for (uint16_t y = 0; y < height; y += chunkHeight) { - float y1 = (y + (y < height / 2 ? chunkHeight : 0)) - height / 2.0; - float d = sqrt(120 * 120 - y1 * y1); + float y1 = (y + (y < height / 2 ? chunkHeight : 0)) - height / 2.0f; + float d = sqrtf(120 * 120 - y1 * y1); uint16_t xOffset = 120 - d; uint16_t chunkWidth = ceil(d * 2); gfx->fillFrame(xOffset, y, chunkWidth, chunkHeight, rgb565(0, 0, 0)); diff --git a/src/apps/clock/OswAppAlarm.cpp b/src/apps/clock/OswAppAlarm.cpp index 0a8b00a5c..5271655ba 100644 --- a/src/apps/clock/OswAppAlarm.cpp +++ b/src/apps/clock/OswAppAlarm.cpp @@ -200,7 +200,7 @@ void OswAppAlarm::listAlarms() { hal->gfx()->setTextLeftAligned(); for (size_t i{}; i < notifications.size(); ++i) { hal->gfx()->setTextSize(2); - hal->gfx()->setTextCursor(hal->gfx()->getTextOfsetColumns(1.5), DISP_H * (i + 3) / 8); + hal->gfx()->setTextCursor(hal->gfx()->getTextOfsetColumns(1.5f), DISP_H * (i + 3) / 8); hal->gfx()->setTextColor(state == AlarmState::LIST && step == i ? colorActive : colorForeground, colorBackground); auto timeToFire = notifications[i].first; date::hh_mm_ss time{floor(timeToFire - floor(timeToFire))}; @@ -212,7 +212,7 @@ void OswAppAlarm::listAlarms() { hal->gfx()->print(minutes / 10); hal->gfx()->print(minutes % 10); hal->gfx()->setTextSize(1); - hal->gfx()->setTextCursor(DISP_W / 3 + hal->gfx()->getTextOfsetColumns(1.5), DISP_H * (i + 3) / 8); + hal->gfx()->setTextCursor(DISP_W / 3 + hal->gfx()->getTextOfsetColumns(1.5f), DISP_H * (i + 3) / 8); auto myDaysOfWeek = notifications[i].second.getDaysOfWeek(); if (std::all_of(myDaysOfWeek.begin(), myDaysOfWeek.end(), [](bool x) { return x; @@ -331,12 +331,12 @@ void OswAppAlarm::loop() { hal->gfx()->print("+"); hal->gfx()->setTextRightAligned(); - hal->gfx()->setTextCursor((DISP_W / 2) - hal->gfx()->getTextOfsetColumns(0.5), DISP_H * 5 / 8); + hal->gfx()->setTextCursor((DISP_W / 2) - hal->gfx()->getTextOfsetColumns(0.5f), DISP_H * 5 / 8); hal->gfx()->setTextColor(step == 4 ? colorActive : colorForeground, colorBackground); hal->gfx()->print(LANG_CANCEL); hal->gfx()->setTextLeftAligned(); - hal->gfx()->setTextCursor((DISP_W / 2) + hal->gfx()->getTextOfsetColumns(0.5), DISP_H * 5 / 8); + hal->gfx()->setTextCursor((DISP_W / 2) + hal->gfx()->getTextOfsetColumns(0.5f), DISP_H * 5 / 8); hal->gfx()->setTextColor(step == 5 ? colorActive : colorForeground, colorBackground); hal->gfx()->print(LANG_NEXT); @@ -400,12 +400,12 @@ void OswAppAlarm::loop() { } hal->gfx()->setTextRightAligned(); - hal->gfx()->setTextCursor((DISP_W / 2) - hal->gfx()->getTextOfsetColumns(0.5), DISP_H * 10 / 12); + hal->gfx()->setTextCursor((DISP_W / 2) - hal->gfx()->getTextOfsetColumns(0.5f), DISP_H * 10 / 12); hal->gfx()->setTextColor(step == 7 ? colorActive : colorForeground, colorBackground); hal->gfx()->print(LANG_CANCEL); hal->gfx()->setTextLeftAligned(); - hal->gfx()->setTextCursor((DISP_W / 2) + hal->gfx()->getTextOfsetColumns(0.5), DISP_H * 10 / 12); + hal->gfx()->setTextCursor((DISP_W / 2) + hal->gfx()->getTextOfsetColumns(0.5f), DISP_H * 10 / 12); hal->gfx()->setTextColor(step == 8 ? colorActive : colorForeground, colorBackground); hal->gfx()->print(LANG_SAVE); } diff --git a/src/apps/clock/OswAppTimer.cpp b/src/apps/clock/OswAppTimer.cpp index 139f2c047..f29f34c43 100644 --- a/src/apps/clock/OswAppTimer.cpp +++ b/src/apps/clock/OswAppTimer.cpp @@ -166,12 +166,12 @@ void drawSetTimerScreen(unsigned char step, uint16_t colorActive, uint16_t color hal->gfx()->setTextSize(2); hal->gfx()->setTextRightAligned(); - hal->gfx()->setTextCursor((DISP_W / 2) - hal->gfx()->getTextOfsetColumns(0.5), DISP_H * 5 / 8); + hal->gfx()->setTextCursor((DISP_W / 2) - hal->gfx()->getTextOfsetColumns(0.5f), DISP_H * 5 / 8); hal->gfx()->setTextColor(step == 6 ? colorActive : colorForeground, colorBackground); hal->gfx()->print(LANG_CANCEL); hal->gfx()->setTextLeftAligned(); - hal->gfx()->setTextCursor((DISP_W / 2) + hal->gfx()->getTextOfsetColumns(0.5), DISP_H * 5 / 8); + hal->gfx()->setTextCursor((DISP_W / 2) + hal->gfx()->getTextOfsetColumns(0.5f), DISP_H * 5 / 8); hal->gfx()->setTextColor(step == 7 ? colorActive : colorForeground, colorBackground); hal->gfx()->print(LANG_START); } diff --git a/src/apps/clock/stopwatch.cpp b/src/apps/clock/stopwatch.cpp index ee2804d38..c5f544d55 100644 --- a/src/apps/clock/stopwatch.cpp +++ b/src/apps/clock/stopwatch.cpp @@ -237,8 +237,8 @@ void OswAppStopWatch::drawPageIndicator() { uint16_t alpha0 = 147 + (spacing / 2 * (lapPages-1)); // Angle of the first Element (147deg = Button 1) for(uint8_t i = 0; i < lapPages ; i++) { uint16_t alpha = alpha0 - (i * spacing); - uint16_t x = (DISP_W / 2) + (cos(alpha * PI / 180) * r); - uint16_t y = (DISP_H / 2) + (sin(alpha * PI / 180) * r); + uint16_t x = (DISP_W / 2) + (cosf(alpha * PI / 180) * r); + uint16_t y = (DISP_H / 2) + (sinf(alpha * PI / 180) * r); if(i == lapPage) { OswHal::getInstance()->gfx()->fillCircle(x, y, rDot, ui->getPrimaryColor()); } else { diff --git a/src/apps/games/brick_breaker.cpp b/src/apps/games/brick_breaker.cpp index 01853dd80..e177c5071 100644 --- a/src/apps/games/brick_breaker.cpp +++ b/src/apps/games/brick_breaker.cpp @@ -38,7 +38,7 @@ void OswAppBrickBreaker::drawGrid() { void OswAppBrickBreaker::drawBall() { OswHal::getInstance()->gfx()->fillCircle(ballPosx, ballPosy, 4, ui->getForegroundColor()); - // hal->gfx()->drawArc(120,120, 0, 360, 60, 80, 0.75,ui->getForegroundColor(),false); + // hal->gfx()->drawArc(120,120, 0, 360, 60, 80, 0.75f,ui->getForegroundColor(),false); // hal->gfx()->drawFrame(18,18,203,250,ui->getForegroundColor()); } @@ -90,7 +90,7 @@ void OswAppBrickBreaker::BrickBreaker() { const int currentTime = millis(); - deltaSeconds += (currentTime - previousTime) / (1000.0); + deltaSeconds += (currentTime - previousTime) / 1000.0f; previousTime = currentTime; @@ -151,28 +151,28 @@ void OswAppBrickBreaker::initGrid() { void OswAppBrickBreaker::hitPlayer() { pHitPosition = ballPosx - playerPos; - absspd = sqrt((ballSpdx * ballSpdx) + (ballSpdy * ballSpdy)); - pHitAngle = atan(-ballSpdy / ballSpdx); - posAngle = asin(pHitPosition / 24); + absspd = sqrtf((ballSpdx * ballSpdx) + (ballSpdy * ballSpdy)); + pHitAngle = atanf(-ballSpdy / ballSpdx); + posAngle = asinf(pHitPosition / 24); angleVar = pHitAngle + posAngle; angleVar = (PI + angleVar * 2); angleout = pHitAngle - angleVar; angleout2 = PI + angleout; if (pHitPosition >= 0) { if (ballSpdx >= 0) { - ballSpdx = cos(angleout2) * absspd; - ballSpdy = -sin(angleout2) * absspd; + ballSpdx = cosf(angleout2) * absspd; + ballSpdy = -sinf(angleout2) * absspd; } else { - ballSpdx = -cos(angleout2) * absspd; - ballSpdy = sin(angleout2) * absspd; + ballSpdx = -cosf(angleout2) * absspd; + ballSpdy = sinf(angleout2) * absspd; } } else { if (ballSpdx >= 0) { - ballSpdx = cos(angleout2) * absspd; - ballSpdy = -sin(angleout2) * absspd; + ballSpdx = cosf(angleout2) * absspd; + ballSpdy = -sinf(angleout2) * absspd; } else { - ballSpdx = -cos(angleout2) * absspd; - ballSpdy = sin(angleout2) * absspd; + ballSpdx = -cosf(angleout2) * absspd; + ballSpdy = sinf(angleout2) * absspd; } } } @@ -180,10 +180,10 @@ void OswAppBrickBreaker::hitPlayer() { void OswAppBrickBreaker::hitWall() { wallPosx = ballPosx - 120; wallPosy = -(ballPosy - 120); - absspd = sqrt((ballSpdx * ballSpdx) + (ballSpdy * ballSpdy)); + absspd = sqrtf((ballSpdx * ballSpdx) + (ballSpdy * ballSpdy)); - pHitAngle = atan(-ballSpdy / ballSpdx); - posAngle = asin(wallPosy / 116); + pHitAngle = atanf(-ballSpdy / ballSpdx); + posAngle = asinf(wallPosy / 116); angleVar = pHitAngle + posAngle; if (wallPosx <= 0) { @@ -198,64 +198,64 @@ void OswAppBrickBreaker::hitWall() { // quadrant 2 (top left) / if (ballSpdx >= 0 && ballSpdy <= 0) { // quadrant 2 x+ y- / - ballSpdx = -cos(angleout) * absspd; - ballSpdy = sin(angleout) * absspd; + ballSpdx = -cosf(angleout) * absspd; + ballSpdy = sinf(angleout) * absspd; } else if (ballSpdx <= 0 && ballSpdy <= 0) { // quadrant 2 x- y- / - ballSpdx = cos(angleout) * absspd; - ballSpdy = -sin(angleout) * absspd; + ballSpdx = cosf(angleout) * absspd; + ballSpdy = -sinf(angleout) * absspd; } else { // quadrant 2 x- y+ / - ballSpdx = -cos(angleout) * absspd; - ballSpdy = -sin(angleout) * absspd; + ballSpdx = -cosf(angleout) * absspd; + ballSpdy = -sinf(angleout) * absspd; } } else if (wallPosx <= 0 && wallPosy <= 0) { // quadrant 3 (bottom left) / if (ballSpdx <= 0 && ballSpdy >= 0) { // quadrant 3 x- y+ / - ballSpdx = cos(angleout) * absspd; - ballSpdy = -sin(angleout) * absspd; + ballSpdx = cosf(angleout) * absspd; + ballSpdy = -sinf(angleout) * absspd; } else if (ballSpdx >= 0 && ballSpdy >= 0) { // quadrant 3 x+ y+ / - ballSpdx = -cos(angleout) * absspd; - ballSpdy = sin(angleout) * absspd; + ballSpdx = -cosf(angleout) * absspd; + ballSpdy = sinf(angleout) * absspd; } else if (ballSpdx <= 0 && ballSpdy <= 0) { // quadrant 3 x- y- / - ballSpdx = -sin(angleout) * absspd; - ballSpdy = cos(angleout) * absspd; + ballSpdx = -sinf(angleout) * absspd; + ballSpdy = cosf(angleout) * absspd; } } else if (wallPosx >= 0 && wallPosy >= 0) { // quadrant 1 (top right) / if (ballSpdx >= 0 && ballSpdy <= 0) { // quadrant 1 x+ y- / - ballSpdx = -cos(angleout) * absspd; - ballSpdy = -sin(angleout) * absspd; + ballSpdx = -cosf(angleout) * absspd; + ballSpdy = -sinf(angleout) * absspd; } else if (ballSpdx >= 0 && ballSpdy >= 0) { ////quadrant 1 x+ y+ - ballSpdx = -cos(angleout) * absspd; - ballSpdy = sin(angleout) * absspd; + ballSpdx = -cosf(angleout) * absspd; + ballSpdy = sinf(angleout) * absspd; } else if (ballSpdx <= 0 && ballSpdy <= 0) { // quadrant 1 x- y- / angleVar = pHitAngle - posAngle; angleVar = (angleVar * 2); angleout = (pHitAngle - angleVar); - ballSpdx = cos(angleout) * absspd; - ballSpdy = -sin(angleout) * absspd; + ballSpdx = cosf(angleout) * absspd; + ballSpdy = -sinf(angleout) * absspd; } } else { // quadrant 4 (bottom right) / if (ballSpdx <= 0 && ballSpdy >= 0) { // quadrant 4 x- y+ / - ballSpdx = cos(angleout) * absspd; - ballSpdy = -sin(angleout) * absspd; + ballSpdx = cosf(angleout) * absspd; + ballSpdy = -sinf(angleout) * absspd; } else if (ballSpdx >= 0 && ballSpdy <= 0) { // quadrant 4 x+ y- / - ballSpdx = -cos(angleout) * absspd; - ballSpdy = sin(angleout) * absspd; + ballSpdx = -cosf(angleout) * absspd; + ballSpdy = sinf(angleout) * absspd; } else if (ballSpdx >= 0 && ballSpdy >= 0) { // quadrant 4 x+ y+ / - ballSpdx = -cos(angleout) * absspd; - ballSpdy = sin(angleout) * absspd; + ballSpdx = -cosf(angleout) * absspd; + ballSpdy = sinf(angleout) * absspd; } } } @@ -306,7 +306,7 @@ void OswAppBrickBreaker::accelerometerController() { void OswAppBrickBreaker::gravityController() { float xAcceleration = OswHal::getInstance()->environment()->getAccelerationX(); - float realAcceleration = -xAcceleration * 59.3346774; + float realAcceleration = -xAcceleration * 59.3346774f; playerSpd = playerSpd + (realAcceleration * (millis() - lastpos)); playerPos = playerPos + (playerSpd * (millis() - lastpos)); if (playerPos <= 30) { diff --git a/src/apps/games/snake_game.cpp b/src/apps/games/snake_game.cpp index 12f3bb648..6e03f1856 100644 --- a/src/apps/games/snake_game.cpp +++ b/src/apps/games/snake_game.cpp @@ -197,7 +197,7 @@ void OswAppSnakeGame::snakeGame() { const int currentTime = millis(); - deltaSeconds += (currentTime - previousTime) / (1000.0); + deltaSeconds += (currentTime - previousTime) / 1000.0f; proceedSnakeCoords(fastGame); diff --git a/src/apps/tools/OswAppCalculator.cpp b/src/apps/tools/OswAppCalculator.cpp index 2265b116e..7e8fc1cb1 100644 --- a/src/apps/tools/OswAppCalculator.cpp +++ b/src/apps/tools/OswAppCalculator.cpp @@ -181,11 +181,11 @@ String OswAppCalculator::calculate(int iNum, char signOfNum1, String mathOperati } else if (mathOperation == "/") { result = Num1 / Num2; } else if (mathOperation == "square root") { - result = sqrt(Num1); + result = sqrtf(Num1); } else if (mathOperation == "^") { - result = pow(Num1, Num2); + result = powf(Num1, Num2); } else if (mathOperation == "x10^") { - result = pow(10, Num2) * Num1; + result = powf(10, Num2) * Num1; } else if (mathOperation == "random") { result = random(-100000, 100001); result = result / 100; diff --git a/src/apps/tools/OswAppFlashLight.cpp b/src/apps/tools/OswAppFlashLight.cpp index 11c6caaad..6602eadb4 100644 --- a/src/apps/tools/OswAppFlashLight.cpp +++ b/src/apps/tools/OswAppFlashLight.cpp @@ -43,7 +43,7 @@ void OswAppFlashLight::onDraw() { this->hal->gfx()->print(int(hal->screenBrightness())); //displays the current brightness } else { this->hal->gfx()->fillCircle(120, 120, 115, blackColor); - this->hal->gfx()->setTextSize(3.5); + this->hal->gfx()->setTextSize(3); this->hal->gfx()->setTextCenterAligned(); this->hal->gfx()->setTextCursor(120, 125); this->hal->gfx()->setTextColor(whiteColor); diff --git a/src/apps/tools/OswAppKcalStats.cpp b/src/apps/tools/OswAppKcalStats.cpp index 1114c3b49..21fbeda56 100644 --- a/src/apps/tools/OswAppKcalStats.cpp +++ b/src/apps/tools/OswAppKcalStats.cpp @@ -47,12 +47,12 @@ void OswAppKcalStats::drawCurvedChart() { hal->gfx()->drawThickTick(this->cursorPos == 6 && Index == 5 ? x2 : x1, 140, 0, 60, 0, 3, ui->getForegroundColor()); } - hal->gfx()->drawLine(x1, y1, x2, y2, changeColor(ui->getSuccessColor(),2.25)); // first-second Coord - hal->gfx()->fillCircle(x1, y1, 2.5,ui->getSuccessColor() ); // draw circle on the first coord + hal->gfx()->drawLine(x1, y1, x2, y2, changeColor(ui->getSuccessColor(),2.25f)); // first-second Coord + hal->gfx()->fillCircle(x1, y1, 2.5f,ui->getSuccessColor() ); // draw circle on the first coord // last coord if (Index == 5) { - hal->gfx()->fillCircle(x2, y2, 2.5,ui->getSuccessColor() ); + hal->gfx()->fillCircle(x2, y2, 2.5f,ui->getSuccessColor() ); } } } diff --git a/src/apps/tools/OswAppTimeConfig.cpp b/src/apps/tools/OswAppTimeConfig.cpp index 8f3a6a3d5..f5be04736 100644 --- a/src/apps/tools/OswAppTimeConfig.cpp +++ b/src/apps/tools/OswAppTimeConfig.cpp @@ -244,13 +244,13 @@ void OswAppTimeConfig::loop() { // Cancel-Field hal->gfx()->setTextRightAligned(); - hal->gfx()->setTextCursor((DISP_W / 2) - hal->gfx()->getTextOfsetColumns(0.5), DISP_H * 5 / 8); + hal->gfx()->setTextCursor((DISP_W / 2) - hal->gfx()->getTextOfsetColumns(0.5f), DISP_H * 5 / 8); hal->gfx()->setTextColor(11 == manualSettingStep ? colorActive : colorForeground, colorBackground); hal->gfx()->print(LANG_CANCEL); // Done-Field hal->gfx()->setTextLeftAligned(); - hal->gfx()->setTextCursor((DISP_W / 2) + hal->gfx()->getTextOfsetColumns(0.5), DISP_H * 5 / 8); + hal->gfx()->setTextCursor((DISP_W / 2) + hal->gfx()->getTextOfsetColumns(0.5f), DISP_H * 5 / 8); hal->gfx()->setTextColor(12 == manualSettingStep ? colorActive : colorForeground, colorBackground); hal->gfx()->print(LANG_SAVE); } diff --git a/src/apps/tools/OswAppWaterLevel.cpp b/src/apps/tools/OswAppWaterLevel.cpp index 7164143f2..2d2f3f308 100644 --- a/src/apps/tools/OswAppWaterLevel.cpp +++ b/src/apps/tools/OswAppWaterLevel.cpp @@ -43,7 +43,7 @@ void OswAppWaterLevel::circlesDisplay() { const float xValue = hal->environment()->getAccelerationX(); const float yValue = hal->environment()->getAccelerationY(); - const bool isXYAccelerationInMiddle = abs(yValue) < 0.25 && abs(xValue) < 0.25; + const bool isXYAccelerationInMiddle = abs(yValue) < 0.25f && abs(xValue) < 0.25f; uint16_t color = isXYAccelerationInMiddle ? ui->getSuccessColor() : ui->getInfoColor(); @@ -79,7 +79,7 @@ void OswAppWaterLevel::drawBar(const float value, char text, const int x) { gfx->fillRFrame(x, 120 - 5 - yOffset, width, height + barHeight, 5, rgb565(redComponent, greenComponent, 0)); - bool isMiddleValue = value > -0.25 && value < 0.25; + bool isMiddleValue = value > -0.25f && value < 0.25f; const int backgroundColor = isMiddleValue ? ui->getSuccessColor() : ui->getBackgroundColor(); const int foregroundColor = isMiddleValue ? ui->getBackgroundColor() : ui->getForegroundColor(); diff --git a/src/apps/watchfaces/OswAppWatchface.cpp b/src/apps/watchfaces/OswAppWatchface.cpp index a77c8cb3e..859c776d1 100644 --- a/src/apps/watchfaces/OswAppWatchface.cpp +++ b/src/apps/watchfaces/OswAppWatchface.cpp @@ -13,6 +13,9 @@ #include "./apps/watchfaces/OswAppWatchface.h" +#define CENTER_X (DISP_W / 2) +#define CENTER_Y (DISP_H / 2) + const char* OswAppWatchface::getAppId() { return OswAppWatchface::APP_ID; } @@ -44,11 +47,11 @@ void OswAppWatchface::drawStepHistory(OswUI* ui, uint8_t x, uint8_t y, uint8_t w hal->gfx()->setTextCenterAligned(); // horiz. hal->gfx()->setTextBottomAligned(); hal->gfx()->setTextSize(1); - hal->gfx()->setTextCursor(DISP_W * 0.5, y - 1); + hal->gfx()->setTextCursor(CENTER_X, y - 1); hal->gfx()->print(hal->environment()->getStepsToday() + (OswConfigAllKeys::settingDisplayStepsGoal.get() ? String("/") + max:"")); - hal->gfx()->setTextCursor(DISP_W * 0.5, y + 1 + 8 + w * 4); + hal->gfx()->setTextCursor(CENTER_X, y + 1 + 8 + w * 4); hal->gfx()->setTextColor(ui->getForegroundColor()); // Let's make the background transparent. // See : https://github.com/Open-Smartwatch/open-smartwatch-os/issues/194 // font : WHITE / bg : None @@ -61,30 +64,30 @@ void OswAppWatchface::drawStepHistory(OswUI* ui, uint8_t x, uint8_t y, uint8_t w void OswAppWatchface::drawWatch() { OswHal* hal = OswHal::getInstance(); - hal->gfx()->drawMinuteTicks(DISP_W * 0.5, DISP_H * 0.5, 116, 112, ui->getForegroundDimmedColor()); - hal->gfx()->drawHourTicks(DISP_W * 0.5, DISP_H * 0.5, 117, 107, ui->getForegroundColor()); + hal->gfx()->drawMinuteTicks(CENTER_Y, CENTER_Y, 116, 112, ui->getForegroundDimmedColor(), true); + hal->gfx()->drawHourTicks(CENTER_X, CENTER_Y, 117, 107, ui->getForegroundColor(), true); #if OSW_PLATFORM_ENVIRONMENT_ACCELEROMETER == 1 uint32_t steps = hal->environment()->getStepsToday(); uint32_t stepsTarget = OswConfigAllKeys::stepsPerDay.get(); - hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 0, 360.0 * (float)(steps % stepsTarget) / (float)stepsTarget, 90, 93, 6, + hal->gfx()->drawArc(CENTER_X, CENTER_Y, 0, 360.0f * (float)(steps % stepsTarget) / (float)stepsTarget, 90, 93, 6, steps > stepsTarget ? ui->getSuccessColor() : ui->getInfoColor(), true); #endif #ifdef OSW_FEATURE_STATS_STEPS uint8_t w = 8; - OswAppWatchface::drawStepHistory(ui, (DISP_W / 2) - w * 3.5, 180, w, w * 4, OswConfigAllKeys::stepsPerDay.get()); + OswAppWatchface::drawStepHistory(ui, (CENTER_X) - w * 3.5f, 180, w, w * 4, OswConfigAllKeys::stepsPerDay.get()); #endif // below two arcs take too long to draw - // hal->gfx()->drawArc(120, 120, 0, 360, 180, 75, 7, changeColor(COLOR_GREEN, 0.25)); + // hal->gfx()->drawArc(120, 120, 0, 360, 180, 75, 7, changeColor(COLOR_GREEN, 0.25f)); // hal->gfx()->drawArc(120, 120, 0, (steps / 360) % 360, 180, 75, 7, dimColor(COLOR_GREEN, 25)); // hal->gfx()->drawArc(120, 120, 0, (steps / 360) % 360, 180, 75, 6, COLOR_GREEN); - // float bat = hal->getBatteryPercent() * 3.6; + // float bat = hal->getBatteryPercent() * 3.6f; - // hal->gfx()->drawArc(120, 120, 0, 360, 180, 57, 7, changeColor(COLOR_BLUE, 0.25)); + // hal->gfx()->drawArc(120, 120, 0, 360, 180, 57, 7, changeColor(COLOR_BLUE, 0.25f)); // hal->gfx()->drawArc(120, 120, 0, bat, 180, 57, 7, dimColor(COLOR_BLUE, 25)); // hal->gfx()->drawArc(120, 120, 0, bat, 180, 57, 6, COLOR_BLUE); @@ -100,29 +103,24 @@ void OswAppWatchface::drawWatch() { hal->getDualTime(&dualHour, &dualMinute, &dualSecond); // dual-hours - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 16, 360.0 / 12.0 * (1.0 * dualHour + dualMinute / 60.0), 2, ui->getBackgroundDimmedColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 16, 60, 360.0 / 12.0 * (1.0 * dualHour + dualMinute / 60.0), 5, ui->getBackgroundDimmedColor()); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 0, 16, (int)(360.0f / 12.0f * (dualHour + dualMinute / 60.0f)), 2, ui->getBackgroundDimmedColor(), true, STRAIGHT_END); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 16, 60, (int)(360.0f / 12.0f * (dualHour + dualMinute / 60.0f)), 5, ui->getBackgroundDimmedColor(), true); } // hours - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 16, 360.0 / 12.0 * (1.0 * hour + minute / 60.0), 1, ui->getForegroundColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 16, 60, 360.0 / 12.0 * (1.0 * hour + minute / 60.0), 4, ui->getForegroundColor()); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 0, (int)16, (int)(360.0f / 12.0f * (hour + minute / 60.0f)), 1, ui->getForegroundColor(), true, STRAIGHT_END); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 16, 60, (int)(360.0f / 12.0f * (hour + minute / 60.0f)), 4, ui->getForegroundColor(), true); // minutes - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 16, 360.0 / 60.0 * (1.0 * minute + second / 60.0), 1, ui->getForegroundColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 16, 105, 360.0 / 60.0 * (1.0 * minute + second / 60.0), 4, ui->getForegroundColor()); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 0, 16, (int)(360.0f / 60.0f * (minute + second / 60.0f)), 1, ui->getForegroundColor(), true, STRAIGHT_END); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 16, 105, (int)(360.0f / 60.0f * (minute + second / 60.0f)), 4, ui->getForegroundColor(), true); #ifndef GIF_BG // seconds - hal->gfx()->fillCircle(DISP_W * 0.5, DISP_H * 0.5, 3, ui->getDangerColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 16, 180 + ( 360.0 / 60.0 * second ), 1, ui->getDangerColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 110, 360.0 / 60.0 * second, 1, ui->getDangerColor()); + hal->gfx()->fillCircleAA(CENTER_X, CENTER_Y, 3, ui->getDangerColor()); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, -16, 110, 180 + 360 / 60 * second, 1, ui->getDangerColor(), true); #endif } -#ifdef GIF_BG -OswAppGifPlayer* bgGif = new OswAppGifPlayer(); -#endif - void OswAppWatchface::onStart() { OswAppV2::onStart(); this->viewFlags = OswAppV2::ViewFlags::NO_OVERLAYS; // no overlay for this watchface diff --git a/src/apps/watchfaces/OswAppWatchfaceDigital.cpp b/src/apps/watchfaces/OswAppWatchfaceDigital.cpp index 5d86c3aa4..dd8308dc7 100644 --- a/src/apps/watchfaces/OswAppWatchfaceDigital.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceDigital.cpp @@ -73,7 +73,7 @@ static void drawDate(time_t timeZone, uint8_t fontSize, uint8_t CoordY) { hal->gfx()->setTextSize(fontSize); hal->gfx()->setTextMiddleAligned(); hal->gfx()->setTextLeftAligned(); - hal->gfx()->setTextCursor(120 - hal->gfx()->getTextOfsetColumns(6.9), CoordY); + hal->gfx()->setTextCursor(120 - hal->gfx()->getTextOfsetColumns(6.9f), CoordY); OswAppWatchfaceDigital::displayWeekDay3(weekday); @@ -136,7 +136,7 @@ const char* OswAppWatchfaceDigital::getAppName() { void OswAppWatchfaceDigital::drawSteps() { #ifdef OSW_FEATURE_STATS_STEPS uint8_t w = 8; - OswAppWatchface::drawStepHistory(OswUI::getInstance(), (DISP_W / 2) - w * 3.5, 180, w, w * 4, OswConfigAllKeys::stepsPerDay.get()); + OswAppWatchface::drawStepHistory(OswUI::getInstance(), (DISP_W / 2) - w * 3.5f, 180, w, w * 4, OswConfigAllKeys::stepsPerDay.get()); #else OswHal* hal = OswHal::getInstance(); uint32_t steps = hal->environment()->getStepsToday(); diff --git a/src/apps/watchfaces/OswAppWatchfaceDual.cpp b/src/apps/watchfaces/OswAppWatchfaceDual.cpp index ab59eb97a..76dff8985 100644 --- a/src/apps/watchfaces/OswAppWatchfaceDual.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceDual.cpp @@ -25,7 +25,7 @@ */ void OswAppWatchfaceDual::drawProgressBar(OswUI* ui,uint8_t cx, uint8_t cy, uint8_t jump, uint8_t length, uint8_t value,float angle, uint8_t radius, uint16_t color, uint8_t* goal) { OswHal* hal = OswHal::getInstance(); - hal->gfx()->drawThickTick(cx, cy, jump, length, angle, radius, changeColor(color, 0.25)); + hal->gfx()->drawThickTick(cx, cy, jump, length, angle, radius, changeColor(color, 0.25f)); hal->gfx()->drawThickTick(cx, cy, jump, value, angle, radius, goal == nullptr ? color :*goalgetSuccessColor():color, true); } @@ -39,7 +39,7 @@ void OswAppWatchfaceDual::drawAnimSec() { barValue = barValue < 2 ? 0 : barValue; uint8_t coordX = (DISP_W - barWidth) / 2; uint8_t levelY = DISP_H / 2; - uint8_t radius = 1.5; + uint8_t radius = 1; drawProgressBar(ui, coordX, levelY - 1, 0, barWidth, barValue, 90, radius, ui->getPrimaryColor()); } @@ -67,7 +67,7 @@ void OswAppWatchfaceDual::onDraw() { // Set Dual Size hal->gfx()->setTextSize(2); - uint8_t mid_little = hal->gfx()->getTextOfsetRows(0.5); + uint8_t mid_little = hal->gfx()->getTextOfsetRows(0.5f); uint8_t mid = hal->gfx()->getTextOfsetRows(2); OswAppWatchfaceDigital::digitalWatch(hal->getTimezoneOffsetPrimary(),1, 120 - mid_little, 120 - mid); diff --git a/src/apps/watchfaces/OswAppWatchfaceFitness.cpp b/src/apps/watchfaces/OswAppWatchfaceFitness.cpp index 1f1b47879..9cd3640f4 100644 --- a/src/apps/watchfaces/OswAppWatchfaceFitness.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceFitness.cpp @@ -11,8 +11,8 @@ float OswAppWatchfaceFitness::calculateDistance(uint32_t steps) { uint8_t userHeight = OswConfigAllKeys::configHeight.get(); - uint16_t avgDist = ((userHeight * 0.37) + (userHeight * 0.45) + (userHeight - 100)) / 3; - return steps * avgDist * 0.01 ; // cm -> m + uint16_t avgDist = ((userHeight * 0.37f) + (userHeight * 0.45f) + (userHeight - 100)) / 3; + return steps * avgDist * 0.01f ; // cm -> m } uint32_t OswAppWatchfaceFitness::calculateKcalorie(uint32_t steps) { @@ -41,7 +41,7 @@ void dateDisplay() { hal->gfx()->setTextSize(2); hal->gfx()->setTextMiddleAligned(); hal->gfx()->setTextLeftAligned(); - hal->gfx()->setTextCursor(DISP_W * 0.5 - 30 + hal->gfx()->getTextOfsetColumns(1), 150); + hal->gfx()->setTextCursor(DISP_W / 2 - 30 + hal->gfx()->getTextOfsetColumns(1), 150); OswAppWatchfaceDigital::dateOutput(yearInt, monthInt, dayInt); } @@ -54,7 +54,7 @@ void timeDisplay(uint32_t hour, uint32_t minute, uint32_t second) { hal->gfx()->setTextSize(1); hal->gfx()->setTextMiddleAligned(); hal->gfx()->setTextLeftAligned(); - hal->gfx()->setTextCursor(215, DISP_W * 0.5); + hal->gfx()->setTextCursor(215, DISP_H / 2); hal->gfx()->printDecimal(second,2); } @@ -70,7 +70,7 @@ void digitalWatchDisplay() { hal->gfx()->setTextSize(4); hal->gfx()->setTextMiddleAligned(); hal->gfx()->setTextLeftAligned(); - hal->gfx()->setTextCursor(DISP_W * 0.5 - 30, DISP_W * 0.5); + hal->gfx()->setTextCursor(DISP_W / 2 - 30, DISP_W / 2); hal->getLocalTime(&hour, &minute, &second, &afterNoon); @@ -95,44 +95,44 @@ void OswAppWatchfaceFitness::showFitnessTracking() { uint32_t distTarget = OswConfigAllKeys::distPerDay.get(); uint32_t kcalTarget = OswConfigAllKeys::kcalPerDay.get(); - hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 180, 360, 90, 93, 7, changeColor(ui->getDangerColor(), 0.25)); - // hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 180, 280, 90, 93, 7, dimColor(rgb565(210, 50, 66), 25)); - hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 180, 180 + (180.0 * (float)(steps % stepsTarget) / (float)stepsTarget), 90, 93, 6, steps > stepsTarget ? changeColor(ui->getSuccessColor(),6.25 ): ui->getDangerColor(), true); + hal->gfx()->drawArc(DISP_W / 2, DISP_H / 2, 180, 360, 90, 93, 7, changeColor(ui->getDangerColor(), 0.25f)); + // hal->gfx()->drawArc(DISP_W / 2, DISP_H / 2, 180, 280, 90, 93, 7, dimColor(rgb565(210, 50, 66), 25)); + hal->gfx()->drawArc(DISP_W / 2, DISP_H / 2, 180, 180 + (180.0f * (float)(steps % stepsTarget) / (float)stepsTarget), 90, 93, 6, steps > stepsTarget ? changeColor(ui->getSuccessColor(),6.25f ): ui->getDangerColor(), true); - hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 180, 360, 90, 75, 7, changeColor(ui->getWarningColor(), 0.25)); - // hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 180, 180+128, 90, 75, 7, dimColor(rgb565(117, 235, 10), 25)); - hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 180, 180 + (180.0 * (float)(kcals % kcalTarget) / (float)kcalTarget), 90, 75, 6, kcals > kcalTarget ? changeColor(ui->getSuccessColor(),1.25): ui->getWarningColor(), true); + hal->gfx()->drawArc(DISP_W / 2, DISP_H / 2, 180, 360, 90, 75, 7, changeColor(ui->getWarningColor(), 0.25f)); + // hal->gfx()->drawArc(DISP_W / 2, DISP_H / 2, 180, 180+128, 90, 75, 7, dimColor(rgb565(117, 235, 10), 25)); + hal->gfx()->drawArc(DISP_W / 2, DISP_H / 2, 180, 180 + (180.0f * (float)(kcals % kcalTarget) / (float)kcalTarget), 90, 75, 6, kcals > kcalTarget ? changeColor(ui->getSuccessColor(),1.25f): ui->getWarningColor(), true); - hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 180, 360, 90, 57, 7, changeColor(ui->getInfoColor(), 0.25)); - // hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 180, 180+32, 90, 57, 7, dimColor(rgb565(25, 193, 202), 25)); - hal->gfx()->drawArc(DISP_W * 0.5, DISP_H * 0.5, 180, 180 + (180.0 * (float)(uint32_t(dists) % distTarget) / (float)distTarget), 90, 57, 6, dists > distTarget ? changeColor(ui->getSuccessColor(),2.25) : ui->getInfoColor(), true); + hal->gfx()->drawArc(DISP_W / 2, DISP_H / 2, 180, 360, 90, 57, 7, changeColor(ui->getInfoColor(), 0.25f)); + // hal->gfx()->drawArc(DISP_W / 2, DISP_H / 2, 180, 180+32, 90, 57, 7, dimColor(rgb565(25, 193, 202), 25)); + hal->gfx()->drawArc(DISP_W / 2, DISP_H / 2, 180, 180 + (180.0f * (float)(uint32_t(dists) % distTarget) / (float)distTarget), 90, 57, 6, dists > distTarget ? changeColor(ui->getSuccessColor(),2.25f) : ui->getInfoColor(), true); hal->gfx()->setTextSize(1); hal->gfx()->setTextMiddleAligned(); hal->gfx()->setTextLeftAligned(); hal->gfx()->setTextColor(dimColor(ui->getDangerColor(), 25)); - hal->gfx()->setTextCursor(DISP_W * 0.5 + 10, 25); + hal->gfx()->setTextCursor(DISP_W / 2 + 10, 25); hal->gfx()->print(steps); hal->gfx()->setTextColor(dimColor(ui->getWarningColor(), 25)); - hal->gfx()->setTextCursor(DISP_W * 0.5 + 10, 45); + hal->gfx()->setTextCursor(DISP_W / 2 + 10, 45); hal->gfx()->print(kcals); hal->gfx()->setTextColor(dimColor(ui->getInfoColor(), 25)); - hal->gfx()->setTextCursor(DISP_W * 0.5 + 10, 65); + hal->gfx()->setTextCursor(DISP_W / 2 + 10, 65); hal->gfx()->print(dists); hal->gfx()->setTextColor(dimColor(ui->getInfoColor(), 25)); - hal->gfx()->setTextCursor(DISP_W * 0.5 + 10, DISP_H-65); + hal->gfx()->setTextCursor(DISP_W / 2 + 10, DISP_H-65); hal->gfx()->print(LANG_WATCHFACE_FITNESS_DISTANCE); hal->gfx()->setTextColor(dimColor(ui->getWarningColor(), 25)); - hal->gfx()->setTextCursor(DISP_W * 0.5 + 10, DISP_H-45); + hal->gfx()->setTextCursor(DISP_W / 2 + 10, DISP_H-45); hal->gfx()->print("kcal"); hal->gfx()->setTextColor(dimColor(ui->getDangerColor(), 25)); - hal->gfx()->setTextCursor(DISP_W * 0.5 + 10, DISP_H-25); + hal->gfx()->setTextCursor(DISP_W / 2 + 10, DISP_H-25); hal->gfx()->print(LANG_WATCHFACE_FITNESS_STEP); } diff --git a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp new file mode 100644 index 000000000..e6083a5bd --- /dev/null +++ b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp @@ -0,0 +1,273 @@ +#include "apps/watchfaces/OswAppWatchfaceFitnessAnalog.h" +#include "apps/watchfaces/OswAppWatchfaceDigital.h" +#include "apps/watchfaces/OswAppWatchface.h" + +#ifdef GIF_BG +#include "./apps/_experiments/gif_player.h" +#endif + +#include +#include +#include +#include +#include +#include + +#define CENTER_X (DISP_W / 2) +#define CENTER_Y (DISP_H / 2) + +inline uint32_t OswAppWatchfaceFitnessAnalog::calculateDistance(uint32_t steps) { + float userHeight = OswConfigAllKeys::configHeight.get(); + float avgDist; + if (userHeight < 180) + avgDist = userHeight * 0.40f; + else + avgDist = userHeight * 0.48f; + + return steps * avgDist * 0.01f; // cm -> m +} + +void OswAppWatchfaceFitnessAnalog::showFitnessTracking(OswHal* hal) { + uint32_t steps = hal->environment()->getStepsToday(); + uint32_t dists = OswAppWatchfaceFitnessAnalog::calculateDistance(steps); + + uint32_t stepsTarget = OswConfigAllKeys::stepsPerDay.get(); + uint32_t distTarget = OswConfigAllKeys::distPerDay.get(); + + uint8_t arcRadius = 6; + uint16_t yellow = rgb565(255, 255,0); + +#ifdef OSW_EMULATOR + steps = 4000; + dists = 3000; +#endif + + { + // draw step arc + int32_t angle_val = 180.0f * (float)min(steps, stepsTarget) / (float)stepsTarget; + uint16_t color = yellow; + uint16_t dimmed_color = changeColor(color, 0.25f); + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, 92 +arcRadius, arcRadius*2, dimmed_color, 90, 270-angle_val); + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y -92, arcRadius, 0, dimmed_color); + + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, 92 +arcRadius, arcRadius*2, steps > stepsTarget ? dimmed_color : color, 270-angle_val, 270); + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y +92, arcRadius, 0, steps > stepsTarget ? dimmed_color : color); + int x = CENTER_X + cosf((270-angle_val)*PI/180) * 92.0f; + int y = CENTER_Y - sinf((270-angle_val)*PI/180) * 92.0f; + hal->gfx()->drawCircleAA(x, y, arcRadius, 0, color); + } + + { + // draw distance arc + int32_t angle_val = 180.0f * (float)min(dists, distTarget) / (float)distTarget; + uint16_t color = ui->getInfoColor(); + uint16_t dimmed_color = changeColor(color, 0.25f); + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, 75 +arcRadius, arcRadius*2, dimmed_color, 90, 270-angle_val); + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y -75, arcRadius, 0, dimmed_color, 0.25f); + + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, 75 +arcRadius, arcRadius*2, steps > stepsTarget ? dimmed_color: color, 270-angle_val, 270); + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y +75, arcRadius, 0, steps > stepsTarget ? dimmed_color: color); + int x = CENTER_X + cosf((270-angle_val)*PI/180) * 75.0f; + int y = CENTER_Y - sinf((270-angle_val)*PI/180) * 75.0f; + hal->gfx()->drawCircleAA(x, y, arcRadius, 0, color); + } + + hal->gfx()->setTextSize(1); + hal->gfx()->setTextLeftAligned(); + + hal->gfx()->setTextColor(dimColor(yellow, 25)); + hal->gfx()->setTextCursor(CENTER_X + 12, 8+23); + hal->gfx()->print(steps); + hal->gfx()->setTextCursor(CENTER_X + 12, DISP_H-23); + hal->gfx()->print(LANG_WATCHFACE_FITNESS_STEP); + + hal->gfx()->setTextColor(dimColor(ui->getInfoColor(), 24)); + hal->gfx()->setTextCursor(CENTER_X + 12, 8+40); + hal->gfx()->print(dists); + hal->gfx()->setTextCursor(CENTER_X + 12, DISP_H-40); + hal->gfx()->print(LANG_WATCHFACE_FITNESS_DISTANCE); +} + +void OswAppWatchfaceFitnessAnalog::drawWatchFace(OswHal* hal, uint32_t hour, uint32_t minute, uint32_t second, bool afterNoon) { + // Indices + hal->gfx()->drawMinuteTicks(CENTER_X, CENTER_Y, 116, 112, ui->getForegroundDimmedColor(), true); + hal->gfx()->drawHourTicks(CENTER_X, CENTER_Y, 117, 107, ui->getForegroundColor(), true); + + // Hours + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 0, 16, (int)(360.0f / 12.0f * (hour + minute / 60.0f)), 3, ui->getForegroundColor(), true, STRAIGHT_END); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 16, 60, (int)(360.0f / 12.0f * (hour + minute / 60.0f)), 7, ui->getForegroundColor(), true); + + // Minutes + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 0, 16, (int)(360.0f / 60.0f * (minute + second / 60.0f)), 3, ui->getForegroundColor(), true, STRAIGHT_END); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 16, 105, (int)(360.0f / 60.0f * (minute + second / 60.0f)), 7, ui->getForegroundColor(), true); + +#ifndef GIF_BG + // Seconds + hal->gfx()->fillCircleAA(CENTER_X, CENTER_Y, 6, ui->getDangerColor()); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, -16, 110, 360 / 60 * second, 3, ui->getDangerColor(), true); +#endif +} + +void OswAppWatchfaceFitnessAnalog::drawDateFace(OswHal* hal, uint32_t hour, uint32_t minute, uint32_t second, bool afterNoon) { + const char* weekday = hal->getLocalWeekday(); + + hal->gfx()->setTextSize(2); + hal->gfx()->setTextRightAligned(); + hal->gfx()->setTextCursor(205, 75); + OswAppWatchfaceDigital::displayWeekDay3(weekday); + + // Date + uint32_t dayInt = 0; + uint32_t monthInt = 0; + uint32_t yearInt = 0; + hal->getLocalDate(&dayInt, &monthInt, &yearInt); + hal->gfx()->setTextSize(3); + hal->gfx()->setTextLeftAligned(); + hal->gfx()->setTextCursor(CENTER_X - 70, 170); + OswAppWatchfaceDigital::dateOutput(yearInt, monthInt, dayInt); + + hal->gfx()->setTextSize(4); + hal->gfx()->setTextLeftAligned(); + hal->gfx()->setTextCursor(CENTER_X - 35, CENTER_Y); + + hal->gfx()->printDecimal(hour, 2); + hal->gfx()->print(":"); + hal->gfx()->printDecimal(minute, 2); + + hal->gfx()->setTextSize(2); + hal->gfx()->setTextLeftAligned(); + hal->gfx()->setTextCursor(215, CENTER_Y); + hal->gfx()->printDecimal(second,2); + + if (!OswConfigAllKeys::timeFormat.get()) { + const char am[] = "AM"; + const char pm[] = "PM"; + hal->gfx()->setTextCursor(215, 130); + if (afterNoon) { + hal->gfx()->print(pm); + } else { + hal->gfx()->print(am); + } + } + +#if OSW_PLATFORM_ENVIRONMENT_TEMPERATURE == 1 + /* + printStatus("Temperature", String(hal->environment()->getTemperature() + String("C")).c_str()); + for(auto& d : *OswTemperatureProvider::getAllTemperatureDevices()) + printStatus((String(" ") + d->getName()).c_str(), String(d->getTemperature() + String("C")).c_str()); + */ + hal->gfx()->setTextSize(2); + hal->gfx()->setTextLeftAligned(); + hal->gfx()->setTextCursor(DISP_W * 0.2f, DISP_H * 0.2f); + hal->gfx()->print(hal->environment()->getTemperature(), 1); + hal->gfx()->print("C"); +#endif +} + +const char* OswAppWatchfaceFitnessAnalog::getAppId() { + return OswAppWatchfaceFitnessAnalog::APP_ID; +} + +const char* OswAppWatchfaceFitnessAnalog::getAppName() { + return LANG_AFIT; +} + +void OswAppWatchfaceFitnessAnalog::onStart() { + OswAppV2::onStart(); + OswAppWatchface::addButtonDefaults(this->knownButtonStates); + + // Report that we support short presses on all buttons + this->knownButtonStates[Button::BUTTON_SELECT] = ButtonStateNames::SHORT_PRESS ; + this->knownButtonStates[Button::BUTTON_UP] = ButtonStateNames::SHORT_PRESS; + this->knownButtonStates[Button::BUTTON_DOWN] = ButtonStateNames::SHORT_PRESS; + + // Here is a snippet to also "support" double presses (on BUTTON_SELECT) - note that this WILL DELAY the reporting of any short press events on that button (as it may needs to wait for the second press) + this->knownButtonStates[Button::BUTTON_SELECT] = (OswAppV2::ButtonStateNames) (this->knownButtonStates[Button::BUTTON_SELECT] | OswAppV2::ButtonStateNames::DOUBLE_PRESS); // OR to set the bit + this->knownButtonStates[Button::BUTTON_UP] = (OswAppV2::ButtonStateNames) (this->knownButtonStates[Button::BUTTON_UP] | OswAppV2::ButtonStateNames::DOUBLE_PRESS); // OR to set the bit + this->knownButtonStates[Button::BUTTON_DOWN] = (OswAppV2::ButtonStateNames) (this->knownButtonStates[Button::BUTTON_DOWN] | OswAppV2::ButtonStateNames::DOUBLE_PRESS); // OR to set the bit + + this->lastTime = time(nullptr); // use +} + +void OswAppWatchfaceFitnessAnalog::onLoop() { + OswAppV2::onLoop(); + + this->needsRedraw = this->needsRedraw or time(nullptr) != this->lastTime; // redraw every second +} + +void OswAppWatchfaceFitnessAnalog::onDraw() { +#ifndef OSW_EMULATOR + unsigned long old_micros = micros(); +#else + unsigned long old_millis = millis(); +#endif + + OswAppV2::onDraw(); + +#ifdef GIF_BG + if(this->bgGif != nullptr) + this->bgGif->loop(); +#endif + + OswHal* hal = OswHal::getInstance(); + + uint32_t second = 0; + uint32_t minute = 0; + uint32_t hour = 0; + bool afterNoon; + hal->getLocalTime(&hour, &minute, &second, &afterNoon); + + if (this->screen == 0) { +#if OSW_PLATFORM_ENVIRONMENT_ACCELEROMETER == 1 + showFitnessTracking(hal); +#endif + + drawWatchFace(hal, hour, minute, second, afterNoon); + } else if (this->screen == 1) { + drawDateFace(hal, hour, minute, second, afterNoon); + + static int wait_time = 1; + if (wait_time >= 0) + --wait_time; + else { + this->screen = 0; + wait_time = 1; + } + } + + this->lastTime = time(nullptr); + +#ifndef OSW_EMULATOR + unsigned long ms_for_onDraw = (micros()-old_micros)/1000; +#else + unsigned long ms_for_onDraw = millis()-old_millis; +#endif +} + +void OswAppWatchfaceFitnessAnalog::onButton(Button id, bool up, OswAppV2::ButtonStateNames state) { + OswAppV2::onButton(id, up, state); + + if(!up) return; + + if (state == OswAppV2::ButtonStateNames::DOUBLE_PRESS) { + if (this->screen < 1) + ++this->screen; + return; + } + + if(OswAppWatchface::onButtonDefaults(*this, id, up, state)) + return; // if the button was handled by the defaults, we are done here +} + +void OswAppWatchfaceFitnessAnalog::onStop() { + OswAppV2::onStop(); // always make sure to call the base class method! + // This is where you de-initialize stuff, gets called when another app is shown + +#ifdef GIF_BG + if(this->bgGif != nullptr) { + this->bgGif->stop(); + delete this->bgGif; + this->bgGif = nullptr; + } +#endif +} diff --git a/src/apps/watchfaces/OswAppWatchfaceMix.cpp b/src/apps/watchfaces/OswAppWatchfaceMix.cpp index ac455c93b..2b7a0875f 100644 --- a/src/apps/watchfaces/OswAppWatchfaceMix.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceMix.cpp @@ -32,12 +32,20 @@ void OswAppWatchfaceMix::analogWatchDisplay() { hal->gfx()->drawHourTicks((int)(DISP_W*0.5)-OFF_SET_ANALOG_WATCH_X_COORD, 100, 45, 40, ui->getForegroundDimmedColor()); // hour - hal->gfx()->drawLine((int)(DISP_W*0.5)-OFF_SET_ANALOG_WATCH_X_COORD, 100, rpx((int)(DISP_W*0.5)-OFF_SET_ANALOG_WATCH_X_COORD, (int)(33 * 0.5), hour * 30 + (int)( minute* 0.1 ) * 6), rpy(100, (int)(33 * 0.5), hour * 30 + (int)( minute* 0.1 ) * 6 ), ui->getForegroundColor()); + hal->gfx()->drawLine(DISP_W/2-OFF_SET_ANALOG_WATCH_X_COORD, 100, + rpx(DISP_W/3-OFF_SET_ANALOG_WATCH_X_COORD, 33 / 2, (int32_t) (hour * 30 + minute/10 * 6)), + rpy(100, 33 / 2, (int32_t)(hour * 30 + minute/10 * 6)), ui->getForegroundColor()); // minute - hal->gfx()->drawLine((int)(DISP_W*0.5)-OFF_SET_ANALOG_WATCH_X_COORD, 100, rpx((int)(DISP_W*0.5)-OFF_SET_ANALOG_WATCH_X_COORD, (int)(66 * 0.5), minute * 6), rpy(100, (int)(66 * 0.5), minute * 6), ui->getSuccessColor()); + hal->gfx()->drawLine(DISP_W/2-OFF_SET_ANALOG_WATCH_X_COORD, 100, + rpx(DISP_W/2-OFF_SET_ANALOG_WATCH_X_COORD, 66 / 2, (int32_t) minute * 6), + rpy(100, 66 / 2, (int32_t)(minute * 6)), ui->getSuccessColor()); // second - hal->gfx()->drawLine((int)(DISP_W*0.5)-OFF_SET_ANALOG_WATCH_X_COORD, 100, rpx((int)(DISP_W*0.5)-OFF_SET_ANALOG_WATCH_X_COORD, (int)(15 * 0.5), s2d(second) + 180), rpy(100, (int)(15 * 0.5), s2d(second) + 180), ui->getDangerColor()); // short backwards - hal->gfx()->drawLine((int)(DISP_W*0.5)-OFF_SET_ANALOG_WATCH_X_COORD, 100, rpx((int)(DISP_W*0.5)-OFF_SET_ANALOG_WATCH_X_COORD, (int)(90 * 0.5), s2d(second)), rpy(100, (int)(90 * 0.5), s2d(second)), ui->getDangerColor()); // long front + hal->gfx()->drawLine(DISP_W/2-OFF_SET_ANALOG_WATCH_X_COORD, 100, + rpx(DISP_W/2-OFF_SET_ANALOG_WATCH_X_COORD, 15 / 2, s2d(second) + 180), + rpy(100, (int)(15 * 0.5f), s2d(second) + 180), ui->getDangerColor()); // short backwards + hal->gfx()->drawLine(DISP_W/2-OFF_SET_ANALOG_WATCH_X_COORD, 100, + rpx(DISP_W/2-OFF_SET_ANALOG_WATCH_X_COORD, 90 / 2, s2d(second)), + rpy(100, (int)(90 * 0.5f), s2d(second)), ui->getDangerColor()); // long front } void OswAppWatchfaceMix::dateDisplay() { @@ -89,7 +97,7 @@ void OswAppWatchfaceMix::digitalWatchDisplay() { hal->gfx()->setTextLeftAligned(); hal->gfx()->setTextBottomAligned(); hal->gfx()->setTextSize(3); - hal->gfx()->setTextCursor(DISP_W / 2 - OFF_SET_DATE_DIGITAL_WATCH_X_COORD + hal->gfx()->getTextOfsetColumns(5.25), 130); + hal->gfx()->setTextCursor(DISP_W / 2 - OFF_SET_DATE_DIGITAL_WATCH_X_COORD + hal->gfx()->getTextOfsetColumns(5.25f), 130); hal->gfx()->setTextSize(1); hal->gfx()->print(" "); if (afterNoon) { diff --git a/src/apps/watchfaces/OswAppWatchfaceMonotimer.cpp b/src/apps/watchfaces/OswAppWatchfaceMonotimer.cpp index d10b1503e..5019be731 100644 --- a/src/apps/watchfaces/OswAppWatchfaceMonotimer.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceMonotimer.cpp @@ -25,7 +25,7 @@ * @param color color code */ void OswAppWatchfaceMonotimer::drawNShiftedTicks(Graphics2D* gfx, uint8_t cx, uint8_t cy, uint8_t r1, uint8_t r2, uint8_t nTicks, float shift, uint16_t color) { - float deltaAngle = 360.0 / nTicks; + float deltaAngle = 360.0f / nTicks; for (uint16_t i=0; idrawTick(cx, cy, r1, r2, (i * deltaAngle) + shift, color); } @@ -44,7 +44,7 @@ void OswAppWatchfaceMonotimer::drawNShiftedTicks(Graphics2D* gfx, uint8_t cx, ui * @param color color code */ void OswAppWatchfaceMonotimer::drawNShiftedMaskedTicks(Graphics2D* gfx, uint8_t cx, uint8_t cy, uint8_t r1, uint8_t r2, uint8_t nTicks, float shift, uint16_t m, uint16_t color) { - float deltaAngle = 360.0 / nTicks; + float deltaAngle = 360.0f / nTicks; for (uint16_t i=0; idrawTick(cx, cy, r1, r2, (i * deltaAngle) + shift, color); @@ -79,11 +79,11 @@ void OswAppWatchfaceMonotimer::drawWatch() { // hours hal->gfx()->drawNTicks(120, 120, 117, 100, 12, ui->getForegroundColor()); // 30 minutes - drawNShiftedTicks(hal->gfx(), 120, 120, 117, 105, 12, 360.0/24.0, ui->getForegroundColor()); + drawNShiftedTicks(hal->gfx(), 120, 120, 117, 105, 12, 360.0f/24.0f, ui->getForegroundColor()); // 15 minutes - drawNShiftedTicks(hal->gfx(), 120, 120, 110, 105, 24, 360.0/48.0, ui->getForegroundColor()); + drawNShiftedTicks(hal->gfx(), 120, 120, 110, 105, 24, 360.0f/48.0f, ui->getForegroundColor()); // 5 minutes - drawNShiftedMaskedTicks(hal->gfx(), 120, 120, 109, 108, 144, 0.0, 3, ui->getForegroundColor()); + drawNShiftedMaskedTicks(hal->gfx(), 120, 120, 109, 108, 144, 0.0f, 3, ui->getForegroundColor()); // hour labels hal->gfx()->setTextSize(2); @@ -115,10 +115,10 @@ void OswAppWatchfaceMonotimer::drawWatch() { uint32_t dualHour = 0; hal->getDualTime(&dualHour, &dualMinute, &dualSecond); - hal->gfx()->drawThickTick(120, 120, 0, 105, (360.0 * (60 * dualHour + dualMinute)) / 720.0, 1, ui->getBackgroundDimmedColor()); + hal->gfx()->drawThickTick(120, 120, 0, 105, (360.0f * (60 * dualHour + dualMinute)) / 720.0f, 1, ui->getBackgroundDimmedColor()); } - hal->gfx()->drawThickTick(120, 120, 0, 105, (360.0 * (60 * hour + minute)) / 720.0, 1, ui->getForegroundColor()); + hal->gfx()->drawThickTick(120, 120, 0, 105, (360.0f * (60 * hour + minute)) / 720.0f, 1, ui->getForegroundColor()); hal->gfx()->fillEllipse(120, 120, 4, 4, ui->getForegroundColor()); } diff --git a/src/apps/watchfaces/OswAppWatchfaceNumerals.cpp b/src/apps/watchfaces/OswAppWatchfaceNumerals.cpp index f228c38e1..ab5d49a92 100644 --- a/src/apps/watchfaces/OswAppWatchfaceNumerals.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceNumerals.cpp @@ -22,8 +22,8 @@ const char* OswAppWatchfaceNumerals::getAppName() { void OswAppWatchfaceNumerals::drawWatch() { OswHal* hal = OswHal::getInstance(); - hal->gfx()->drawMinuteTicks(DISP_W * 0.5, DISP_H * 0.5, 116, 112, ui->getForegroundDimmedColor()); - hal->gfx()->drawHourTicks(DISP_W * 0.5, DISP_H * 0.5, 117, 107, ui->getForegroundColor()); + hal->gfx()->drawMinuteTicks(DISP_W / 2, DISP_H / 2, 116, 112, ui->getForegroundDimmedColor()); + hal->gfx()->drawHourTicks(DISP_W / 2, DISP_H / 2, 117, 107, ui->getForegroundColor()); // hour labels hal->gfx()->setTextSize(1); @@ -72,23 +72,23 @@ void OswAppWatchfaceNumerals::drawWatch() { hal->getDualTime(&dualHour, &dualMinute, &dualSecond); // dual-hours - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 16, 360.0 / 12.0 * (1.0 * dualHour + dualMinute / 60.0), 2, ui->getBackgroundDimmedColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 16, 60, 360.0 / 12.0 * (1.0 * dualHour + dualMinute / 60.0), 5, ui->getBackgroundDimmedColor()); + hal->gfx()->drawThickTick(DISP_W / 2, DISP_H / 2, 0, 16, 360.0f / 12.0f * (dualHour + dualMinute / 60.0f), 2, ui->getBackgroundDimmedColor()); + hal->gfx()->drawThickTick(DISP_W / 2, DISP_H / 2, 16, 60, 360.0f / 12.0f * (dualHour + dualMinute / 60.0f), 5, ui->getBackgroundDimmedColor()); } // hours - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 16, 360.0 / 12.0 * (1.0 * hour + minute / 60.0), 1, ui->getForegroundColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 16, 60, 360.0 / 12.0 * (1.0 * hour + minute / 60.0), 4, ui->getForegroundColor()); + hal->gfx()->drawThickTick(DISP_W / 2, DISP_H / 2, 0, 16, 360.0f / 12.0f * (hour + minute / 60.0f), 1, ui->getForegroundColor()); + hal->gfx()->drawThickTick(DISP_W / 2, DISP_H / 2, 16, 60, 360.0f / 12.0f * (hour + minute / 60.0f), 4, ui->getForegroundColor()); // minutes - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 16, 360.0 / 60.0 * (1.0 * minute + second / 60.0), 1, ui->getForegroundColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 16, 80, 360.0 / 60.0 * (1.0 * minute + second / 60.0), 4, ui->getForegroundColor()); + hal->gfx()->drawThickTick(DISP_W / 2, DISP_H / 2, 0, 16, 360.0f / 60.0f * (minute + second / 60.0f), 1, ui->getForegroundColor()); + hal->gfx()->drawThickTick(DISP_W / 2, DISP_H / 2, 16, 80, 360.0f / 60.0f * (minute + second / 60.0f), 4, ui->getForegroundColor()); #ifndef GIF_BG // seconds - hal->gfx()->fillCircle(DISP_W * 0.5, DISP_H * 0.5, 3, ui->getDangerColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 16, 180 + ( 360.0 / 60.0 * second ), 1, ui->getDangerColor()); - hal->gfx()->drawThickTick(DISP_W * 0.5, DISP_H * 0.5, 0, 105, 360.0 / 60.0 * second, 1, ui->getDangerColor()); + hal->gfx()->fillCircle(DISP_W / 2, DISP_H / 2, 3, ui->getDangerColor()); + hal->gfx()->drawThickTick(DISP_W / 2, DISP_H / 2, 0, 16, 180 + ( 360.0f / 60.0f * second ), 1, ui->getDangerColor()); + hal->gfx()->drawThickTick(DISP_W / 2, DISP_H / 2, 0, 105, 360.0f / 60.0f * second, 1, ui->getDangerColor()); #endif } diff --git a/src/devices/bma400.cpp b/src/devices/bma400.cpp index 478c39e25..36f9e6799 100644 --- a/src/devices/bma400.cpp +++ b/src/devices/bma400.cpp @@ -19,7 +19,7 @@ #define READ_WRITE_LENGTH UINT8_C(46) struct bma400_dev bma; -float accelT = 0, accelX = 0, accelY = 0, accelZ = 0; +float accelT = 0.0f, accelX = 0.0f, accelY = 0.0f, accelZ = 0.0f; static uint8_t dev_addr; uint8_t act_int; @@ -333,7 +333,7 @@ float OswDevices::BMA400::getTemperature() { int16_t temperature; rslt = bma400_get_temperature_data(&temperature, &bma); bma400_check_rslt("bma400_get_temperature_data", rslt); - return temperature / 10; + return temperature / 10.0f; } uint32_t OswDevices::BMA400::getStepCount() { diff --git a/src/devices/esp32.cpp b/src/devices/esp32.cpp index fc0fb0795..759e35e23 100644 --- a/src/devices/esp32.cpp +++ b/src/devices/esp32.cpp @@ -111,7 +111,7 @@ float OswDevices::NativeESP32::getTemperature() { const uint8_t temp = temprature_sens_read(); if(!this->tempSensorIsBuiltIn) return 0.0f; - return (temp - 32) / 1.8; + return (temp - 32) / 1.8f; } bool OswDevices::NativeESP32::isTemperatureSensorAvailable() { diff --git a/src/devices/qmc5883l.cpp b/src/devices/qmc5883l.cpp index 21ca8dfe3..7ce9aec5d 100644 --- a/src/devices/qmc5883l.cpp +++ b/src/devices/qmc5883l.cpp @@ -40,7 +40,7 @@ int OswDevices::QMC5883L::getMagnetometerZ() { int OswDevices::QMC5883L::getMagnetometerAzimuth() { // NOT (y,x),(y,z) (z,y) (x,y) - int a = atan2(qmc5883l.getY(), qmc5883l.getX()) * 180.0 / PI; + int a = atan2(qmc5883l.getY(), qmc5883l.getX()) * 180.0f / PI; a = a < 0 ? 360 + a : a; #if defined(GPS_EDITION_ROTATED) diff --git a/src/gfx_2d.cpp b/src/gfx_2d.cpp new file mode 100644 index 000000000..79b1728b7 --- /dev/null +++ b/src/gfx_2d.cpp @@ -0,0 +1,1548 @@ + +#include + +#include "gfx_2d.h" +#include "gfx_util.h" +#include "math_angles.h" + +#include + +#pragma GCC optimize("O2") + +void Graphics2D::fillBuffer(uint16_t color = rgb565(0, 0, 0)) { + if (!hasBuffer()) + return; + + for (int chunk = numChunks - 1; chunk >= 0; --chunk) { + for (int i = (chunkWidths[chunk] << chunkHeightLd) - 1; i >= 0; --i) { + buffer[chunk][i] = color; + } + } +} + +void Graphics2D::enableBuffer() { + drawPixelCallback = NULL; + numChunks = height >> chunkHeightLd; + buffer = new uint16_t* [numChunks]; + if (isRound) { + missingPixelColor = rgb565(128, 128, 128); + chunkXOffsets = new uint16_t[numChunks]; + chunkWidths = new uint16_t[numChunks]; + for (uint16_t i = 0; i < numChunks; i++) { + uint16_t y = i << chunkHeightLd; + float y1 = (y + (y < height / 2 ? (1<= 0; --i) { + delete[] buffer[i]; + buffer[i] = NULL; + } + delete[] buffer; + buffer = NULL; + + delete[] chunkXOffsets; + chunkXOffsets = NULL; + + delete[] chunkWidths; + chunkWidths = NULL; +} + +Graphics2D::~Graphics2D() { + for (int32_t i = numChunks - 1; i >= 0; --i) { + delete[] buffer[i]; + buffer[i] = NULL; + } + delete[] buffer; + buffer = NULL; + + delete[] chunkXOffsets; + chunkXOffsets = NULL; + + delete[] chunkWidths; + chunkWidths = NULL; +} + +void Graphics2D::drawPixelClipped(int32_t x, int32_t y, uint16_t color) { + if (x >= width || y >= height || x < 0 || y < 0) { + return; + } + if (maskEnabled && color == maskColor) { + return; + } + + // if we have a pixel callback, there is now buffer + // draw with the callback and return.. + if (drawPixelCallback != NULL) { + drawPixelCallback->drawPixel(x, y, color); + return; + } + + uint8_t chunkId = y >> chunkHeightLd; + int16_t chunkY = y - (chunkId << chunkHeightLd); + + bool isRoundAndInsideChunkCached = isRound && isInsideChunk(x, y); + if (alphaEnabled) { + if (isRoundAndInsideChunkCached) { + int16_t chunkX = x - chunkXOffsets[chunkId]; + color = blend(buffer[chunkId][chunkX + chunkY * chunkWidths[chunkId]], color, alpha); + } else { + color = blend(buffer[chunkId][x + chunkY * width], color, alpha); + } + } + + if (isRoundAndInsideChunkCached) { + int16_t chunkX = x - chunkXOffsets[chunkId]; + buffer[chunkId][chunkX + chunkY * chunkWidths[chunkId]] = color; + } else if (!isRound) { // fix for round module + buffer[chunkId][x + chunkY * width] = color; + } +} + +uint16_t Graphics2D::getPixel(uint16_t x, uint16_t y) { + if (x >= width || y >= height) { + return 0; + } + uint8_t chunkId = y >> chunkHeightLd; + uint16_t chunkY = y - (chunkId << chunkHeightLd); + // printf("chunkid %d, offetY %d for y=%d and chunkHeight=%d\n", chunkId, chunkY, y, chunkHeight); + if (isRound) { + // TODO: check if inside chunk + if (isInsideChunk(x, y)) { + uint16_t chunkX = x - chunkXOffsets[chunkId]; + return buffer[chunkId][chunkX + chunkY * chunkWidths[chunkId]]; + } else { + return missingPixelColor; + } + } else { + return buffer[chunkId][x + chunkY * width]; + } +} + +bool Graphics2D::isInsideChunk(uint16_t x, uint16_t y) { + uint8_t chunkId = y >> chunkHeightLd; + // uint16_t chunkY = y - (chunkId << chunkHeightLd); + uint16_t chunkOffset = chunkXOffsets[chunkId]; + uint16_t chunkWidth = chunkWidths[chunkId]; + bool xFit = chunkOffset < x && x < chunkOffset + chunkWidth; + // y always fits, because we chunk in rows + return xFit; +} + +/** + * @brief Draw an horizontal line from the point (x,y) to an other horizontal point at h pixels + * + * @param x x-axis of the start point + * @param y y-axis of the start point + * @param w width of the horizontal line + * @param color color code of the line + */ +void Graphics2D::drawHLine(int32_t x, int32_t y, uint16_t w, uint16_t color) { + for (uint16_t i = 0; i < w; i++) { + drawPixel(x + i, y, color); + } +} + +/** + * @brief Draw a vertical line from the bottom point (x,y) to an other vertical point at h pixels + * + * @param x x-axis of the start point + * @param y y-axis of the start point + * @param h height of the vertical line + * @param color color code of the line + */ +void Graphics2D::drawVLine(int32_t x, int32_t y, uint16_t h, uint16_t color) { + for (int32_t i = 0; i < h; i++) { + drawPixel(x, y + i, color); + } +} + +void Graphics2D::drawFrame(int32_t x, int32_t y, uint16_t w, uint16_t h, uint16_t color) { + drawHLine(x, y, w, color); + drawHLine(x, y + h, w, color); + drawVLine(x, y, h, color); + drawVLine(x + w, y, h, color); +} + +void Graphics2D::fillFrame(int32_t x0, int32_t y0, uint16_t w, uint16_t h, uint16_t color) { + for (int32_t y = y0; y < y0 + h; y++) { + drawHLine(x0, y, w, color); + } +} + +/** + * Draw line from (x1,y1) to (x2,y2) point with color + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param color + */ +void Graphics2D::drawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t color) { // see p3dt_gfx_2d_license.txt + // printf("\ndrawLine(%d, %d, %d, %d)",x1,y1,x2,y2); + // see p3dt_gfx_2d_license.txt + int32_t tmp; + int32_t x, y; + int32_t dx, dy; + int32_t err; + int32_t ystep; + + uint8_t swapxy = 0; + + /* no intersection check at the moment, should be added... */ + + if (x1 > x2) + dx = x1 - x2; + else + dx = x2 - x1; + if (y1 > y2) + dy = y1 - y2; + else + dy = y2 - y1; + + if (dy > dx) { + swapxy = 1; + tmp = dx; dx = dy; dy = tmp; + tmp = x1; x1 = y1; y1 = tmp; + tmp = x2; x2 = y2; y2 = tmp; + } + if (x1 > x2) { + tmp = x1; x1 = x2; x2 = tmp; + tmp = y1; y1 = y2; y2 = tmp; + } + err = dx / 2; + if (y2 > y1) + ystep = 1; + else + ystep = -1; + y = y1; + + if (x2 == 0xffff) + x2--; + + for (x = x1; x <= x2; x++) { + if (swapxy == 0) + drawPixel(x, y, color); + else + drawPixel(y, x, color); + err -= abs(dy); + if (err < 0) { + y += ystep; + err += dx; + } + } +} + +/** + * Draw a thin anti-aliased line from (x1,y1) to (x2,y2) point with color + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param color + */ +void Graphics2D::drawLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const uint16_t color) { + // anti-aliased line + + int dx = abs(x1-x0); + int sx = x0 < x1 ? 1 : -1; + int dy = abs(y1-y0); + int sy = y0 < y1 ? 1 : -1; + int err = dx-dy, e2, x2; /* error value e_xy */ + int ed = dx+dy == 0 ? 1 : hypotf(dx, dy); //sqrtf(dx*dx+dy*dy); + + for ( ; ; ){ /* pixel loop */ + drawPixelAA(x0, y0, color, 255-255*abs(err-dx+dy)/ed); + e2 = err; + x2 = x0; + if (2*e2 >= -dx) { /* x step */ + if (x0 == x1) + break; + if (e2+dy < ed) + drawPixelAA(x0, y0+sy, color, 255-255*(e2+dy)/ed); + err -= dy; + x0 += sx; + } + if (2*e2 <= dy) { /* y step */ + if (y0 == y1) + break; + if (dx-e2 < ed) + drawPixelAA(x2+sx, y0, color, 255-255*(dx-e2)/ed); + err += dx; + y0 += sy; + } + } +} + +/** + * @brief Draw a line between (x1,y1) and (x2,y2) with a thick of radius and with specific color + * + * Radius is a multiple of 4 pixels. + * + * @param x1 x-axis of the start point + * @param y1 y-axis of the start point + * @param x2 x-axis of the end point + * @param y2 y-axis of the end point + * @param radius radius of the line. Example : radius = 1 give a line of 4 px of diameter, radius 2 -> 8px, etc.... + * @param color color code use to draw the line. + * @param highQuality + */ +void Graphics2D::drawThickLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint8_t radius, uint16_t color, + bool highQuality) { // see p3dt_gfx_2d_license.txt + + // see p3dt_gfx_2d_license.txt + int32_t tmp; + int32_t x, y; + int32_t dx, dy; + int32_t err; + int32_t ystep; + + uint8_t swapxy = 0; + + /* no intersection check at the moment, should be added... */ + + if (x1 > x2) + dx = x1 - x2; + else + dx = x2 - x1; + if (y1 > y2) + dy = y1 - y2; + else + dy = y2 - y1; + + if (dy > dx) { + swapxy = 1; + tmp = dx; dx = dy; dy = tmp; + tmp = x1; x1 = y1; y1 = tmp; + tmp = x2; x2 = y2; y2 = tmp; + } + if (x1 > x2) { + tmp = x1; x1 = x2; x2 = tmp; + tmp = y1; y1 = y2; y2 = tmp; + } + err = dx >> 1; + if (y2 > y1) + ystep = 1; + else + ystep = -1; + y = y1; + + if (x2 == 0xffff) x2--; + + for (x = x1; x <= x2; x++) { + if (swapxy == 0) { + if (highQuality) { + fillCircle(x, y, radius, color); + } else { + drawCircle(x, y, radius, color); + if (radius > 2) { + drawCircle(x, y, radius - 1, color); + } + if (radius > 3) { + drawCircle(x, y, radius - 2, color); + } + } + } else { + if (highQuality) { + fillCircle(y, x, radius, color); + } else { + drawCircle(y, x, radius, color); + if (radius > 2) { + drawCircle(y, x, radius - 1, color); + } + if (radius > 3) { + drawCircle(y, x, radius - 2, color); + } + } + } + + err -= (uint8_t)dy; + if (err < 0) { + y += (uint16_t)ystep; + err += (uint16_t)dx; + } + } +} + +/** + * @brief Draw an anti-aliased line between (x1,y1) and (x2,y2) with a thicknes of line_width and with a specific color + * + * @param x1 x-axis of the start point + * @param y1 y-axis of the start point + * @param x2 x-axis of the end point + * @param y2 y-axis of the end point + * @param line_width thickness of the line + * @param color color code use to draw the line. + */ +void Graphics2D::drawThickLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t line_width, + const uint16_t color, LINE_END_OPT eol) { + // thanks to https://github.com/foo123/Rasterizer + + int32_t tmp, + sx, sy, + w, w2, wx, wy, + wsx, wsy, + dx, dy, n, + xa, xb, xc, xd, + ya, yb, yc, yd; + + /* + given line's thickness w and dx,dy of line, wx, wy are computed as: + n = hypot(dx, dy) + w2 = (w-1)/2 + wx = dy*w2/n + wy = dx*w2/n + */ + + w = line_width; + w2 = (w-1)/2; + + if (x0 > x1) { + tmp = x0; x0 = x1; x1 = tmp; + tmp = y0; y0 = y1; y1 = tmp; + } + + sx = 1; + sy = y1 > y0 ? 1 : -1; + + dx = abs(x1 - x0); + dy = abs(y1 - y0); + + if (dx == 0) + return fillBoxHV(x0 - w2, y0, x1 - w2 + w, y1, color); + else if (dy == 0) + return fillBoxHV(x0, y0 - w2, x1, y1 - w2 + w, color); + + n = hypotf(dx, dy) + 0.5f; //sqrtf(dx*dx + dy*dy) + 0.5f; + // more readable: wx = (dy*w/2 + (n/2))/n; where (n/2) is for rounding + wx = (dy*w+n)/2/n; // +1 is for rounding of /2 + wy = (dx*w+n)/2/n; + +// printf("xxx x0=%d, y0=%d, x1=%d, y1=%d, dx=%d, dy=%d, w2=%d, wx=%d, wy=%d, %d, %d\n", x0, y0, x1, y1, dx, dy, w2, wx, wy); + +/* + wx .b + +-----.(0) . +wy |.a | . . + . | . .f + . . . + dy | .g . .d + +---.-----.(1) + dx .c + +a: y0 + wsy - y0 = -(x - x0)/m => x = x0 - m*wsy: (xs-wsx, y0+wsy) +b: y0 - wsy - y0 = -(x - x0)/m => x = x0 + m*wsy: (xs+wsx, y0-wsy) +c: y1 + wsy - y1 = -(x - x1)/m => x = x1 - m*wsy: (xe-wsx, y1+wsy) +d: y1 - wsy - y1 = -(x - x1)/m => x = x1 + m*wsy: (xe+wsx, y1-wsy) +*/ + + wsx = sx*wx; + wsy = sy*wy; + + xa = x0 - wsx; + ya = y0 + wsy; + xb = x0 + wsx; + yb = y0 - wsy; + xc = x1 - wsx; + yc = y1 + wsy; + xd = x1 + wsx; + yd = y1 - wsy; + +// printf("xxx xa=%d,ya=%d xb=%d,yb=%d xc=%d,yc=%d xd=%d,yd=%d\n", xa,ya, xb,yb, xc,yc, xd,yd); + + // outline + drawLineAA(xa, ya, xb, yb, color); + drawLineAA(xb, yb, xd, yd, color); + drawLineAA(xd, yd, xc, yc, color); + drawLineAA(xc, yc, xa, ya, color); + + // fill + drawFilledTriangle(xb, yb, xa, ya, xc, yc, color); + drawFilledTriangle(xb, yb, xd, yd, xc, yc, color); + + switch (eol) { + case STRAIGHT_END: + break; + case ROUND_END: // circle at end + fillCircleAA(x0, y0, (line_width)/2, color); + fillCircleAA(x1, y1, (line_width)/2, color); + break; + case TRIANGLE_END: + { + // still experimental and not finished + int32_t x, y; + int32_t xo = sx * line_width * dx / n; + int32_t yo = sy * line_width * dy / n; + x = x0 - xo; + y = y0 - yo; + drawFilledTriangle(xa, ya, xb, yb, x, y, color/2); + x = x1 + xo; + y = y1 + yo; + drawFilledTriangle(xc, yc, xd, yd, x, y, color/2); + } + break; + default: + break; + } + +/* + // Center pixel + drawPixel(x0, y0, rgb565(255,255,0)); + drawPixel(x1, y1, rgb565(255,255,0)); + + // Draw vertices + drawPixel(xa, ya, rgb565(0,0,255)); + drawPixel(xb, yb, rgb565(0,0,255)); + drawPixel(xd, yd, rgb565(0,0,255)); + drawPixel(xc, yc, rgb565(0,0,255)); +*/ +} + +void Graphics2D::drawFilledTriangle(int32_t ax, int32_t ay, int32_t bx, int32_t by, int32_t cx, int32_t cy, const uint16_t color) { + int32_t tmp, + x, xx, y, + xac, xab, xbc, + yac, yab, ybc, + zab, zbc; + + // correction if tirangles edge AC is rising + int32_t corr = cy-ay < 0 ? 0 : 1; + + if (ay > by) { + tmp = ax; ax = bx; bx = tmp; + tmp = ay; ay = by; by = tmp; + } + if (ay > cy) { + tmp = ax; ax = cx; cx = tmp; + tmp = ay; ay = cy; cy = tmp; + } + if (by > cy) { + tmp = bx; bx = cx; cx = tmp; + tmp = by; by = cy; cy = tmp; + } + + yac = cy - ay; + if (yac == 0) + { + // line or single point + y = ay; + x = min(ax, min(bx, cx)); + xx = max(ax, max(bx, cx)); + return fillBoxHV(x, y, xx, y, color); + } + + yab = by - ay; + ybc = cy - by; + xac = cx - ax; + xab = bx - ax; + xbc = cx - bx; + + zab = yab == 0; + zbc = ybc == 0; + + for (y = ay; y < cy; ++y) + { + if (y < by) { // upper part + if (zab) { + x = ax; + xx = bx; + } else { + x = (xac*(y - ay))/yac + ax; + xx = (xab*(y - ay))/yab + ax + corr; + } + } else { // lower part + if (zbc) { + x = bx; + xx = cx; + } else { + x = (xac*(y - ay))/yac + ax; + xx = (xbc*(y - by))/ybc + bx + corr; + } + } + if (x > xx) { // left handed ;-) + tmp = x; x = xx; xx = tmp; + } + + if (abs(xx-x) <= 0) { + drawPixel(x, y, color); + } else { + for (x = x; x < xx; ++x) + drawPixel(x, y, color); + } + } +} + + +/** + * @brief Draw a horizontal/vertical box (x1,y1) and (x2,y2) with a specific color + * + * @param x1 x-axis of the start point + * @param y1 y-axis of the start point + * @param x2 x-axis of the end point + * @param y2 y-axis of the end point + * @param color color code use to fill the box. + */ +void Graphics2D::fillBoxHV(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const uint16_t color){ + int sx, sy; + sx = x1 > x0 ? 1 : -1; + sy = y1 > y0 ? 1 : -1; + + for (int32_t x = x0; x != x1; x += sx) + for (int32_t y = y0; y != y1; y += sy) + drawPixel(x, y, color); +} + +void Graphics2D::drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) { + drawLine(x0, y0, x1, y1, color); + drawLine(x1, y1, x2, y2, color); + drawLine(x2, y2, x0, y0, color); +} + +/* + * "Complex" Stuff: + */ + +void Graphics2D::_drawCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, + CIRC_OPT option) { // see p3dt_gfx_2d_license.txt + + if (option == DRAW_UPPER_RIGHT || option == DRAW_ALL) { + drawPixel(x0 + x, y0 - y, color); + drawPixel(x0 + y, y0 - x, color); + } + + if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { + drawPixel(x0 - x, y0 - y, color); + drawPixel(x0 - y, y0 - x, color); + } + + if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { + drawPixel(x0 + x, y0 + y, color); + drawPixel(x0 + y, y0 + x, color); + } + + if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { + drawPixel(x0 - x, y0 + y, color); + drawPixel(x0 - y, y0 + x, color); + } +} + +/** + * @brief Draw a circle + * + * @param x0 x-axis of the center of the circle + * @param y0 y-axis of the center of the circle + * @param rad radius of the circle + * @param color color code of the circle + * @param option + */ +void Graphics2D::drawCircle(int16_t x0, int16_t y0, int16_t rad, uint16_t color, + CIRC_OPT option) { // see p3dt_gfx_2d_license.txt + + float f; + float ddFx; + float ddFy; + float x; + float y; + + f = 1; + f -= rad; + ddFx = 1; + ddFy = 0; + ddFy -= rad; + ddFy *= 2; + x = 0; + y = rad; + + _drawCircleSection(x, y, x0, y0, color, option); + + while (x < y) { + if (f >= 0) { + y--; + ddFy += 2; + f += ddFy; + } + x++; + ddFx += 2; + f += ddFx; + + _drawCircleSection(x, y, x0, y0, color, option); + } +} + +void Graphics2D::isPixelMaskedByAnglesInit(int32_t off_x, int32_t off_y, int32_t sa, int32_t ea){ + start_angle = sa; + end_angle = ea; + if (ea != -1 && sa != -1) { + ox = off_x; + oy = off_y; + tan_sa = tanf(start_angle*(float)PI/180.0f); + tan_ea = tanf(end_angle*(float)PI/180.0f); + } +} + +bool Graphics2D::isPixelMaskedByAngles(int32_t x, int32_t y) { + if (end_angle == -1 && start_angle == -1) + return false; + else { + float tan_pixel = - (float)(y-oy) / (float)(x-ox); + if (start_angle < 90) { + if (x-ox >= 0 && y-oy <= 0 && tan_pixel < tan_sa) + return true; + } else if (start_angle < 180) { + if (x-ox >= 0 && y-oy <= 0) + return true; // 1.quadrant + else if (x-ox < 0 && y-oy <= 0) + if (tan_pixel < 0 && tan_pixel < tan_sa) + return true; // 2.quadrant + } else if (start_angle < 270) { + if (x-ox >= 0 && y-oy <= 0) + return true; // 1.quadrant + else if (x-ox < 0 && y-oy <= 0) + return true; // 2.quadrant + else if (x-ox < 0 && y-oy > 0) + if (tan_pixel > 0 && tan_pixel < tan_sa) + return true; // 3.quadrant + } else if (start_angle <= 360) { + if (x-ox >= 0 && y-oy <= 0) + return true; // 1.quadrant + else if (x-ox < 0 && y-oy <= 0) + return true; // 2.quadrant + else if (x-ox < 0 && y-oy > 0) + return true; // 3.quadrant + else + if (tan_pixel < 0 && tan_pixel < tan_sa) + return true; // 4.quadrant + } + + // end angle + if (end_angle < 90) { + if (x-ox >= 0 && y-oy <= 0 && tan_pixel > tan_ea) + return true; // 1.quadrant + else if (x-ox <= 0) + return true; // 2.&3.quadrant + else if (x-ox >= 0 && y-oy >0) + return true; // 4. quadrant + } else if (end_angle < 180) { + if (x-ox < 0 && y-oy <= 0 && tan_pixel > tan_ea) + return true; // 2.quadrant + else if (y-oy >= 0) + return true; // 3.&4.quadrant + } else if (end_angle < 270) { + if (x-oy <= 0 && y-oy >= 0 && tan_pixel > tan_ea) + return true; // 3.quadrant + else if (x-ox >= 0 && y-oy >= 0) + return true; // 4.quadrant + } else if (end_angle <= 360) { + if (x-ox >= 0 && y-oy >= 0 && tan_pixel >= tan_ea) + return true; // 4.quadrant + } + } + return false; +} + + +/** + * @brief Draw an anti-aliased circle with thicknes + * + * @param x0 x-axis of the center of the circle + * @param y0 y-axis of the center of the circle + * @param r radius of the circle + * @param bw thickness of th circle + * @param color color code of the circle + */ +void Graphics2D::drawCircleAA(int16_t off_x, int16_t off_y, int16_t r, int16_t bw, + uint16_t color, int16_t sa, int16_t ea) { + + int x0 = -r; + int y0 = -r; + int x1 = r; + int y1 = r; + + isPixelMaskedByAnglesInit(off_x, off_y, sa, ea); + + // for a filled circle + if (bw >= r || bw <= 0) { + bw = r - 1; + drawPixel(off_x, off_y, color); + } + + int o_diam = 2*r; // outer diameter + const int odd_diam = 0; // o_diam&1; // odd diameter + int a2 = 2*r-2*bw; + int dx = 4*(o_diam-1)*o_diam*o_diam; + int dy = 4*(odd_diam-1)*o_diam*o_diam; // error increment + int err = odd_diam*o_diam*o_diam; + int dx2, dy2, e2, ed; + + if ((bw-1)*(2*o_diam-bw) > o_diam*o_diam) { + a2 = 0; + bw = o_diam/2; + } + + if (x0 > x1) { // if called with swapped points + x0 = x1; + x1 += o_diam; + } + if (y0 > y1) + y0 = y1; // .. exchange them + if (a2 <= 0) + bw = o_diam; // filled ellipse + e2 = bw; + bw = x0+bw-e2; + dx2 = 4*(a2+2*e2-1)*a2*a2; + dy2 = 4*(odd_diam-1)*a2*a2; + e2 = dx2*e2; + y0 += (o_diam+1)>>1; + y1 = y0-odd_diam+1; // starting pixel + int a1; + a1 = 8*o_diam*o_diam; + a2 = 8*a2*a2; + + int i = 0; + + do { + for (;;) { + if (err <= 0 || x0 >= x1) { + i = x0; + break; + } + i = dx < dy ? dx : dy; + ed = dx > dy ? dx : dy; + + ed += 2*ed*i*i/(4*ed*ed+i*i+1)+1;// approx ed=sqrtf(dx*dx+dy*dy) + + int alpha = 255 - 255.0f*err/ed; // outside anti-aliasing; integer overflow for big radius + if (alpha < 0) + alpha = 0; + else if (alpha > 255) + alpha = 255; + + if (!isPixelMaskedByAngles(off_x + x0, off_y + y0)) drawPixelAA(off_x + x0, off_y + y0, color, alpha); // 3. quadrant + if (!isPixelMaskedByAngles(off_x + x0, off_y + y1 - 1)) drawPixelAA(off_x + x0, off_y + y1 - 1, color, alpha); // 2. quadrant + if (!isPixelMaskedByAngles(off_x + x1, off_y + y0)) drawPixelAA(off_x + x1, off_y + y0, color, alpha); // 1. quadrant + if (!isPixelMaskedByAngles(off_x + x1, off_y + y1-1)) drawPixelAA(off_x + x1, off_y + y1-1, color, alpha); // 4. quadrant + + if (err+dy+a1 < dx) { + i = x0+1; + break; + } + ++x0; + --x1; + err -= dx; + dx -= a1; // x error increment + } + for ( ; i < bw && 2*i <= x0+x1; ++i) { // fill line pixel + if (!isPixelMaskedByAngles(off_x + i, off_y + y0)) drawPixel(off_x + i, off_y + y0, color); // 3. quadrant + if (!isPixelMaskedByAngles(off_x + i, off_y + y1 - 1)) drawPixel(off_x + i, off_y + y1 - 1, color); // 2. quadrant + if (!isPixelMaskedByAngles(off_x + x0 + x1 - i, off_y + y1 - 1)) drawPixel(off_x + x0 + x1 - i, off_y + y1 - 1, color); // 1. quadrant + if (!isPixelMaskedByAngles(off_x + x0 + x1 - i, off_y + y0)) drawPixel(off_x + x0 + x1 - i, off_y + y0, color); // 4. quadrant + } + while (e2 >= 0 && 2*bw <= x0+x1) { // inside anti-aliasing + i = dx2 < dy2 ? dx2 : dy2; + ed = dx2 > dy2 ? dx2 : dy2; + + ed += 2*ed*i*i/(4*ed*ed+i*i+1)+1; // approx ed=sqrtf(dx*dx+dy*dy) + + int alpha = 255.0f*e2/ed; + if (alpha < 0) + alpha = 0; + else if (alpha > 255) + alpha = 255; + + if (!isPixelMaskedByAngles(off_x + bw, off_y + y0)) drawPixelAA(off_x + bw, off_y + y0, color, alpha); // 3. quadrant + if (!isPixelMaskedByAngles(off_x + bw, off_y + y1 - 1)) drawPixelAA(off_x + bw, off_y + y1 - 1, color, alpha); // 2. quadrant + if (!isPixelMaskedByAngles(off_x + x0 + x1 - bw, off_y + y1 - 1)) drawPixelAA(off_x + x0 + x1 - bw, off_y + y1 - 1, color, alpha); // 1. quadrant + if (!isPixelMaskedByAngles(off_x + x0 + x1 - bw, off_y + y0)) drawPixelAA(off_x + x0 + x1 - bw, off_y + y0, color, alpha); // 4. quadrant + if (e2+dy2+a2 < dx2) + break; + ++bw; + e2 -= dx2; + dx2 -= a2; // x error increment + } + dy2 += a2; + e2 += dy2; + dy += a1; // y step + err += dy; + y0++; + y1--; + } while (x0 < x1); +} + +void Graphics2D::_fillCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, + CIRC_OPT option) { // see p3dt_gfx_2d_license.txt + + if (option == DRAW_UPPER_RIGHT || option == DRAW_ALL) { + drawVLine(x0 + x, y0 - y, y + 1, color); + drawVLine(x0 + y, y0 - x, x + 1, color); + } + + if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { + drawVLine(x0 - x, y0 - y, y + 1, color); + drawVLine(x0 - y, y0 - x, x + 1, color); + } + + if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { + drawVLine(x0 + x, y0, y + 1, color); + drawVLine(x0 + y, y0, x + 1, color); + } + + if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { + drawVLine(x0 - x, y0, y + 1, color); + drawVLine(x0 - y, y0, x + 1, color); + } +} + +void Graphics2D::fillCircle(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color, + CIRC_OPT option) { // see p3dt_gfx_2d_license.txt + + float f; + float ddFx; + float ddFy; + float x; + float y; + + f = 1; + f -= rad; + ddFx = 1; + ddFy = 0; + ddFy -= rad; + ddFy *= 2; + x = 0; + y = rad; + + _fillCircleSection(x, y, x0, y0, color, option); + + while (x < y) { + if (f >= 0) { + y--; + ddFy += 2; + f += ddFy; + } + x++; + ddFx += 2; + f += ddFx; + + _fillCircleSection(x, y, x0, y0, color, option); + } +} + +void Graphics2D::_drawEllipseSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, + CIRC_OPT option) { // see p3dt_gfx_2d_license.txt + + /* upper right */ + if (option == DRAW_UPPER_RIGHT || option == DRAW_ALL) { + drawPixel(x0 + x, y0 - y, color); + } + + /* upper left */ + if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { + drawPixel(x0 - x, y0 - y, color); + } + + /* lower right */ + if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { + drawPixel(x0 + x, y0 + y, color); + } + + /* lower left */ + if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { + drawPixel(x0 - x, y0 + y, color); + } +} + +void Graphics2D::drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color, + CIRC_OPT option) { // see p3dt_gfx_2d_license.txt + + float x; + float y; + float xchg; + float ychg; + float err; + float rxrx2; + float ryry2; + float stopx; + float stopy; + + rxrx2 = rx; + rxrx2 *= rx; + rxrx2 *= 2; + + ryry2 = ry; + ryry2 *= ry; + ryry2 *= 2; + + x = rx; + y = 0; + + xchg = 1; + xchg -= rx; + xchg -= rx; + xchg *= ry; + xchg *= ry; + + ychg = rx; + ychg *= rx; + + err = 0; + + stopx = ryry2; + stopx *= rx; + stopy = 0; + + while (stopx >= stopy) { + _drawEllipseSection(x, y, x0, y0, color, option); + y++; + stopy += rxrx2; + err += ychg; + ychg += rxrx2; + if (2 * err + xchg > 0) { + x--; + stopx -= ryry2; + err += xchg; + xchg += ryry2; + } + } + + x = 0; + y = ry; + + xchg = ry; + xchg *= ry; + + ychg = 1; + ychg -= ry; + ychg -= ry; + ychg *= rx; + ychg *= rx; + + err = 0; + + stopx = 0; + + stopy = rxrx2; + stopy *= ry; + + while (stopx <= stopy) { + _drawEllipseSection(x, y, x0, y0, color, option); + x++; + stopx += ryry2; + err += xchg; + xchg += ryry2; + if (2 * err + ychg > 0) { + y--; + stopy -= rxrx2; + err += ychg; + ychg += rxrx2; + } + } +} + +void Graphics2D::_fillEllipseSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, + CIRC_OPT option) { // see p3dt_gfx_2d_license.txt + + /* upper right */ + if (option == DRAW_UPPER_RIGHT || option == DRAW_ALL) { + drawVLine(x0 + x, y0 - y, y + 1, color); + } + + /* upper left */ + if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { + drawVLine(x0 - x, y0 - y, y + 1, color); + } + + /* lower right */ + if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { + drawVLine(x0 + x, y0, y + 1, color); + } + + /* lower left */ + if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { + drawVLine(x0 - x, y0, y + 1, color); + } +} + +void Graphics2D::fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color, + CIRC_OPT option) { // see p3dt_gfx_2d_license.txt + + float x; + float y; + float xchg; + float ychg; + float err; + float rxrx2; + float ryry2; + float stopx; + float stopy; + + rxrx2 = rx; + rxrx2 *= rx; + rxrx2 *= 2; + + ryry2 = ry; + ryry2 *= ry; + ryry2 *= 2; + + x = rx; + y = 0; + + xchg = 1; + xchg -= rx; + xchg -= rx; + xchg *= ry; + xchg *= ry; + + ychg = rx; + ychg *= rx; + + err = 0; + + stopx = ryry2; + stopx *= rx; + stopy = 0; + + while (stopx >= stopy) { + _fillEllipseSection(x, y, x0, y0, color, option); + y++; + stopy += rxrx2; + err += ychg; + ychg += rxrx2; + if (2 * err + xchg > 0) { + x--; + stopx -= ryry2; + err += xchg; + xchg += ryry2; + } + } + + x = 0; + y = ry; + + xchg = ry; + xchg *= ry; + + ychg = 1; + ychg -= ry; + ychg -= ry; + ychg *= rx; + ychg *= rx; + + err = 0; + + stopx = 0; + + stopy = rxrx2; + stopy *= ry; + + while (stopx <= stopy) { + _fillEllipseSection(x, y, x0, y0, color, option); + x++; + stopx += ryry2; + err += xchg; + xchg += ryry2; + if (2 * err + ychg > 0) { + y--; + stopy -= rxrx2; + err += ychg; + ychg += rxrx2; + } + } +} + +void Graphics2D::drawRFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t r, + uint16_t color) { // see p3dt_gfx_2d_license.txt + + uint16_t xl; + uint16_t yu; + + xl = x; + xl += r; + yu = y; + yu += r; + + { + uint16_t yl; + uint16_t xr; + + xr = x; + xr += w; + xr -= r; + xr -= 1; + + yl = y; + yl += h; + yl -= r; + yl -= 1; + + drawCircle(xl, yu, r, color, DRAW_UPPER_LEFT); + drawCircle(xr, yu, r, color, DRAW_UPPER_RIGHT); + drawCircle(xl, yl, r, color, DRAW_LOWER_LEFT); + drawCircle(xr, yl, r, color, DRAW_LOWER_RIGHT); + } + + { + uint16_t ww; + uint16_t hh; + + ww = w; + ww -= r; + ww -= r; + hh = h; + hh -= r; + hh -= r; + + xl++; + yu++; + + if (ww >= 3) { + ww -= 2; + h--; + drawHLine(xl, y, ww, color); + drawHLine(xl, y + h, ww, color); + } + + if (hh >= 3) { + hh -= 2; + w--; + drawVLine(x, yu, hh, color); + drawVLine(x + w, yu, hh, color); + } + } +} + +void Graphics2D::fillRFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t r, + uint16_t color) { // see p3dt_gfx_2d_license.txt + //Prevent infinite looping + if(h < 2 * r) return; + + uint16_t xl; + uint16_t yu; + uint16_t yl; + uint16_t xr; + + xl = x; + xl += r; + yu = y; + yu += r; + + xr = x; + xr += w; + xr -= r; + xr -= 1; + + yl = y; + yl += h; + yl -= r; + yl -= 1; + + fillCircle(xl, yu, r, color, DRAW_UPPER_LEFT); + fillCircle(xr, yu, r, color, DRAW_UPPER_RIGHT); + fillCircle(xl, yl, r, color, DRAW_LOWER_LEFT); + fillCircle(xr, yl, r, color, DRAW_LOWER_RIGHT); + + { + uint16_t ww; + uint16_t hh; + + ww = w; + ww -= r; + ww -= r; + xl++; + yu++; + + if (ww >= 3) { + ww -= 2; + fillFrame(xl, y, ww, r + 1, color); + fillFrame(xl, yl, ww, r + 1, color); + } + + hh = h; + hh -= r; + hh -= r; + // h--; + if (hh >= 3) { + hh -= 2; + fillFrame(x, yu, w, hh, color); + } + } +} + +/** + * @brief Draw N ticks around the clock to visualize the hours. + * + * @param cx center x axis + * @param cy center y axis + * @param r1 radius from the begin of the tick. + * @param r2 radius from the end of the tick. + * @param nTicks number of ticks to draw + * @param color color code + */ +void Graphics2D::drawNTicks(int16_t cx, int16_t cy, int16_t r1, int16_t r2, int16_t nTicks, uint16_t color, int16_t skip_every_nth) { + if (360 % nTicks != 0) { + const float deltaAngle = 360.0f / nTicks; + for (int h = nTicks; h >= 0; --h) { + if (h % skip_every_nth != 0) + drawTick(cx, cy, r1, r2, h * deltaAngle, color); + } + } else { + const int deltaAngle = 360 / nTicks; + for (int h = nTicks; h >= 0; --h) { + if (h % skip_every_nth != 0) + drawTick(cx, cy, r1, r2, h * deltaAngle, color); + } + } +} + +/** + * @brief Draw N ticks around the clock to visualize the hours. + * + * @param cx center x axis + * @param cy center y axis + * @param r1 radius from the begin of the tick. + * @param r2 radius from the end of the tick. + * @param nTicks number of ticks to draw + * @param color color code + */ + +void Graphics2D::drawNTicksAA(int16_t cx, int16_t cy, int16_t r1, int16_t r2, int16_t nTicks, uint16_t color, int16_t skip_every_nth) { + if (360 % nTicks != 0) { + const float deltaAngle = 360.0f / nTicks; + for (int h = nTicks-1; h >= 0; --h) { + if (h % skip_every_nth != 0) + drawTickAA(cx, cy, r1, r2, h * deltaAngle, color); + } + } else { + const int deltaAngle = 360 / nTicks; + for (int h = nTicks-1; h >= 0; --h) { + if (h % skip_every_nth != 0) + drawTickAA(cx, cy, r1, r2, h * deltaAngle, color); + } + } +} + +/** + * Draw an arc + * + * @param cx Arc X center coordinates + * @param cy Arc Y center coordinates + * @param start Beginning angle of the arc (in deg). O° is equivalent to 12AM + * @param stop End angle of the arc (in deg). + * @param steps Number of lines that will compose the arc. + * @param radius Radius of the arc from the cx/cy center + * @param lineRadius Radius of the line. Example : radius = 1 give a line of 4 px of diameter, radius 2 -> 8px, + * etc.... + * @param color Color code of the arc + * @param highQuality + * @param anti_alias + */ +void Graphics2D::drawArc(int16_t cx, int16_t cy, float start, float stop, int16_t steps, int16_t radius, int16_t lineRadius, + uint16_t color, bool highQuality, bool anti_alias) { + + int32_t old_x = radius * 4; // impossible value + int32_t old_y = radius * 4; + for (float alpha = start; alpha <= stop; alpha += 0.1f) { + int32_t x = rpx(cx, radius, alpha); + int32_t y = rpy(cy, radius, alpha); + if (old_x != x || old_y != y) { + if (anti_alias) + fillCircleAA(x, y, lineRadius, color); + else + fillCircle(x, y, lineRadius, color); + } + old_x = x; + old_y = y; + } + return; +} + +void Graphics2D::drawBWBitmap(int16_t x0, int16_t y0, int16_t cnt, int16_t h, uint8_t* bitmap, uint16_t color, + uint16_t bgColor, bool drawBackground) { + // cnt: Number of bytes of the bitmap in horizontal direction. The width of the bitmap is cnt*8. + // h: Height of the bitmap. + + for (int32_t x = 0; x < cnt; x++) { + for (int32_t y = 0; y < h; y++) { + uint8_t bits = bitmap[x + y * cnt]; + for (uint8_t b = 1; b <= 8; b++) { + if (bits & (1 << (8 - b))) { + drawPixel(x * 8 + x0 + b, y + y0, color); + } else if (drawBackground) { + drawPixel(x * 8 + x0 + b, y + y0, bgColor); + } + } + } + } +} + +/** + * @brief Fill all the display with a color. + * + * Used for initialisation of the display with a background color. + * + * @param color Color code + */ +void Graphics2D::fill(uint16_t color) { + for (int16_t x = width-1; x >= 0; --x) { + for (int16_t y = height-1; y >= 0 ; --y) { + drawPixel(x, y, color); + } + } +} + +void Graphics2D::dim(uint8_t amount) { + for (int16_t x = width-1; x >= 0; --x) { + for (int16_t y = height-1; y >= 0 ; --y) { + drawPixel(x, y, dimColor(getPixel(x, y), amount)); + } + } +} + +void Graphics2D::drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source) { + for (int16_t y = source->getHeight()-1; y >= 0; --y) { + for (int16_t x = source->getWidth() - 1; x >= 0; --x) { + drawPixel(x + offsetX, y + offsetY, source->getPixel(x, y)); + } + } +} + +void Graphics2D::drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source, int16_t sourceOffsetX, + int16_t sourceOffsetY, int16_t sourceWidth, int16_t sourceHeight) { + for (int16_t x = sourceWidth - 1; x >= 0; --x) { + for (int16_t y = sourceHeight - 1;y >= 0; --y) { + drawPixel(x + offsetX, y + offsetY, source->getPixel(x + sourceOffsetX, y + sourceOffsetY)); + } + } +} + +// draw scaled by 2x +void Graphics2D::drawGraphics2D_2x(int16_t offsetX, int16_t offsetY, Graphics2D* source) { + for (int32_t x = source->getWidth() * 2 - 1; x >= 0; --x) { + for (int32_t y = source->getHeight() * 2 -1; y >= 0; --y) { + drawPixel(x + offsetX, y + offsetY, source->getPixel(x / 2, y / 2)); + } + } +} + +// draw section scaled by 2x +void Graphics2D::drawGraphics2D_2x(int16_t offsetX, int16_t offsetY, Graphics2D* source, int16_t sourceOffsetX, + int16_t sourceOffsetY, int16_t sourceWidth, int16_t sourceHeight) { + for (int16_t x = sourceWidth * 2 - 1; x >= 0; --x) { + for (int16_t y = sourceHeight * 2 -1; y >= 0; --y) { + drawPixel(x + offsetX, y + offsetY, source->getPixel(sourceOffsetX + x / 2, sourceOffsetY + y / 2)); + } + } +} + +#ifdef ROTATE_LEGACY +// this rotate function is faster, but it has artifacts +void Graphics2D::drawGraphics2D_rotatedLegacy(uint16_t offsetX, uint16_t offsetY, Graphics2D* source, uint16_t rotationX, + uint16_t rotationY, float angle) { + float cosA = cosh(angle); + float sinA = sinh(angle); + for (uint16_t x = 0; x < source->getWidth(); x++) { + for (uint16_t y = 0; y < source->getHeight(); y++) { + int32_t newX = (x - rotationX) * cosA + (y - rotationY) * sinA; + int32_t newY = (y - rotationY) * cosA - (x - rotationX) * sinA; + drawPixel(newX + offsetX, newY + offsetY, source->getPixel(x, y)); + } + } +} +#endif + +void Graphics2D::drawGraphics2D_rotated(int16_t offsetX, int16_t offsetY, Graphics2D* source, int16_t rx, int16_t ry, + float angle) { + float cosA = cosf(angle); + float sinA = sinf(angle); + // rotateX = (x - rx) * cosf(angle) + (y - ry) * sinf(angle); + // rotateY = (y - ry) * cosf(angle) - (x - rx) * sinf(angle); + + // first calculate the bounding box of the new image + // // top left + int32_t tl_x = rotateX(0, 0, rx, ry, cosA, sinA); + int32_t tl_y = rotateY(0, 0, rx, ry, cosA, sinA); + // // top right + int32_t tr_x = rotateX(source->getWidth() - 1, 0, rx, ry, cosA, sinA); + int32_t tr_y = rotateY(source->getWidth() - 1, 0, rx, ry, cosA, sinA); + + // // bottom left + int32_t bl_x = rotateX(0, source->getHeight() - 1, rx, ry, cosA, sinA); + int32_t bl_y = rotateY(0, source->getHeight() - 1, rx, ry, cosA, sinA); + + // // bottom right + int32_t br_x = rotateX(source->getWidth(), source->getHeight(), rx, ry, cosA, sinA); + int32_t br_y = rotateY(source->getWidth(), source->getHeight(), rx, ry, cosA, sinA); + + // debug: draw rotated image + // this->drawLine(offsetX + tl_x, offsetY + tl_y, offsetX + tr_x, offsetY + tr_y, rgb565(255, 0, 0)); + // this->drawLine(offsetX + tr_x, offsetY + tr_y, offsetX + br_x, offsetY + br_y, rgb565(255, 0, 0)); + // this->drawLine(offsetX + bl_x, offsetY + bl_y, offsetX + br_x, offsetY + br_y, rgb565(255, 0, 0)); + // this->drawLine(offsetX + bl_x, offsetY + bl_y, offsetX + tl_x, offsetY + tl_y, rgb565(255, 0, 0)); + + // determine bounding box + int32_t boxX = min(tl_x, min(tr_x, min(bl_x, br_x))); + int32_t boxY = min(tl_y, min(tr_y, min(bl_y, br_y))); + int32_t boxW = max(tl_x, max(tr_x, max(bl_x, br_x))) - boxX; + int32_t boxH = max(tl_y, max(tr_y, max(bl_y, br_y))) - boxY; + + // debug: draw bounding box + // this->drawFrame(boxX + offsetX, boxY + offsetY, boxW, boxH, rgb565(0, 255, 0)); + cosA = cosA; // cosA = cos(-angle); + sinA = -sinA; //sinA = sin(-angle); + for (int16_t x = boxX; x < boxX + boxW; x++) { + for (int16_t y = boxY; y < boxY + boxH; y++) { + if (pointInsideTriangle(x, y, tl_x, tl_y, tr_x, tr_y, br_x, br_y)) { + int16_t origX = rotateX(x, y, 0, 0, cosA, sinA); + int16_t origY = rotateY(x, y, 0, 0, cosA, sinA); + drawPixel(x + offsetX, y + offsetY, source->getPixel(origX + rx, origY + ry)); + } else if (pointInsideTriangle(x, y, tl_x, tl_y, bl_x, bl_y, br_x, br_y)) { + int16_t origX = rotateX(x, y, 0, 0, cosA, sinA); + int16_t origY = rotateY(x, y, 0, 0, cosA, sinA); + drawPixel(x + offsetX, y + offsetY, source->getPixel(origX + rx, origY + ry)); + } + } + } +} diff --git a/src/gfx_util.cpp b/src/gfx_util.cpp index 425a5b9c5..8b4cbdb66 100644 --- a/src/gfx_util.cpp +++ b/src/gfx_util.cpp @@ -2,27 +2,22 @@ #include -uint16_t rgb565(uint8_t red, uint8_t green, uint8_t blue) { - return ((red & 0b00011111000) << 8) | ((green & 0b00011111100) << 3) | (blue >> 3); -} - -uint32_t rgb888(uint8_t red, uint8_t green, uint8_t blue) { - return ((uint32_t)red << 16) | ((uint32_t)green << 8) | (uint32_t)blue; -} - -uint16_t rgb888to565(uint32_t rgb888) { - return rgb565(rgb888_red(rgb888), rgb888_green(rgb888), rgb888_blue(rgb888)); -} +uint16_t blend(uint16_t target, uint16_t source, float alpha) { + uint8_t r = rgb565_red(source) * alpha + rgb565_red(target) * (1.0f - alpha); + uint8_t g = rgb565_green(source) * alpha + rgb565_green(target) * (1.0f - alpha); + uint8_t b = rgb565_blue(source) * alpha + rgb565_blue(target) * (1.0f - alpha); -uint32_t rgb565to888(uint16_t rgb565) { - return rgb888(rgb565_red(rgb565), rgb565_green(rgb565), rgb565_blue(rgb565)); + return rgb565(r, g, b); } -uint16_t blend(uint16_t target, uint16_t source, float alpha) { - uint8_t r = rgb565_red(source) * alpha + rgb565_red(target) * (1.0 - alpha); - uint8_t g = rgb565_green(source) * alpha + rgb565_green(target) * (1.0 - alpha); - uint8_t b = rgb565_blue(source) * alpha + rgb565_blue(target) * (1.0 - alpha); +// optimized integer version +uint16_t blend(uint16_t target, uint16_t source, uint8_t alpha) { + const uint8_t a_inv = 255 - alpha; + uint8_t r = (rgb565_red(source) * alpha + rgb565_red(target) * a_inv) / 255; + uint8_t g = (rgb565_green(source) * alpha + rgb565_green(target) * a_inv) / 255; + uint8_t b = (rgb565_blue(source) * alpha + rgb565_blue(target) * a_inv) / 255; + return rgb565(r, g, b); } @@ -57,36 +52,6 @@ uint16_t changeColor(uint16_t oc, float amount) { return nc; } -uint8_t rgb565_red(uint16_t rgb565) { - // |rrrrrggg|gggbbbbb| - return (rgb565 >> 8) & 0b11111000; -} - -uint8_t rgb565_green(uint16_t rgb565) { - // |rrrrrggg|gggbbbbb| - return (rgb565 >> 3) & 0b11111100; -} - -uint8_t rgb565_blue(uint16_t rgb565) { - // |rrrrrggg|gggbbbbb| - return (rgb565 << 3); -} - -uint8_t rgb888_red(uint32_t rgb888) { - // |rrrrrrrr|gggggggg|bbbbbbbb| - return rgb888 >> 16; -} - -uint8_t rgb888_green(uint32_t rgb888) { - // |rrrrrrrr|gggggggg|bbbbbbbb| - return rgb888 >> 8; -} - -uint8_t rgb888_blue(uint32_t rgb888) { - // |rrrrrrrr|gggggggg|bbbbbbbb| - return rgb888; -} - // Shamelessly copied from // https://stackoverflow.com/questions/3018313/algorithm-to-convert-rgb-to-hsv-and-hsv-to-rgb-in-range-0-255-for-both void hsvToRgb(const unsigned char& h, const unsigned char& s, const unsigned char& v, unsigned char& r, diff --git a/src/hal/Arduino_Canvas_Graphics2D.cpp b/src/hal/Arduino_Canvas_Graphics2D.cpp index 65dfba64c..f5b807efb 100644 --- a/src/hal/Arduino_Canvas_Graphics2D.cpp +++ b/src/hal/Arduino_Canvas_Graphics2D.cpp @@ -5,30 +5,15 @@ #endif #include -#include "config_defaults.h" +Arduino_Canvas_Graphics2D::Arduino_Canvas_Graphics2D(int16_t w, int16_t h, Arduino_G* output, int16_t output_x, int16_t output_y) + : Graphics2DPrint(w, h, DISP_CHUNK_H_LD, true), _output(output), _output_x(output_x), _output_y(output_y) + {} -Arduino_Canvas_Graphics2D::Arduino_Canvas_Graphics2D(int16_t w, int16_t h, Arduino_G* output, int16_t output_x, - int16_t output_y) - : Graphics2DPrint(w, h, DISP_CHUNK_H, true), _output(output), _output_x(output_x), _output_y(output_y) {} - -void Arduino_Canvas_Graphics2D::begin(int32_t speed) { - _output->begin(speed); - // _output->fillScreen(BLACK); -} -void Arduino_Canvas_Graphics2D::writePixelPreclipped(int16_t x, int16_t y, uint16_t color) { - this->drawPixel(x, y, color); -} -void Arduino_Canvas_Graphics2D::writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { - this->drawVLine(x, y, h, color); -} -void Arduino_Canvas_Graphics2D::writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { - this->drawHLine(x, y, w, color); -} void Arduino_Canvas_Graphics2D::flush() { // only flush if there is a buffer if (this->hasBuffer()) { - uint8_t chunkHeight = this->getChunkHeight(); - for (uint8_t chunk = 0; chunk < this->numChunks(); chunk++) { + uint8_t chunkHeight = 1 << chunkHeightLd; + for (uint8_t chunk = 0; chunk < this->getNumChunks(); chunk++) { _output->draw16bitRGBBitmap(this->getChunkOffset(chunk), chunk * chunkHeight, this->getChunk(chunk), this->getChunkWidth(chunk), chunkHeight); } diff --git a/src/hal/display.cpp b/src/hal/display.cpp index 4d26cb6ee..cf3f4f085 100644 --- a/src/hal/display.cpp +++ b/src/hal/display.cpp @@ -71,6 +71,12 @@ void OswHal::setupDisplay() { this->canvas = new Arduino_Canvas_Graphics2D(DISP_W, DISP_H, tft); this->canvas->begin(); // use default speed and default SPI mode + + // Another nasty hack to avoid display flicker when turning on + // Let the display settle a bit befor sending commands to it. + // This avoids a partially white screen on turning on the device. + // 45 ms is tested on an V3.3 device. + delay(45); this->displayOn(); } diff --git a/src/hal/power.cpp b/src/hal/power.cpp index 5ed16b15f..5f3bfd852 100644 --- a/src/hal/power.cpp +++ b/src/hal/power.cpp @@ -14,6 +14,64 @@ #define V_REF 1100 // ADC reference voltage #define CHANNEL ADC2_CHANNEL_8 +// Some functions to determine the capacity of batteries +// see https://github.com/rlogiacco/BatterySense +// define one of the following +#define BATT_ASIGMOIDAL +//#define BATT_SIGMOIDAL +//#define BATT_LINEAR +//#define BATT_LEGACY + +/** + * Symmetric sigmoidal approximation + * https://www.desmos.com/calculator/7m9lu26vpy + * + * c - c / (1 + k*x/v)^3 + */ +static inline uint8_t sigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { + int volt_diff = maxVoltage - minVoltage; + if ( volt_diff <= 0) + volt_diff = 1; + + // slow + // uint8_t result = 110 - (110 / (1 + pow(1.468f * (voltage - minVoltage)/(volt_diff), 6f))); + + // steep + // uint8_t result = 102 - (102 / (1 + pow(1.621f * (voltage - minVoltage)/(volt_diff), 8.1f))); + + // normal + uint8_t result = 105 - (105 / (1 + powf(1.724f * (voltage - minVoltage) / volt_diff, 5.5f))); + return result >= 100 ? 100 : result; +} + +/** + * Asymmetric sigmoidal approximation + * https://www.desmos.com/calculator/oyhpsu8jnw + * + * c - c / [1 + (k*x/v)^4.5]^3 + */ +static inline uint8_t asigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { + int volt_diff = maxVoltage - minVoltage; + if ( volt_diff <= 0) + volt_diff = 1; + uint8_t result = 101 - (101 / powf(1 + powf(1.33f * (voltage - minVoltage) / volt_diff, 4.5f), 3)); + return result >= 100 ? 100 : result; +} + +/** + * Linear mapping + * https://www.desmos.com/calculator/sowyhttjta + * + * x * 100 / v + */ +static inline uint8_t linear(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { + int volt_diff = maxVoltage - minVoltage; + if ( volt_diff <= 0) + volt_diff = 1; + + return (unsigned long)(voltage - minVoltage) * 100 / volt_diff; +} + uint16_t OswHal::getBatteryRawMin() { return this->powerPreferences.getUShort("-", 60); // Every battery should be able to deliver lower than this at some point } @@ -92,15 +150,23 @@ uint16_t OswHal::getBatteryRaw(const uint16_t numAvg) { uint8_t OswHal::getBatteryPercent(void) { const uint16_t batRaw = this->getBatteryRaw(); +#ifdef BATT_ASIGMOIDAL + return asigmoidal(batRaw, this->getBatteryRawMin(), this->getBatteryRawMax()); +#elif BATT_SIGMOIDAL + return sigmoidal(batRaw, this->getBatteryRawMin(), this->getBatteryRawMax()); +#elif BATT_LINEAR + return linear(batRaw, this->getBatteryRawMin(), this->getBatteryRawMax()); +#else // BATT_LEGACY + // https://en.wikipedia.org/wiki/Logistic_function + // f(x)=L/(1+e(-k(x-x0))) // The value for k (=12) is chosen by guessing, just make sure f(0) < 0.5 to indicate the calibration process... // Original Formula: 1/(1+e^(-12*(x-0.5))*((1/0.5)-1)) // Optimized Formula: 1/(1+e^(-12*(x-0.5))) const float minMaxDiff = (float) max(abs(this->getBatteryRawMax() - this->getBatteryRawMin()), 1); // To prevent division by zero const float batNormalized = ((float) batRaw - (float) this->getBatteryRawMin()) * (1.0f / minMaxDiff); - const float batTransformed = 1 / (1 + pow(2.71828, -12 * (batNormalized - 0.5))); - + const float batTransformed = 1.0f / (1 + powf(2.71828f, -12 * (batNormalized - 0.5f))); // Just in case here is a bug ;) // OSW_LOG_D("r", batRaw, @@ -111,6 +177,7 @@ uint8_t OswHal::getBatteryPercent(void) { // "t", batTransformed); return max(min((uint8_t) roundf(batTransformed * 100), (uint8_t) 100), (uint8_t) 0); +#endif } // float OswHal::getBatteryVoltage(void) { @@ -125,7 +192,7 @@ uint8_t OswHal::getBatteryPercent(void) { // esp_adc_cal_get_voltage(ADC_CHANNEL_8, &characteristics, &voltage); // // some dodgy math to get a representable value -// return voltage / (100.0) + 0.3; +// return voltage / (100.0f) + 0.3f; // } void OswHal::setCPUClock(uint8_t mhz) { @@ -145,7 +212,7 @@ void OswHal::doSleep(bool deepSleep) { // register user wakeup sources if (OswConfigAllKeys::buttonToWakeEnabled.get()) - // ore set Button1 wakeup if no sensor wakeups registered + // or set Button1 wakeup if no sensor wakeups registered esp_sleep_enable_ext0_wakeup(GPIO_NUM_0 /* BTN_0 */, LOW); // special handling as low is the trigger, otherwise ↓ bitmask should be used! /** @@ -291,7 +358,8 @@ void OswHal::noteUserInteraction() { void OswHal::handleDisplayTimout() { // Did enough time pass since the last user interaction? - if(OswConfigAllKeys::settingDisplayTimeout.get() == 0 or this->_lastUserInteraction + OswConfigAllKeys::settingDisplayTimeout.get() * 1000 > millis()) + const int lastDisplayTimeout = OswConfigAllKeys::settingDisplayTimeout.get(); + if (lastDisplayTimeout == 0 or this->_lastUserInteraction + lastDisplayTimeout * 1000 > millis()) return; // Does the UI allow us to go to sleep? OswAppV2* app = OswUI::getInstance()->getRootApplication(); diff --git a/src/main.cpp b/src/main.cpp index 553fd4ab7..32155d6b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -68,6 +68,7 @@ #include "./apps/watchfaces/OswAppWatchfaceBinary.h" #include "./apps/watchfaces/OswAppWatchfaceMonotimer.h" #include "./apps/watchfaces/OswAppWatchfaceNumerals.h" +#include "./apps/watchfaces/OswAppWatchfaceFitnessAnalog.h" #if OSW_PLATFORM_ENVIRONMENT_MAGNETOMETER == 1 && OSW_PLATFORM_HARDWARE_QMC5883L == 1 #include "./apps/_experiments/magnetometer_calibrate.h" #endif @@ -121,6 +122,7 @@ void setup() { main_mainDrawer.registerAppLazy(LANG_WATCHFACES); main_mainDrawer.registerAppLazy(LANG_WATCHFACES); main_mainDrawer.registerAppLazy(LANG_WATCHFACES); + main_mainDrawer.registerAppLazy(LANG_WATCHFACES); try { main_mainDrawer.startApp(OswConfigAllKeys::settingDisplayDefaultWatchface.get().c_str()); // if this id is invalid, the drawer will fall back to alternatives automatically } catch(const std::runtime_error& e) { diff --git a/src/math_angles.cpp b/src/math_angles.cpp index c7b17923e..a69da6cdf 100644 --- a/src/math_angles.cpp +++ b/src/math_angles.cpp @@ -2,30 +2,6 @@ #include -// rotate a point around a center (cy,cy), with a radius r, 0 degrees is 12 o'clock -float rpx(float cx, float r, float d) { - return cx + r * cos((d - 90) * 1000.0 / 57296.0); -} -float rpy(float cy, float r, float d) { - return cy + r * sin((d - 90) * 1000.0 / 57296.0); -} - -// rotate a point around a point -int32_t rotateX(int32_t x, int32_t y, int32_t rx, int32_t ry, float cosA, float sinA) { - return (x - rx) * cosA + (y - ry) * sinA; -} -int32_t rotateY(int32_t x, int32_t y, int32_t rx, int32_t ry, float cosA, float sinA) { - return (y - ry) * cosA - (x - rx) * sinA; -} - -int32_t rotateX(int32_t x, int32_t y, int32_t rx, int32_t ry, float a) { - return (x - rx) * cos(a) + (y - ry) * sin(a); -} -int32_t rotateY(int32_t x, int32_t y, int32_t rx, int32_t ry, float a) { - return (y - ry) * cos(a) - (x - rx) * sin(a); -} - - // seconds to degrees (0-360) float s2d(long seconds) { return (seconds % 60) * 6; @@ -34,13 +10,13 @@ float s2d(long seconds) { // minutes to degrees (0-360) float m2d(long seconds) { long fh = (seconds / 3600); // full hours - return (((seconds - fh * 3600) / 60.0)) * 6; + return (((seconds - fh * 3600) / 60.0f)) * 6; } // hours to degrees (0-360) float h2d(long seconds) { long fd = (seconds / 3600) / 24; // full days - return ((seconds - fd * 24 * 3600) / 3600.0) * 30; + return ((seconds - fd * 24 * 3600) / 3600.0f) * 30; } float sign(float x1, float y1, float x2, float y2, float x3, float y3) { diff --git a/src/math_osm.cpp b/src/math_osm.cpp index ac65ab18b..16cfe140e 100644 --- a/src/math_osm.cpp +++ b/src/math_osm.cpp @@ -7,12 +7,12 @@ // we return float here, because we need the fraction float lon2tilex(float lon, uint8_t z) { - return (lon + 180.0) / 360.0 * (float)(1 << z); + return (lon + 180.0f) / 360.0f * (float)(1 << z); } float lat2tiley(float lat, uint8_t z) { - float latrad = lat * PI / 180.0; - return (1.0 - asinh(tan(latrad)) / PI) / 2.0 * (float)(1 << z); + float latrad = lat * PI / 180.0f; + return (1.0f - asinh(tanf(latrad)) / PI) / 2.0f * (float)(1 << z); } // helper function to get the offset within the tile @@ -22,18 +22,18 @@ int32_t tileOffset(float tilex) { } float tilex2lon(float x, uint8_t z) { - return x / (float)(1 << z) * 360.0 - 180; + return x / (float)(1 << z) * 360.0f - 180.0f; } float tiley2lat(float y, uint8_t z) { float n = PI - TWO_PI * y / (float)(1 << z); - return 180.0 / PI * atan(0.5 * (exp(n) - exp(-n))); + return 180.0f / PI * atanf(0.5f * (expf(n) - expf(-n))); } -float osmResolution[] = {156543.03, 78271.52, 39135.76, 19567.88, 9783.94, 4891.97, 2445.98, 1222.99, 611.50, 305.75, - 152.87, 76.43, 38.21, 19.10, 9.55, 4.77, 2.38, 1.19, 0.59 +float osmResolution[] = {156543.03f, 78271.52f, 39135.76f, 19567.88f, 9783.94f, 4891.97f, 2445.98f, 1222.99f, 611.50f, 305.75f, + 152.87f, 76.43f, 38.21f, 19.10f, 9.55f, 4.77f, 2.38f, 1.19f, 0.59f }; float getTileResolution(float lat, uint8_t z) { - return 156543.03 /*meters/pixel*/ * cos(lat) / (2 ^ z); + return 156543.03f /*meters/pixel*/ * cosf(lat) / (2 ^ z); } diff --git a/src/math_tables.cpp b/src/math_tables.cpp new file mode 100644 index 000000000..1a48b964a --- /dev/null +++ b/src/math_tables.cpp @@ -0,0 +1,52 @@ + +// These are values for the trigonometric functions +// sinDeg[12]=sinf(12*PI/180.0f) + +extern const float sinDeg[]; + +const float sinDeg[] = { ++0.000000, +0.017452, +0.034899, +0.052336, +0.069756, +0.087156, +0.104528, +0.121869, ++0.139173, +0.156434, +0.173648, +0.190809, +0.207912, +0.224951, +0.241922, +0.258819, ++0.275637, +0.292372, +0.309017, +0.325568, +0.342020, +0.358368, +0.374607, +0.390731, ++0.406737, +0.422618, +0.438371, +0.453991, +0.469472, +0.484810, +0.500000, +0.515038, ++0.529919, +0.544639, +0.559193, +0.573577, +0.587785, +0.601815, +0.615662, +0.629320, ++0.642788, +0.656059, +0.669131, +0.681998, +0.694658, +0.707107, +0.719340, +0.731354, ++0.743145, +0.754710, +0.766044, +0.777146, +0.788011, +0.798636, +0.809017, +0.819152, ++0.829038, +0.838671, +0.848048, +0.857167, +0.866025, +0.874620, +0.882948, +0.891007, ++0.898794, +0.906308, +0.913545, +0.920505, +0.927184, +0.933580, +0.939693, +0.945519, ++0.951057, +0.956305, +0.961262, +0.965926, +0.970296, +0.974370, +0.978148, +0.981627, ++0.984808, +0.987688, +0.990268, +0.992546, +0.994522, +0.996195, +0.997564, +0.998630, ++0.999391, +0.999848, +1.000000, +0.999848, +0.999391, +0.998630, +0.997564, +0.996195, ++0.994522, +0.992546, +0.990268, +0.987688, +0.984808, +0.981627, +0.978148, +0.974370, ++0.970296, +0.965926, +0.961262, +0.956305, +0.951056, +0.945519, +0.939693, +0.933580, ++0.927184, +0.920505, +0.913545, +0.906308, +0.898794, +0.891006, +0.882948, +0.874620, ++0.866025, +0.857167, +0.848048, +0.838671, +0.829037, +0.819152, +0.809017, +0.798635, ++0.788011, +0.777146, +0.766044, +0.754710, +0.743145, +0.731354, +0.719340, +0.707107, ++0.694658, +0.681998, +0.669131, +0.656059, +0.642787, +0.629320, +0.615661, +0.601815, ++0.587785, +0.573576, +0.559193, +0.544639, +0.529919, +0.515038, +0.500000, +0.484810, ++0.469472, +0.453990, +0.438371, +0.422618, +0.406737, +0.390731, +0.374606, +0.358368, ++0.342020, +0.325568, +0.309017, +0.292372, +0.275637, +0.258819, +0.241922, +0.224951, ++0.207912, +0.190809, +0.173648, +0.156434, +0.139173, +0.121869, +0.104528, +0.087156, ++0.069756, +0.052336, +0.034899, +0.017452, -0.000000, -0.017453, -0.034900, -0.052336, +-0.069756, -0.087156, -0.104528, -0.121869, -0.139173, -0.156435, -0.173648, -0.190809, +-0.207912, -0.224951, -0.241922, -0.258819, -0.275638, -0.292372, -0.309017, -0.325568, +-0.342020, -0.358368, -0.374607, -0.390731, -0.406737, -0.422618, -0.438371, -0.453991, +-0.469472, -0.484810, -0.500000, -0.515038, -0.529919, -0.544639, -0.559193, -0.573577, +-0.587785, -0.601815, -0.615661, -0.629320, -0.642788, -0.656059, -0.669131, -0.681998, +-0.694659, -0.707107, -0.719340, -0.731354, -0.743145, -0.754710, -0.766045, -0.777146, +-0.788011, -0.798635, -0.809017, -0.819152, -0.829038, -0.838671, -0.848048, -0.857167, +-0.866025, -0.874620, -0.882948, -0.891007, -0.898794, -0.906308, -0.913545, -0.920505, +-0.927184, -0.933581, -0.939693, -0.945519, -0.951057, -0.956305, -0.961262, -0.965926, +-0.970296, -0.974370, -0.978148, -0.981627, -0.984808, -0.987688, -0.990268, -0.992546, +-0.994522, -0.996195, -0.997564, -0.998630, -0.999391, -0.999848, -1.000000, -0.999848, +-0.999391, -0.998630, -0.997564, -0.996195, -0.994522, -0.992546, -0.990268, -0.987688, +-0.984808, -0.981627, -0.978148, -0.974370, -0.970296, -0.965926, -0.961262, -0.956305, +-0.951056, -0.945519, -0.939693, -0.933580, -0.927184, -0.920505, -0.913545, -0.906308, +-0.898794, -0.891006, -0.882948, -0.874620, -0.866025, -0.857167, -0.848048, -0.838670, +-0.829038, -0.819152, -0.809017, -0.798635, -0.788011, -0.777146, -0.766044, -0.754709, +-0.743145, -0.731354, -0.719339, -0.707107, -0.694658, -0.681998, -0.669131, -0.656059, +-0.642787, -0.629320, -0.615661, -0.601815, -0.587785, -0.573576, -0.559193, -0.544639, +-0.529919, -0.515038, -0.500000, -0.484809, -0.469471, -0.453991, -0.438371, -0.422618, +-0.406736, -0.390731, -0.374606, -0.358368, -0.342020, -0.325568, -0.309017, -0.292372, +-0.275638, -0.258819, -0.241922, -0.224951, -0.207911, -0.190809, -0.173648, -0.156434, +-0.139173, -0.121869, -0.104529, -0.087156, -0.069756, -0.052336, -0.034900, -0.017452, 0.000000}; diff --git a/src/osw_config_keys.cpp b/src/osw_config_keys.cpp index 3baa34ac4..7d0ef86c7 100644 --- a/src/osw_config_keys.cpp +++ b/src/osw_config_keys.cpp @@ -16,6 +16,7 @@ #include "apps/watchfaces/OswAppWatchfaceBinary.h" #include "apps/watchfaces/OswAppWatchfaceMonotimer.h" #include "apps/watchfaces/OswAppWatchfaceNumerals.h" +#include "apps/watchfaces/OswAppWatchfaceFitnessAnalog.h" /** * !!!WARNING!!! @@ -63,7 +64,8 @@ OswConfigKeyDropDown settingDisplayDefaultWatchface("n", "Display", "Default Wat OswAppWatchfaceFitness::APP_ID, OswAppWatchfaceBinary::APP_ID, OswAppWatchfaceMonotimer::APP_ID, - OswAppWatchfaceNumerals::APP_ID + OswAppWatchfaceNumerals::APP_ID, + OswAppWatchfaceFitnessAnalog::APP_ID }, CONFIG_DEFAULT_WATCHFACE_ID); OswConfigKeyBool settingDisplayDualHourTick("h2", "Display", "Display Dual-Time Hour Tick", "Show dual time hour tick", false); #if OSW_PLATFORM_ENVIRONMENT_ACCELEROMETER == 1 @@ -102,8 +104,8 @@ OswConfigKeyBool timeFormat("g", "Date & Time", "Use 24h time format?", nullptr, OswConfigKeyString timezonePrimary("p1", "Date & Time", "Primary Timezone", "Empty = UTC0, use values from https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv", CONFIG_TIMEZONE_PRIMARY); OswConfigKeyString timezoneSecondary("p2", "Date & Time", "Secondary Timezone", nullptr, CONFIG_TIMEZONE_SECONDARY); #if OSW_PLATFORM_ENVIRONMENT_ACCELEROMETER == 1 -OswConfigKeyShort configHeight("f4", "Fitness", "User Height", "E.g 175.7 cm -> 175 (Rounds off)", 175); -OswConfigKeyShort configWeight("f5", "Fitness", "User Weight", "E.g 70.3 kg -> 70 (Rounds off)", 70); +OswConfigKeyShort configHeight("f4", "Fitness", "User Height", "E.g 182.7 cm -> 182 (Rounds off)", 182); +OswConfigKeyShort configWeight("f5", "Fitness", "User Weight", "E.g 85.3 kg -> 70 (Rounds off)", 85); OswConfigKeyInt stepsPerDay("f1", "Fitness", "Steps per day", "> 0!", STEPS_PER_DAY); OswConfigKeyInt distPerDay("f2", "Fitness", "Distance per day", "> 0!", DIST_PER_DAY); OswConfigKeyInt kcalPerDay("f3", "Fitness", "kcalorie per day", "> 0!", KCAL_PER_DAY); diff --git a/src/osw_hal.cpp b/src/osw_hal.cpp index 6af8297da..b00bd4c4d 100644 --- a/src/osw_hal.cpp +++ b/src/osw_hal.cpp @@ -55,6 +55,7 @@ void OswHal::setup(bool fromLightSleep) { this->environment()->updateProviders(); #endif } + this->setupPower(fromLightSleep); this->setupButtons(); this->setupFileSystem(); @@ -63,6 +64,7 @@ void OswHal::setup(bool fromLightSleep) { this->setupPower(fromLightSleep); this->displayOn(); } + this->devices()->setup(fromLightSleep); this->devices()->update(); // Update internal cache to refresh / initialize the value obtained by calling this->getAccelStepCount() - needed for e.g. the step statistics! this->updateTimezoneOffsets(); // Always update, just in case DST changed during (light) sleep - after all devices are setup/updated, as they might use their time for this calculation @@ -86,10 +88,12 @@ void OswHal::stop(bool toLightSleep) { this->displayOff(); // This disables the display OswServiceManager::getInstance().stop(); - if(!toLightSleep) { + if (!toLightSleep) { + this->gfx()->fillBuffer(rgb565(0,0,0)); // This makes the display black + this->flushCanvas(); this->_environment.reset(); this->_devices.reset(); this->timeProvider = nullptr; // He is properly destroyed after devices destruction ↑ } OSW_LOG_D(toLightSleep ? "-> light sleep " : "-> deep sleep "); -} \ No newline at end of file +} diff --git a/src/osw_ui.cpp b/src/osw_ui.cpp index dabb906f6..4780e3e34 100644 --- a/src/osw_ui.cpp +++ b/src/osw_ui.cpp @@ -151,7 +151,7 @@ void OswUI::loop() { // Full-Screen progress OswHal::getInstance()->gfx()->setTextCenterAligned(); OswHal::getInstance()->gfx()->setTextSize(2.0f); - OswHal::getInstance()->gfx()->setTextCursor(DISP_W * 0.5, DISP_W * 0.5); + OswHal::getInstance()->gfx()->setTextCursor(DISP_W * 0.5f, DISP_H * 0.5f); OswHal::getInstance()->gfx()->print(this->mProgressText); this->mProgressBar->draw(); } @@ -184,7 +184,7 @@ bool OswUI::getProgressActive() { void OswUI::startProgress(const char* text) { if (!this->getProgressActive()) - this->mProgressBar = new OswUI::OswUIProgress((short)DISP_W * 0.2, (short)DISP_H * 0.6, (short)DISP_W * 0.6); + this->mProgressBar = new OswUI::OswUIProgress((short)DISP_W * 0.2f, (short)DISP_H * 0.6f, (short)DISP_W * 0.6f); this->mProgressText = text; } @@ -269,7 +269,7 @@ float OswUI::OswUIProgress::calcValue() { } void OswUI::OswUIProgress::draw() { - const float barWidth = 0.4; // For unknown progress bouncing + const float barWidth = 0.4f; // For unknown progress bouncing const short barHeight = 6; const short barRadius = 3; const float value = this->calcValue(); @@ -312,6 +312,6 @@ void OswUI::OswUINotification::draw(unsigned y) const { gfx->drawHLine(0, y, DISP_W, OswUI::getInstance()->getInfoColor()); gfx->resetText(); gfx->setTextCenterAligned(); - gfx->setTextCursor(DISP_W * 0.5, y + (this->sDrawHeight * 0.5) + 8 / 2); // To center the text, it is assumed that one char has a height of 8 pixels + gfx->setTextCursor(DISP_W * 0.5f, y + (this->sDrawHeight * 0.5f) + 8 / 2); // To center the text, it is assumed that one char has a height of 8 pixels gfx->print(this->message); -} \ No newline at end of file +} diff --git a/src/overlays/overlays.cpp b/src/overlays/overlays.cpp index 68c48a1d5..bd0e370f0 100644 --- a/src/overlays/overlays.cpp +++ b/src/overlays/overlays.cpp @@ -26,11 +26,11 @@ void drawBattery(uint16_t x, uint16_t y) { batColor = batLvl < 50 ? OswUI::getInstance()->getWarningColor() : batColor; batColor = batLvl < 25 ? OswUI::getInstance()->getDangerColor() : batColor; - if (batLvl < 0.5) { + if (batLvl < 0.5f) { // This happens initial discharging (calibration phase) of the battery or when you're in trouble! gfx->fillFrame(x + 2, y + 2, 25, 9, OswUI::getInstance()->getInfoColor()); } else { - gfx->fillFrame(x + 2, y + 2, 25 * (batLvl / 100.0), 9, batColor); // charge + gfx->fillFrame(x + 2, y + 2, 25 * (batLvl / 100.0f), 9, batColor); // charge } }