From 8a2a6e66845c2f50260f4ca12fa73a9fa17eb038 Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Sat, 2 Mar 2024 06:37:35 +0100 Subject: [PATCH 01/12] New watchface and antialiased lines, circles, arcs ... minor cleanups Avoid flickering and optimize new line drawing algorithm Use more floats (save 1k of Flash) Fix AA-circle quirk more float to go use hypotf Different ending for thick lines Try to optimize with trigonometric table. fine tune Optimize division of chunkHeight Optimized arc, better AA lower CPU freq for power savings Saves approx 50% during on time. disable cpufreq honor review Screen fill --- include/gfx_2d.h | 4 ++- platformio.ini | 26 +++++++++++++++++- .../OswAppWatchfaceFitnessAnalog.cpp | 4 +++ src/gfx_2d.cpp | 5 ++-- src/gfx_util.cpp | 27 +++++++++++++++++++ src/math_tables.cpp | 2 +- 6 files changed, 63 insertions(+), 5 deletions(-) diff --git a/include/gfx_2d.h b/include/gfx_2d.h index 79a377f68..c175466fc 100644 --- a/include/gfx_2d.h +++ b/include/gfx_2d.h @@ -95,7 +95,7 @@ class Graphics2D { * @param y y axis coordinate * @param color color code of the pixel */ - void drawPixel(int32_t x, int32_t y, uint16_t color) { + void drawPixel(int32_t x, int32_t y, uint16_t color) { drawPixelClipped(x, y, color); } @@ -312,6 +312,8 @@ class Graphics2D { void fill(uint16_t color); void dim(uint8_t amount); + + void drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source); void drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source, int16_t sourceOffsetX, diff --git a/platformio.ini b/platformio.ini index 3486d3ef5..651111c5f 100755 --- a/platformio.ini +++ b/platformio.ini @@ -17,6 +17,8 @@ platform = espressif32@^6.5.0 ; platform_packages = ; framework-arduinoespressif32 @ https://github.com/marcovannoord/arduino-esp32.git#idf-release/v4.2 ; toolchain-xtensa32 @ ~2.80400.0 +;platform_packages = toolchain-xtensa-esp32@8 +;platform_packages = toolchain-xtensa-esp32@12.2.0 board = pico32 framework = arduino board_build.partitions = min_spiffs.csv ; OTA updates (two app slots), but no space for the SPIFFS (as it is currently not used) @@ -36,7 +38,29 @@ extra_scripts = pre:scripts/build/prebuild_assets.py pre:scripts/build/prebuild_cppflags.py pre:scripts/build/prebuild_lua.py ; Needed to generate the .cxx file(s), enabled via "OSW_FEATURE_LUA" build flag -build_unflags = -std=gnu++11 # The correct flag will be set by the cppflags python script... +build_unflags = -std=gnu++11 # The correct flag will be set by the cppflags python script... + -fno-jump-table + +[env:LIGHT_EDITION_V3_3_LUA] + +;build_unflags = '-std=gnu++2a' + + +build_flags = + -D OSW_TARGET_PLATFORM_HEADER='"platform/LIGHT_EDITION_V3_3.h"' + -D OSW_FEATURE_STATS_STEPS + -D OSW_FEATURE_WIFI +; -D OSW_FEATURE_LUA +; -D LUA_C89_NUMBERS ; Required by OSW_FEATURE_LUA + -Wdouble-promotion + -ffunction-sections + -fdata-sections + -Wno-deprecated-declarations + -fpermissive +; -O2 + -fjump-tables + +build_type = debug ; Light edition by hardware revisions ; This revision did not change anything on hardware or software level, it just resized the pcb from V3_2 diff --git a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp index e6083a5bd..219dea889 100644 --- a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp @@ -16,6 +16,10 @@ #define CENTER_X (DISP_W / 2) #define CENTER_Y (DISP_H / 2) +#ifdef GIF_BG +OswAppGifPlayer* bgGif = new OswAppGifPlayer(); +#endif + inline uint32_t OswAppWatchfaceFitnessAnalog::calculateDistance(uint32_t steps) { float userHeight = OswConfigAllKeys::configHeight.get(); float avgDist; diff --git a/src/gfx_2d.cpp b/src/gfx_2d.cpp index 793d3ac27..01ddbc5d1 100644 --- a/src/gfx_2d.cpp +++ b/src/gfx_2d.cpp @@ -568,6 +568,7 @@ void Graphics2D::drawThickLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, 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, @@ -716,7 +717,7 @@ void Graphics2D::_drawCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_ * @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 + CIRC_OPT option) { // see p3dt_gfx_2d_license.txt float f; float ddFx; @@ -1514,7 +1515,7 @@ void Graphics2D::drawGraphics2D_2x(int16_t offsetX, int16_t offsetY, Graphics2D* #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) { + uint16_t rotationY, float angle) { float cosA = cosh(angle); float sinA = sinh(angle); for (uint16_t x = 0; x < source->getWidth(); x++) { diff --git a/src/gfx_util.cpp b/src/gfx_util.cpp index 6202ee13c..ed2390532 100644 --- a/src/gfx_util.cpp +++ b/src/gfx_util.cpp @@ -21,6 +21,33 @@ uint16_t blend(uint16_t target, uint16_t source, uint8_t alpha) { return rgb565(r, g, b); } +// 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); +} + +// ToDo check if this function is faster +/** + * Fast RGB565 pixel blending + * @param fg The foreground color in uint16_t RGB565 format + * @param bg The background color in uint16_t RGB565 format + * @param alpha The alpha in range 0-255 + **/ +/*uint16_t alphaBlendRGB565( uint32_t fg, uint32_t bg, uint8_t alpha ){ + alpha = ( alpha + 4 ) >> 3; + bg = (bg | (bg << 16)) & 0b00000111111000001111100000011111; + fg = (fg | (fg << 16)) & 0b00000111111000001111100000011111; + uint32_t result = ((((fg - bg) * alpha) >> 5) + bg) & 0b00000111111000001111100000011111; + return (uint16_t)((result >> 16) | result); +} +*/ + /** * @brief Calculated the color code of a dimmed color * diff --git a/src/math_tables.cpp b/src/math_tables.cpp index 9af5a1a2c..7e3b3f182 100644 --- a/src/math_tables.cpp +++ b/src/math_tables.cpp @@ -5,7 +5,7 @@ extern const float sinDeg[]; const float sinDeg[] = { - +0.000000, +0.017452, +0.034899, +0.052336, +0.069756, +0.087156, +0.104528, +0.121869, + +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, From 04ac22d6512b8297cb2e63480421ec07969e412d Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Sat, 6 Apr 2024 19:47:58 +0200 Subject: [PATCH 02/12] Save power by lowering frequency, and change UARC clk to a stable source --- CMakeLists.txt | 4 ++- include/osw_pins.h | 6 ++-- src/gfx_2d.cpp | 1 - src/hal/buttons.cpp | 22 +++++++++++++++ src/hal/power.cpp | 12 +++++--- src/main.cpp | 69 +++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 101 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 446455932..ee53467ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,6 +157,8 @@ target_compile_options(emulator.run PUBLIC -O0 -g3 -Wall + -Wdouble-promotion + -fpermissive > $<$: -O4 @@ -171,4 +173,4 @@ endif() # And add unit testing, as we broke our stuff often enough... enable_testing() list(APPEND CMAKE_CTEST_ARGUMENTS "--verbose") # this will always show the logs and not only on failure, like "--output-on-failure" does -add_test(NAME emulator.run COMMAND emulator.run --unit_tests --headless) \ No newline at end of file +add_test(NAME emulator.run COMMAND emulator.run --unit_tests --headless) diff --git a/include/osw_pins.h b/include/osw_pins.h index 36d15f183..28c74f3e6 100644 --- a/include/osw_pins.h +++ b/include/osw_pins.h @@ -45,10 +45,12 @@ #define VIBRATE 35 #else #define BTN_1 0 -#define BTN_2 13 -#define BTN_3 10 +#define BTN_2 10 +#define BTN_3 13 #endif +#define BMA400_INT 34 + // assign active LOW or HIGH states according to hardware #if defined(GPS_EDITION_ROTATED) #define BTN_STATE_ARRAY {HIGH, HIGH, LOW} diff --git a/src/gfx_2d.cpp b/src/gfx_2d.cpp index 01ddbc5d1..183c11017 100644 --- a/src/gfx_2d.cpp +++ b/src/gfx_2d.cpp @@ -568,7 +568,6 @@ void Graphics2D::drawThickLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, 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, diff --git a/src/hal/buttons.cpp b/src/hal/buttons.cpp index b53577dd3..7deac4d38 100644 --- a/src/hal/buttons.cpp +++ b/src/hal/buttons.cpp @@ -12,6 +12,20 @@ static int16_t buttonPositionsY[BTN_NUMBER] = BTN_POSY_ARRAY; static bool buttonIsTop[BTN_NUMBER] = BTN_POS_ISTOP_ARRAY; static bool buttonIsLeft[BTN_NUMBER] = BTN_POS_ISLEFT_ARRAY; +#ifndef OSW_EMULATOR +void IRAM_ATTR ISR_BTN1() { + setCpuFrequencyMhz(OSW_PLATFORM_DEFAULT_CPUFREQ); +} + +void IRAM_ATTR ISR_BTN2() { + setCpuFrequencyMhz(OSW_PLATFORM_DEFAULT_CPUFREQ); +} + +void IRAM_ATTR ISR_BTN3() { + setCpuFrequencyMhz(OSW_PLATFORM_DEFAULT_CPUFREQ); +} +#endif + void OswHal::setupButtons(void) { // rtc_gpio_deinit(GPIO_NUM_0); // rtc_gpio_deinit(GPIO_NUM_10); @@ -19,6 +33,14 @@ void OswHal::setupButtons(void) { pinMode(BTN_1, INPUT); pinMode(BTN_2, INPUT); pinMode(BTN_3, INPUT); + +#ifndef OSW_EMULATOR + // raise speed to maximum if a button is pressed + attachInterrupt(BTN_1, ISR_BTN1, CHANGE); + attachInterrupt(BTN_2, ISR_BTN2, CHANGE); + attachInterrupt(BTN_3, ISR_BTN3, CHANGE); +#endif + #if defined(GPS_EDITION) || defined(GPS_EDITION_ROTATED) pinMode(VIBRATE, OUTPUT); diff --git a/src/hal/power.cpp b/src/hal/power.cpp index a616ff8ed..85e1d139c 100644 --- a/src/hal/power.cpp +++ b/src/hal/power.cpp @@ -213,7 +213,7 @@ void OswHal::doSleep(bool deepSleep) { // register user wakeup sources if (OswConfigAllKeys::buttonToWakeEnabled.get()) // 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! + esp_sleep_enable_ext0_wakeup((gpio_num_t)BTN_1, LOW); // special handling as low is the trigger, otherwise ↓ bitmask should be used! /** * Okay. Hear me out: In the very special case that you do not enable "button to wake" and only try to use the @@ -224,9 +224,9 @@ void OswHal::doSleep(bool deepSleep) { */ if (OswConfigAllKeys::raiseToWakeEnabled.get() || OswConfigAllKeys::tapToWakeEnabled.get()) { if (!OswConfigAllKeys::buttonToWakeEnabled.get()) { - esp_sleep_enable_ext0_wakeup(GPIO_NUM_34 /* BTN_1 */, HIGH); + esp_sleep_enable_ext0_wakeup((gpio_num_t)BMA400_INT, HIGH); } else { - esp_sleep_enable_ext1_wakeup(0x400000000 /* BTN_1 = GPIO_34 = 2^34 as bitmask */, ESP_EXT1_WAKEUP_ANY_HIGH); + esp_sleep_enable_ext1_wakeup(((uint64_t)1<setCPUClock(OSW_PLATFORM_DEFAULT_CPUFREQ); // to allow a faster wakeup from sleep + if (deepSleep) esp_deep_sleep_start(); else diff --git a/src/main.cpp b/src/main.cpp index 32155d6b7..a6d8f2683 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -96,8 +96,60 @@ using OswGlobals::main_tutorialApp; #define _MAIN_CRASH_SLEEP 2 #endif +#ifndef OSW_EMULATOR +#include "driver/uart.h" + +// a helper directly stolen from esp32-hal-uart.c +static inline uint32_t _get_effective_baudrate(uint32_t baudrate) +{ + uint32_t Freq = getApbFrequency()/1000000; + if (Freq < 80) { + return 80 / Freq * baudrate; + } + else { + return baudrate; + } +} + +// This is basically the standard Serial.begin, but with a REF_TICK as clock source +// So that the uart works at any(?) cpu frequency +void initSerial(uint32_t baud_rate = 115200) { + uart_port_t uart_num = 0; + uint8_t rx_flow_ctrl_thresh = 112; + + Serial.begin(baud_rate, SERIAL_8N1, -1, -1, false, 20000UL, rx_flow_ctrl_thresh); + + uart_word_length_t data_bits; + uart_parity_t parity; + uart_stop_bits_t stop_bits; + uart_hw_flowcontrol_t flow_ctrl; + + uart_get_word_length(uart_num, &data_bits); + uart_get_parity(uart_num, &parity); + uart_get_stop_bits(uart_num, &stop_bits); + uart_get_hw_flow_ctrl(uart_num, &flow_ctrl); + + uart_config_t uart_config; + uart_config.source_clk = UART_SCLK_REF_TICK; // ESP32, ESP32S2 + + uart_config.data_bits = data_bits; + uart_config.parity = parity; + uart_config.stop_bits = stop_bits; + uart_config.flow_ctrl = flow_ctrl; + uart_config.rx_flow_ctrl_thresh = rx_flow_ctrl_thresh; + uart_config.baud_rate = _get_effective_baudrate(baud_rate); + + ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config)); +} +#else +void initSerial(uint32_t baud_rate = 115200) { + Serial.begin(baud_rate); +} +#endif + void setup() { - Serial.begin(115200); + initSerial(115200); + OSW_LOG_I("Welcome to the OSW-OS! This build is based on commit ", GIT_COMMIT_HASH, " from ", GIT_BRANCH_NAME, ". Compiled at ", __DATE__, " ", __TIME__, " for platform ", PIO_ENV_NAME, "."); @@ -131,9 +183,11 @@ void setup() { // Install drawer and (maybe) jump into tutorial OswUI::getInstance()->setRootApplication(&main_mainDrawer); +/* main_tutorialApp.reset(new OswAppTutorial()); if(!main_tutorialApp->changeRootAppIfNecessary()) main_tutorialApp.reset(); // no need to keep it around, as it's not the root app +*/ #if USE_ULP == 1 // register the ULP program @@ -181,20 +235,25 @@ void loop() { // Now update the screen (this will maybe sleep for a while) try { + // to use dynamic frequencies you have to change + // uart_config.source_clk from UART_SCLK_APB; to UART_SCLK_REF_TICK; in esp32-hal-uart.c in uartBegin(....) + + setCpuFrequencyMhz(OSW_PLATFORM_DEFAULT_CPUFREQ); OswUI::getInstance()->loop(); + setCpuFrequencyMhz(10); } catch(const std::exception& e) { OSW_LOG_E("CRITICAL ERROR AT APP: ", e.what()); sleep(_MAIN_CRASH_SLEEP); ESP.restart(); } - if (delayedAppInit) { - // fix flickering display on latest Arduino_GFX library - ledcWrite(1, OswConfigAllKeys::settingDisplayBrightness.get()); - } if (delayedAppInit) { delayedAppInit = false; + // fix flickering display on latest Arduino_GFX library + // TODO: check this again + ledcWrite(1, OswConfigAllKeys::settingDisplayBrightness.get()); + // TODO port all v1 apps to v2, to allow for lazy loading (or let them in compat mode) // GPS From 018e2e463a4ff0b5d41f66c0a5ce7940c6ccf737 Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Fri, 12 Apr 2024 14:49:29 +0200 Subject: [PATCH 03/12] dynfreq and immediate respond to key press --- include/OswAppV2.h | 1 + include/hal/buttons.h | 35 ++++++++++++++++++++++++------ include/osw_hal.h | 2 +- src/OswAppV2.cpp | 4 ++++ src/apps/OswAppDrawer.cpp | 11 ++++++---- src/apps/tools/OswAppWebserver.cpp | 8 +++---- src/hal/buttons.cpp | 8 +++++-- src/main.cpp | 25 +++++++++++++++------ 8 files changed, 69 insertions(+), 25 deletions(-) diff --git a/include/OswAppV2.h b/include/OswAppV2.h index 9323676e2..e4bbc31e7 100644 --- a/include/OswAppV2.h +++ b/include/OswAppV2.h @@ -43,6 +43,7 @@ class OswAppV2 { virtual const ViewFlags& getViewFlags(); virtual bool getNeedsRedraw(); + virtual void setNeedsRedraw(); virtual void resetNeedsRedraw(); protected: class OswHalProxy { diff --git a/include/hal/buttons.h b/include/hal/buttons.h index ca3691bf7..efb948265 100644 --- a/include/hal/buttons.h +++ b/include/hal/buttons.h @@ -1,14 +1,35 @@ -#pragma once +#pragma once -enum Button { +#include "osw_pins.h" + +/* button order is: select, down, up + +. _.---._ +. /,-"0"-.\ up +. // | \\ +. ||9 o 3|D +. \\ `. // +. select \`-.6.-'/ down +. `=---=' + +*/ +#define BTN_NUMBER 3 +#define BTN_POSX_ARRAY {24, 208, 208} +#define BTN_POSY_ARRAY {190, 190, 44} +#define BTN_POS_ISTOP_ARRAY {false, false, true} +#define BTN_POS_ISLEFT_ARRAY {true, false, false} +#define BTN_NAME_ARRAY {"SELECT", "DOWN", "UP"} +#define BTN_PIN_ARRAY {BTN_1, BTN_2, BTN_3} + +typedef enum { // Every button must have an id in the range of 0 to BTN_NUMBER-1 BUTTON_SELECT = 0, - BUTTON_UP = 1, - BUTTON_DOWN = 2, + BUTTON_DOWN = 1, + BUTTON_UP = 2, // Historical reasons (will be removed in the future!) ↓ BUTTON_1 = 0, - BUTTON_2 = 2, - BUTTON_3 = 1 -}; + BUTTON_2 = 1, + BUTTON_3 = 2, +} Button; extern const char* ButtonNames[]; diff --git a/include/osw_hal.h b/include/osw_hal.h index a17b34d8a..d36c3e1d7 100644 --- a/include/osw_hal.h +++ b/include/osw_hal.h @@ -76,7 +76,7 @@ class OswHal { void stopPower(); // Buttons (Engine-Style) - void checkButtons(); + bool checkButtons(); bool btnIsDown(Button btn); unsigned long btnIsDownFor(Button btn); unsigned long btnIsDownSince(Button btn); diff --git a/src/OswAppV2.cpp b/src/OswAppV2.cpp index 096135cf8..5bae339d3 100644 --- a/src/OswAppV2.cpp +++ b/src/OswAppV2.cpp @@ -28,6 +28,10 @@ bool OswAppV2::getNeedsRedraw() { return this->needsRedraw; } +void OswAppV2::setNeedsRedraw() { + this->needsRedraw = true; +} + void OswAppV2::resetNeedsRedraw() { this->needsRedraw = false; } diff --git a/src/apps/OswAppDrawer.cpp b/src/apps/OswAppDrawer.cpp index f897f9154..0e7e96b79 100644 --- a/src/apps/OswAppDrawer.cpp +++ b/src/apps/OswAppDrawer.cpp @@ -176,13 +176,16 @@ void OswAppDrawer::onDraw() { // draw the button labels if(!this->minimizeButtonLabels) { + this->hal->gfx()->setTextColor(rgb565(0,150,150)); OswUI::getInstance()->setTextCursor(Button::BUTTON_UP); - this->hal->gfx()->print(LANG_CATEGORY); + this->hal->gfx()->print(LANG_APP); OswUI::getInstance()->setTextCursor(Button::BUTTON_DOWN); - this->hal->gfx()->print(LANG_APP); + this->hal->gfx()->print(LANG_CATEGORY); OswUI::getInstance()->setTextCursor(Button::BUTTON_SELECT); + this->hal->gfx()->setTextColor(OswUI::getInstance()->getForegroundColor()); + } else { OswUI::getInstance()->setTextCursor(Button::BUTTON_SELECT); this->hal->gfx()->setTextSize(1); @@ -208,10 +211,10 @@ void OswAppDrawer::onButton(Button id, bool up, ButtonStateNames state) { OswAppV2::onButton(id, up, state); if(!this->current) { if(up and state == OswAppV2::ButtonStateNames::SHORT_PRESS) { - if(id == Button::BUTTON_UP) { + if(id == Button::BUTTON_DOWN) { this->highlightCategoryIndex = (this->highlightCategoryIndex + 1) % this->apps.size(); this->needsRedraw = true; - } else if(id == Button::BUTTON_DOWN) { + } else if(id == Button::BUTTON_UP) { ++this->highlightAppIndex; this->needsRedraw = true; } else if(id == Button::BUTTON_SELECT) { diff --git a/src/apps/tools/OswAppWebserver.cpp b/src/apps/tools/OswAppWebserver.cpp index 530d3d4d5..6fbd8930a 100644 --- a/src/apps/tools/OswAppWebserver.cpp +++ b/src/apps/tools/OswAppWebserver.cpp @@ -19,7 +19,7 @@ void OswAppWebserver::loop() { hal->gfx()->setTextSize(2); // Configuration - OswUI::getInstance()->setTextCursor(BUTTON_3); + OswUI::getInstance()->setTextCursor(BUTTON_UP); if (OswServiceAllTasks::wifi.isConnected()) { hal->gfx()->print(LANG_DISCONNECT); } else { @@ -27,7 +27,7 @@ void OswAppWebserver::loop() { hal->gfx()->print("..."); else { hal->gfx()->print(LANG_CONNECT); - OswUI::getInstance()->setTextCursor(BUTTON_2); + OswUI::getInstance()->setTextCursor(BUTTON_DOWN); if(OswConfigAllKeys::hostPasswordEnabled.get()) { hal->gfx()->print(LANG_WEBSRV_AP_PASSWORD_ON); } else { @@ -36,7 +36,7 @@ void OswAppWebserver::loop() { } } - if (hal->btnHasGoneDown(BUTTON_3)) { + if (hal->btnHasGoneDown(BUTTON_UP)) { if (OswServiceAllTasks::wifi.isConnected()) { OswServiceAllTasks::wifi.disconnectWiFi(); OswServiceAllTasks::wifi.disableWiFi(); @@ -45,7 +45,7 @@ void OswAppWebserver::loop() { OswServiceAllTasks::wifi.connectWiFi(); } } - if (hal->btnHasGoneDown(BUTTON_2)) { + if (hal->btnHasGoneDown(BUTTON_DOWN)) { if (!OswServiceAllTasks::wifi.isConnected()) { OswServiceAllTasks::wifi.toggleAPPassword(); } diff --git a/src/hal/buttons.cpp b/src/hal/buttons.cpp index 7deac4d38..be2ac978a 100644 --- a/src/hal/buttons.cpp +++ b/src/hal/buttons.cpp @@ -66,12 +66,15 @@ void OswHal::vibrate(long millis) { } #endif -void OswHal::checkButtons(void) { +bool OswHal::checkButtons(void) { // Buttons (Engine) + bool hasUserInteraction = false; for (uint8_t i = 0; i < BTN_NUMBER; i++) { _btnIsDown[i] = digitalRead(buttonPins[i]) == buttonClickStates[i]; - if(_btnIsDown[i]) + if(_btnIsDown[i]) { this->noteUserInteraction(); // Button pressing counts as user interaction + hasUserInteraction = true; + } } for (uint8_t i = 0; i < BTN_NUMBER; i++) { @@ -112,6 +115,7 @@ void OswHal::checkButtons(void) { _btnSuppressUntilUpAgain[i] = false; } } + return hasUserInteraction; } // Buttons (Engine) diff --git a/src/main.cpp b/src/main.cpp index a6d8f2683..5daec8e43 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -102,9 +102,9 @@ using OswGlobals::main_tutorialApp; // a helper directly stolen from esp32-hal-uart.c static inline uint32_t _get_effective_baudrate(uint32_t baudrate) { - uint32_t Freq = getApbFrequency()/1000000; - if (Freq < 80) { - return 80 / Freq * baudrate; + uint32_t Freq = getApbFrequency(); + if (Freq < 80000000) { + return 80000000.0 / Freq * baudrate; } else { return baudrate; @@ -202,6 +202,8 @@ void loop() { static time_t nextTimezoneUpdate = time(nullptr) + 60; // Already done after sleep -> revisit in a while static bool delayedAppInit = true; + bool wifiDisabled = true; + // check possible interaction with ULP program #if USE_ULP == 1 loop_ulp(); @@ -210,14 +212,17 @@ void loop() { try { OswHal::getInstance()->handleDisplayTimout(); OswHal::getInstance()->handleWakeupFromLightSleep(); - OswHal::getInstance()->checkButtons(); + if (OswHal::getInstance()->checkButtons()) { + // Update screen as soon as possible + OswUI::getInstance()->getRootApplication()->setNeedsRedraw(); + } OswHal::getInstance()->devices()->update(); // update power statistics only when WiFi isn't used - fixing: // https://github.com/Open-Smartwatch/open-smartwatch-os/issues/163 - bool wifiDisabled = true; #ifdef OSW_FEATURE_WIFI wifiDisabled = !OswServiceAllTasks::wifi.isEnabled(); #endif + if (time(nullptr) > lastPowerUpdate and wifiDisabled) { // Only update those every second OswHal::getInstance()->updatePowerStatistics(OswHal::getInstance()->getBatteryRaw(20)); @@ -238,9 +243,15 @@ void loop() { // to use dynamic frequencies you have to change // uart_config.source_clk from UART_SCLK_APB; to UART_SCLK_REF_TICK; in esp32-hal-uart.c in uartBegin(....) - setCpuFrequencyMhz(OSW_PLATFORM_DEFAULT_CPUFREQ); + if (wifiDisabled) + setCpuFrequencyMhz(OSW_PLATFORM_DEFAULT_CPUFREQ); + + // Call the onLoop() and onDraw() methods of the app if necessary OswUI::getInstance()->loop(); - setCpuFrequencyMhz(10); + + if (wifiDisabled) + setCpuFrequencyMhz(10); + } catch(const std::exception& e) { OSW_LOG_E("CRITICAL ERROR AT APP: ", e.what()); sleep(_MAIN_CRASH_SLEEP); From 91e48f81935c8d536bedead73ae9c2e79e748d6f Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:04:43 +0200 Subject: [PATCH 04/12] And make it Emulator-fit --- emulator/include/Serial.h | 4 +++- emulator/include/driver/uart.h | 9 +++++++++ src/hal/power.cpp | 3 +++ src/main.cpp | 3 ++- 4 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 emulator/include/driver/uart.h diff --git a/emulator/include/Serial.h b/emulator/include/Serial.h index d4c610140..20c88810c 100644 --- a/emulator/include/Serial.h +++ b/emulator/include/Serial.h @@ -44,6 +44,8 @@ class Serial_t { int read(); void println(); + + void flush() {} private: std::list inputBuffer; int bauds = 0; @@ -51,4 +53,4 @@ class Serial_t { bool addBufferNewline = true; }; -extern Serial_t Serial; \ No newline at end of file +extern Serial_t Serial; diff --git a/emulator/include/driver/uart.h b/emulator/include/driver/uart.h new file mode 100644 index 000000000..950c74f3a --- /dev/null +++ b/emulator/include/driver/uart.h @@ -0,0 +1,9 @@ +typedef struct { + int baud_rate; /*!< UART baud rate*/ + unsigned int data_bits; /*!< UART byte size*/ + unsigned int parity; /*!< UART parity mode*/ + unsigned int stop_bits; /*!< UART stop bits*/ + unsigned int flow_ctrl; /*!< UART HW flow control mode (cts/rts)*/ + unsigned int rx_flow_ctrl_thresh; /*!< UART HW RTS threshold*/ + unsigned int source_clk; /*!< UART source clock selection */ +} uart_config_t; diff --git a/src/hal/power.cpp b/src/hal/power.cpp index 85e1d139c..3aaacd109 100644 --- a/src/hal/power.cpp +++ b/src/hal/power.cpp @@ -71,6 +71,9 @@ static inline uint8_t linear(uint16_t voltage, uint16_t minVoltage, uint16_t max return (unsigned long)(voltage - minVoltage) * 100 / volt_diff; } +#ifdef OSW_EMULATOR +#define gpio_num_t int +#endif uint16_t OswHal::getBatteryRawMin() { return this->powerPreferences.getUShort("-", 60); // Every battery should be able to deliver lower than this at some point diff --git a/src/main.cpp b/src/main.cpp index 5daec8e43..1db985869 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -99,6 +99,7 @@ using OswGlobals::main_tutorialApp; #ifndef OSW_EMULATOR #include "driver/uart.h" +#ifndef OSW_EMULATOR // a helper directly stolen from esp32-hal-uart.c static inline uint32_t _get_effective_baudrate(uint32_t baudrate) { @@ -143,7 +144,7 @@ void initSerial(uint32_t baud_rate = 115200) { } #else void initSerial(uint32_t baud_rate = 115200) { - Serial.begin(baud_rate); + Serial.begin(115200); } #endif From a92ef5d79ad977fd54d483593bb9bf42a4726835 Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Thu, 11 Apr 2024 19:15:37 +0200 Subject: [PATCH 05/12] Background behind indices in FitnessAnalog --- .../OswAppWatchfaceFitnessAnalog.cpp | 76 ++++++++++++++----- src/main.cpp | 1 - 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp index 219dea889..f4ca4e511 100644 --- a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp @@ -39,7 +39,6 @@ void OswAppWatchfaceFitnessAnalog::showFitnessTracking(OswHal* hal) { uint32_t distTarget = OswConfigAllKeys::distPerDay.get(); uint8_t arcRadius = 6; - uint16_t yellow = rgb565(255, 255,0); #ifdef OSW_EMULATOR steps = 4000; @@ -49,7 +48,7 @@ void OswAppWatchfaceFitnessAnalog::showFitnessTracking(OswHal* hal) { { // draw step arc int32_t angle_val = 180.0f * (float)min(steps, stepsTarget) / (float)stepsTarget; - uint16_t color = yellow; + uint16_t color = ui->getWarningColor(); 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); @@ -79,7 +78,7 @@ void OswAppWatchfaceFitnessAnalog::showFitnessTracking(OswHal* hal) { hal->gfx()->setTextSize(1); hal->gfx()->setTextLeftAligned(); - hal->gfx()->setTextColor(dimColor(yellow, 25)); + hal->gfx()->setTextColor(dimColor(ui->getWarningColor(), 25)); hal->gfx()->setTextCursor(CENTER_X + 12, 8+23); hal->gfx()->print(steps); hal->gfx()->setTextCursor(CENTER_X + 12, DISP_H-23); @@ -95,7 +94,7 @@ void OswAppWatchfaceFitnessAnalog::showFitnessTracking(OswHal* hal) { 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); + hal->gfx()->drawHourTicks(CENTER_X, CENTER_Y, 117, 106, 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); @@ -155,11 +154,6 @@ void OswAppWatchfaceFitnessAnalog::drawDateFace(OswHal* hal, uint32_t hour, uint } #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); @@ -168,6 +162,34 @@ void OswAppWatchfaceFitnessAnalog::drawDateFace(OswHal* hal, uint32_t hour, uint #endif } +void OswAppWatchfaceFitnessAnalog::drawFitnessFace(OswHal *hal, uint32_t hour, uint32_t minute, uint32_t second, bool afterNoon) { + + uint32_t steps = hal->environment()->getStepsToday(); + uint32_t dists = OswAppWatchfaceFitnessAnalog::calculateDistance(steps); + +#ifdef OSW_EMULATOR + steps = 10213; + dists = 23444; +#endif + + // Steps + hal->gfx()->setTextSize(3); + hal->gfx()->setTextColor(ui->getWarningColor()); + hal->gfx()->setTextLeftAligned(); + hal->gfx()->setTextCursor(CENTER_X - 80, CENTER_Y - 10); + hal->gfx()->print(LANG_FITNESS_STEP); + hal->gfx()->print(": "); + hal->gfx()->print(steps); + + hal->gfx()->setTextSize(2); + hal->gfx()->setTextColor(ui->getInfoColor()); + hal->gfx()->setTextLeftAligned(); + hal->gfx()->setTextCursor(CENTER_X - 80, CENTER_Y + 20); + hal->gfx()->print(LANG_FITNESS_DISTANCE); + hal->gfx()->print(": "); + hal->gfx()->print(dists); +} + const char* OswAppWatchfaceFitnessAnalog::getAppId() { return OswAppWatchfaceFitnessAnalog::APP_ID; } @@ -196,6 +218,7 @@ void OswAppWatchfaceFitnessAnalog::onStart() { void OswAppWatchfaceFitnessAnalog::onLoop() { OswAppV2::onLoop(); +printf("xxx onLoop %d\n", (int)millis()); this->needsRedraw = this->needsRedraw or time(nullptr) != this->lastTime; // redraw every second } @@ -208,6 +231,9 @@ void OswAppWatchfaceFitnessAnalog::onDraw() { OswAppV2::onDraw(); +printf("xxx onDraw %d\n", (int)millis()); + + #ifdef GIF_BG if(this->bgGif != nullptr) this->bgGif->loop(); @@ -221,22 +247,35 @@ void OswAppWatchfaceFitnessAnalog::onDraw() { bool afterNoon; hal->getLocalTime(&hour, &minute, &second, &afterNoon); - if (this->screen == 0) { + if (screen == 0) { #if OSW_PLATFORM_ENVIRONMENT_ACCELEROMETER == 1 showFitnessTracking(hal); #endif drawWatchFace(hal, hour, minute, second, afterNoon); - } else if (this->screen == 1) { + } else if (screen == 1) { drawDateFace(hal, hour, minute, second, afterNoon); - static int wait_time = 1; + static int wait_time = 5; + if (wait_time >= 0) + --wait_time; + else { + screen = 0; + wait_time = 5; + } + } else if (screen == 2) { + drawFitnessFace(hal, hour, minute, second, afterNoon); + + static int wait_time = 5; if (wait_time >= 0) --wait_time; else { - this->screen = 0; - wait_time = 1; + screen = 0; + wait_time = 5; } + } else if (screen == 3) { + screen = 0; + onDraw(); } this->lastTime = time(nullptr); @@ -246,6 +285,7 @@ void OswAppWatchfaceFitnessAnalog::onDraw() { #else unsigned long ms_for_onDraw = millis()-old_millis; #endif + OSW_LOG_I("Time to draw ", ms_for_onDraw, " ms"); } void OswAppWatchfaceFitnessAnalog::onButton(Button id, bool up, OswAppV2::ButtonStateNames state) { @@ -253,12 +293,14 @@ void OswAppWatchfaceFitnessAnalog::onButton(Button id, bool up, OswAppV2::Button if(!up) return; - if (state == OswAppV2::ButtonStateNames::DOUBLE_PRESS) { - if (this->screen < 1) - ++this->screen; + // Swallow short presses + if (state == OswAppV2::ButtonStateNames::SHORT_PRESS) { + if (screen < 3) + ++screen; return; } + // Do only the long press if(OswAppWatchface::onButtonDefaults(*this, id, up, state)) return; // if the button was handled by the defaults, we are done here } diff --git a/src/main.cpp b/src/main.cpp index 1db985869..c9761e063 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -99,7 +99,6 @@ using OswGlobals::main_tutorialApp; #ifndef OSW_EMULATOR #include "driver/uart.h" -#ifndef OSW_EMULATOR // a helper directly stolen from esp32-hal-uart.c static inline uint32_t _get_effective_baudrate(uint32_t baudrate) { From 27cfb747dbbd0b4045f7c95b4b9bcba0592a82f1 Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Sat, 13 Apr 2024 20:55:15 +0200 Subject: [PATCH 06/12] mix --- CMakeLists.txt | 6 ++++ emulator/src/CPU.cpp | 6 ++-- src/apps/tools/OswAppTimeConfig.cpp | 34 +++++++++--------- .../OswAppWatchfaceFitnessAnalog.cpp | 36 +++++++------------ src/hal/buttons.cpp | 20 +++++++++-- 5 files changed, 55 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ee53467ef..a7c297b5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,11 @@ project (OSW-OS-Emulator) set(CMAKE_CXX_STANDARD 20) +find_program(CCACHE "ccache") +if(CCACHE) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE}") +endif() + # Prepare some defines, which are normally evaluated using some Python snippets... execute_process(COMMAND "git" "rev-parse" "--short" "HEAD" OUTPUT_VARIABLE GIT_COMMIT_HASH) execute_process(COMMAND "git" "log" "-1" "--pretty=format:%cd" "--date=format:%Y-%m-%dT%H:%M:%S%z" OUTPUT_VARIABLE GIT_COMMIT_TIME) @@ -159,6 +164,7 @@ target_compile_options(emulator.run PUBLIC -Wall -Wdouble-promotion -fpermissive + -Wno-deprecated-declarations > $<$: -O4 diff --git a/emulator/src/CPU.cpp b/emulator/src/CPU.cpp index cd0834b3f..5e15f1847 100644 --- a/emulator/src/CPU.cpp +++ b/emulator/src/CPU.cpp @@ -3,11 +3,11 @@ void setCpuFrequencyMhz(unsigned int) { // I mean... How? And why?! - OSW_EMULATOR_THIS_IS_NOT_IMPLEMENTED; + //OSW_EMULATOR_THIS_IS_NOT_IMPLEMENTED; } unsigned int getCpuFrequencyMhz() { // I mean... How? And why?! - OSW_EMULATOR_THIS_IS_NOT_IMPLEMENTED; + //OSW_EMULATOR_THIS_IS_NOT_IMPLEMENTED; return 0; -} \ No newline at end of file +} diff --git a/src/apps/tools/OswAppTimeConfig.cpp b/src/apps/tools/OswAppTimeConfig.cpp index f5be04736..26b449c87 100644 --- a/src/apps/tools/OswAppTimeConfig.cpp +++ b/src/apps/tools/OswAppTimeConfig.cpp @@ -38,11 +38,11 @@ void OswAppTimeConfig::enterManualMode() { } void OswAppTimeConfig::handleIncrementButton() { - OswUI::getInstance()->setTextCursor(BUTTON_3); + OswUI::getInstance()->setTextCursor(BUTTON_UP); OswHal* hal = OswHal::getInstance(); hal->gfx()->print("+"); if (manualSettingStep == 12) { // SAVE - if (hal->btnHasGoneDown(BUTTON_3)) { + if (hal->btnHasGoneDown(BUTTON_UP)) { // Date int16_t yy = 2020 + manualSettingTimestamp[0]; int8_t mm = manualSettingTimestamp[1] * 10 + manualSettingTimestamp[2] - 1; // January = 0 @@ -57,11 +57,11 @@ void OswAppTimeConfig::handleIncrementButton() { manualSettingScreen = false; } } else if (manualSettingStep == 11) { // CANCEL - if (hal->btnHasGoneDown(BUTTON_3)) { + if (hal->btnHasGoneDown(BUTTON_UP)) { manualSettingScreen = false; } } else { // +1 - if (hal->btnHasGoneDown(BUTTON_3)) { + if (hal->btnHasGoneDown(BUTTON_UP)) { manualSettingTimestamp[manualSettingStep]++; if (manualSettingStep == 1) { // MONTHTEN @@ -91,17 +91,17 @@ void OswAppTimeConfig::handleIncrementButton() { void OswAppTimeConfig::handleDecrementButton() { OswHal* hal = OswHal::getInstance(); - OswUI::getInstance()->setTextCursor(BUTTON_2); + OswUI::getInstance()->setTextCursor(BUTTON_DOWN); hal->gfx()->print("-"); // decrement should not saved - code has been removed versus incrementButton if (manualSettingStep == 11) { // CANCEL - if (hal->btnHasGoneDown(BUTTON_2)) { + if (hal->btnHasGoneDown(BUTTON_DOWN)) { manualSettingScreen = false; } } else { // -1 - if (hal->btnHasGoneDown(BUTTON_2)) { + if (hal->btnHasGoneDown(BUTTON_DOWN)) { manualSettingTimestamp[manualSettingStep]--; if (manualSettingStep == 1) { // MONTHTEN @@ -131,9 +131,9 @@ void OswAppTimeConfig::handleDecrementButton() { void OswAppTimeConfig::handleNextButton() { OswHal* hal = OswHal::getInstance(); - OswUI::getInstance()->setTextCursor(BUTTON_1); + OswUI::getInstance()->setTextCursor(BUTTON_SELECT); hal->gfx()->print(">"); - if (hal->btnHasGoneDown(BUTTON_1)) { + if (hal->btnHasGoneDown(BUTTON_SELECT)) { manualSettingStep++; manualSettingStep = manualSettingStep > 12 ? 0 : manualSettingStep; } @@ -147,7 +147,7 @@ void OswAppTimeConfig::loop() { hal->gfx()->setTextSize(2); if (!manualSettingScreen) { - OswUI::getInstance()->setTextCursor(BUTTON_3); + OswUI::getInstance()->setTextCursor(BUTTON_UP); #ifdef OSW_FEATURE_WIFI if (OswServiceAllTasks::wifi.isConnected()) { hal->gfx()->print(LANG_DISCONNECT); @@ -156,12 +156,12 @@ void OswAppTimeConfig::loop() { hal->gfx()->print("..."); else { hal->gfx()->print(LANG_CONNECT); - OswUI::getInstance()->setTextCursor(BUTTON_2); + OswUI::getInstance()->setTextCursor(BUTTON_DOWN); hal->gfx()->print(LANG_MANUALLY); } } - if (hal->btnHasGoneDown(BUTTON_3)) { + if (hal->btnHasGoneDown(BUTTON_UP)) { if (OswServiceAllTasks::wifi.isConnected()) { OswServiceAllTasks::wifi.disconnectWiFi(); OswServiceAllTasks::wifi.disableWiFi(); @@ -172,22 +172,22 @@ void OswAppTimeConfig::loop() { } if (OswServiceAllTasks::wifi.isConnected()) { - OswUI::getInstance()->setTextCursor(BUTTON_2); + OswUI::getInstance()->setTextCursor(BUTTON_DOWN); hal->gfx()->print(LANG_TFW_UPDATE); - if (hal->btnHasGoneDown(BUTTON_2)) { + if (hal->btnHasGoneDown(BUTTON_DOWN)) { if (OswServiceAllTasks::wifi.isConnected()) { OswServiceAllTasks::wifi.queueTimeUpdateViaNTP(); } } } else { - if (hal->btnHasGoneDown(BUTTON_2)) { + if (hal->btnHasGoneDown(BUTTON_DOWN)) { enterManualMode(); } } #else - OswUI::getInstance()->setTextCursor(BUTTON_2); + OswUI::getInstance()->setTextCursor(BUTTON_DOWN); hal->gfx()->print(LANG_MANUALLY); - if (hal->btnHasGoneDown(BUTTON_2)) { + if (hal->btnHasGoneDown(BUTTON_DOWN)) { enterManualMode(); } #endif diff --git a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp index f4ca4e511..7da24d145 100644 --- a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp @@ -218,7 +218,6 @@ void OswAppWatchfaceFitnessAnalog::onStart() { void OswAppWatchfaceFitnessAnalog::onLoop() { OswAppV2::onLoop(); -printf("xxx onLoop %d\n", (int)millis()); this->needsRedraw = this->needsRedraw or time(nullptr) != this->lastTime; // redraw every second } @@ -231,9 +230,6 @@ void OswAppWatchfaceFitnessAnalog::onDraw() { OswAppV2::onDraw(); -printf("xxx onDraw %d\n", (int)millis()); - - #ifdef GIF_BG if(this->bgGif != nullptr) this->bgGif->loop(); @@ -247,6 +243,10 @@ printf("xxx onDraw %d\n", (int)millis()); bool afterNoon; hal->getLocalTime(&hour, &minute, &second, &afterNoon); + if (screen > 0 && millis() - lastShortPressTime > 5000) { + screen = 0; + } + if (screen == 0) { #if OSW_PLATFORM_ENVIRONMENT_ACCELEROMETER == 1 showFitnessTracking(hal); @@ -256,28 +256,14 @@ printf("xxx onDraw %d\n", (int)millis()); } else if (screen == 1) { drawDateFace(hal, hour, minute, second, afterNoon); - static int wait_time = 5; - if (wait_time >= 0) - --wait_time; - else { - screen = 0; - wait_time = 5; - } } else if (screen == 2) { drawFitnessFace(hal, hour, minute, second, afterNoon); - - static int wait_time = 5; - if (wait_time >= 0) - --wait_time; - else { - screen = 0; - wait_time = 5; - } - } else if (screen == 3) { + } else { screen = 0; - onDraw(); } + + this->lastTime = time(nullptr); #ifndef OSW_EMULATOR @@ -285,7 +271,7 @@ printf("xxx onDraw %d\n", (int)millis()); #else unsigned long ms_for_onDraw = millis()-old_millis; #endif - OSW_LOG_I("Time to draw ", ms_for_onDraw, " ms"); +// OSW_LOG_I("Time to draw ", ms_for_onDraw, " ms"); } void OswAppWatchfaceFitnessAnalog::onButton(Button id, bool up, OswAppV2::ButtonStateNames state) { @@ -293,10 +279,12 @@ void OswAppWatchfaceFitnessAnalog::onButton(Button id, bool up, OswAppV2::Button if(!up) return; - // Swallow short presses + // Process short presses if (state == OswAppV2::ButtonStateNames::SHORT_PRESS) { - if (screen < 3) + if (screen < 3) { ++screen; + lastShortPressTime = millis(); + } return; } diff --git a/src/hal/buttons.cpp b/src/hal/buttons.cpp index be2ac978a..dce66ad52 100644 --- a/src/hal/buttons.cpp +++ b/src/hal/buttons.cpp @@ -66,14 +66,20 @@ void OswHal::vibrate(long millis) { } #endif +/* +* Check if a user interaction has/is taking place. +* Store the result, so that the members can return the button states. +* additionally return true if a button press is first detected. +*/ bool OswHal::checkButtons(void) { // Buttons (Engine) - bool hasUserInteraction = false; + bool currentButtonDown = false; + static bool lastButtonDown = false; for (uint8_t i = 0; i < BTN_NUMBER; i++) { _btnIsDown[i] = digitalRead(buttonPins[i]) == buttonClickStates[i]; if(_btnIsDown[i]) { this->noteUserInteraction(); // Button pressing counts as user interaction - hasUserInteraction = true; + currentButtonDown = true; } } @@ -115,7 +121,15 @@ bool OswHal::checkButtons(void) { _btnSuppressUntilUpAgain[i] = false; } } - return hasUserInteraction; + if (!currentButtonDown) { + lastButtonDown = false; + return false; + } + else if (currentButtonDown && !lastButtonDown) { + lastButtonDown = true; + return true; + } + return false; } // Buttons (Engine) From 68c4bffec3029302921f274ca8a3d03f392d928e Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Mon, 15 Apr 2024 18:33:29 +0200 Subject: [PATCH 07/12] play with lua One step to lua Make Twilight watchface with LUA support add drawArcAA and migrate OswAppWatchface Make LUA work with emulator --- CMakeLists.txt | 11 + data/lua/apps/twilight.lua | Bin 0 -> 342 bytes data/lua/libs/suntime.lua | Bin 0 -> 8135 bytes include/apps/main/luaapp.h | 18 +- include/apps/watchfaces/OswAppWatchfaceMix.h | 2 +- .../apps/watchfaces/OswAppWatchfaceZwilight.h | 76 +++ include/gfx_2d.h | 4 + include/locales/de-DE.h | 3 + include/locales/en-US.h | 4 + include/math_angles.h | 31 +- lib/LUA | 2 +- platformio.ini | 7 +- scripts/build/compile_lua_libs.py | 5 + scripts/build/prebuild_lua.py | 17 +- scripts/build/use_ccache.py | 15 + src.lua/libs/suntime.lua | 607 ++++++++++++++++++ src/apps/watchfaces/OswAppWatchface.cpp | 8 +- .../watchfaces/OswAppWatchfaceZwilight.cpp | 358 +++++++++++ src/devices/virtual.cpp | 24 +- src/gfx_2d.cpp | 79 ++- src/hal/power.cpp | 2 +- src/hal/time.cpp | 1 + src/main.cpp | 4 +- src/osw_config_keys.cpp | 4 +- src/osw_lua.cpp | 42 +- 25 files changed, 1269 insertions(+), 55 deletions(-) create mode 100644 data/lua/apps/twilight.lua create mode 100644 data/lua/libs/suntime.lua create mode 100644 include/apps/watchfaces/OswAppWatchfaceZwilight.h create mode 100644 scripts/build/compile_lua_libs.py mode change 100755 => 100644 scripts/build/prebuild_lua.py create mode 100644 scripts/build/use_ccache.py create mode 100644 src.lua/libs/suntime.lua create mode 100644 src/apps/watchfaces/OswAppWatchfaceZwilight.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a7c297b5c..d7b375118 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,15 @@ file(GLOB_RECURSE SOURCES_Pngle ./lib/pngle/*.c) add_library(Pngle ${SOURCES_Pngle}) target_include_directories(Pngle PUBLIC ./lib/pngle/src/) +# LUA +file(GLOB_RECURSE SOURCES_LUA ./lib/LUA/*.c) +## remove onelua.c luac.c form build list +list(FILTER SOURCES_LUA EXCLUDE REGEX "onelua.c") +list(FILTER SOURCES_LUA EXCLUDE REGEX "luac.c") +#MESSAGE(STATUS "${SOURCES_LUA}") +add_library(LUA ${SOURCES_LUA}) +target_include_directories(LUA PUBLIC ./lib/LUA/) + # ArduinoJSON add_subdirectory(emulator/lib/ArduinoJson) @@ -114,11 +123,13 @@ target_include_directories(emulator.run PUBLIC ./include ./lib/lib-open-smartwatch emulator/lib/Jzon/ + ./lib/LUA/ ${SDL2_INCLUDE_DIRS} ${SDL2IMAGE_INCLUDE_DIRS} ) target_link_libraries(emulator.run LINK_PUBLIC Pngle + LUA ArduinoJson Threads::Threads Jzon diff --git a/data/lua/apps/twilight.lua b/data/lua/apps/twilight.lua new file mode 100644 index 0000000000000000000000000000000000000000..1e689d6caf10fc1eea9483e52d40bf998914eeef GIT binary patch literal 342 zcmYLF+e*Vg5IwV*Y(j(72Vc~m5e)hwRI&IT+C>6QirEd+w{2=`ZHpq+kMRrivz*|i z3(J`^mz{I=_@d7GINa`ajylXdxdCwUaZ28MkXl^{O#qsKkV2`h@SfZ*0`z=~j583; zd3jxz{k$-|tjt}VR}$E|bZus3jl0aMtg!raYh2r9rvx;9_8MdVEn(o`A*LWC_)uQg zf?5GlfX4_(Ii}y9APK?i5MiRBy^f@&|7uaniq4s^+=Yr<1D z=}5J@T`5=9PH1v2wWhU^YE>=U&Zj6b%p7* z_=@XKVWOsg!P&6xy=hant$PoXEjZV#LtEOk(R9wQ%w&9;LHSJ9r&*M*(RW{_$*|`-FAl!|vhX zi=S~ne$;u?{o$W{xX=Cex4yN@#m?yw)BR)RBcmgu!$SjNYNY>Y8Rg`_(3ge|iP=*B zk@8df4;<<*mrEn=kKg$1yT$4LFO`n;A1v)Jmxd3cRz7;9#8XDP$@0)}X-qtGuvGr7 zgNGg)8a{leB%Y0+J%eBB9~meOisNIYa&LdRBxX?g-00X)d1!P*oB$AaF!&|vsi^m5 zTxJLRpB{T`2pn(oJWtezMuy5?>_0LlX3*ga5TkeKzyWarzyqbBgHM&k>_Gpafuop5 z$+U`-E5HZKr6Wxg5065kWs1I`!I6;1vnx796E)l(9C@rXAWp_3e|2=E^vHn&m_-HV zcGACakrFmhhl1v@8@mYAZxco?5lZ|DO%a$ae*f|zCDxf)5X}-wZFHQYlm$N1XDH_Z z<+7JDR-{aL6`|{BFEVNczNObOAw4G5BHHIDS3yPT4%m7Z>JLyx?x&O-7ZZa<Z{6iqgtea5rWTC zM!P80os824%6TSe7o)n!DGhw8U*wzxlsO&nC0$&pId1Ww%5{4L%UMoDXH?g7oLOGQ z(nSIJt5TnZ-)V3yvx2DksJ_&1j&mJUG-xe5sM`8m)UH6Ax|~)xUe(cPOJCk*WrAwP zv}ISfLAD%ZkPR-S9r`_z>lKXEQ74uni>TWcC9@W3>9v5j80nODsv%OkL+W&wNUM3N z?Ji;Kd6`KyWJr!}PLh(7u)m|j z`%7O5t(}{Z-Nzq){2nn0+uq*?bA71?hWm?MV#*+H>jQA22k5Om5oUWI=<6|Xd!O6e zdol*@=o8h4#PQPMv7tkwBRmJz<5UuA5o#0akZ{R;TBk2*l6wn&nIZs?nxMoep+Jbf zh%O78h`$$`Z($?xv8~C%HN=N9NkMSXn^Lf-wD@{KWk95Tk2prU5jX&=fT$2&z~%?6 z1EQmLz&6Ipk28A#@gT0JB3=$RoAL$0{%{-h6-4$wBd~%9K(pbOaco6NT4kNHGLvn{ ztSm?;^WZpWAk&-U7U>4h%DCkhMB0vUGRA#!tRONmPBr1O_ReaK7+E42TEzX3@A=q} zYH+f(EoB%K9Hf{W9vy*~9*1#-YsLqmD*hbw!#A4RHTa8cPfR>DdK9jE*tqVo(g1ws z(AeNuxe5xpj`-n^euuuJi1YqMU_-&H1O8q_<^@LlI?7JUR2Nm+)aPO4jj&G18`w3W z@?h~1EhVpUMRrgX;_IA}TT!or?hVc(96Urs%R<^WmS_Qov^-Q|MG(IOJG&vY+8{Lb z1WnZiv7)ReY!?M`T|j^ z&MgA#P6gGJo)yG7BkXiAo|c;-~okqzQHVjjO+c-xtS^lnxx} zA1J&3`RgBFIbgcmPaVJ4{pC$B?{>504L#x+W6w)NK7$~>1RY0^z=~ow8B2x9B3FWW z!V1D4eUJVP0!$5ln!-$}2`p8S#LX44IO^cjpV0TO-eXm~PWt>K{W&A@8(D~fOp>lc zwDGVHO(a^wBExKBf_scRCK44{6sg*Q>tbBaB7H%oX#uvnka51i=-8`dTD^#Rz@(Gl zAHepxpHVWk6tQ4n{I31<5-$R-P);HV>A$j{G1AKZ{h}XRjc@rE{OQnw z;93bh1UX%SJ%jrqOYnc#URY=4((V)V>mRGf!VH2mCVdYg-GpxO; zme}C}Jnewmy2dQh!7R~WLN)O<@u|6A+N-9Q;96B))~CkQ5)N9$a!w522%!Z3ua94%gd;RKXLF2i|5**u_9Vp2#~W+k!ke zr-k`d=lD(pXs0Uo5eVtI5%}!P7{yK36Cwo_?Fe< z-Rw==yhIzzvcVzY)g;?7mt05H*t%`*zI$c(-ll~>Gntl zS%=ONd6GW1Wf7CtAhhPATm>OV+N4)9Hg82ai^+WoWLwI$h%g?0&f;EB@id<-l55`5rN`6~VxlON?>FwU+b~z{c?Lp;seMV;P2qy24MaAz7DtGGIR2%6~xoksa)x(he zRLm3oyMs!%>3^3ud6((GBdF}qd}<>(C$pI@l~LfaGpOtYk4;NF=3?9#mD?3e?UL_; zC!PG{SVa>5c9kL(Mf`W9AT!!&v29Lf>{N?6qt5Gmp`i=99r;+7%*!843X@uFYg6f* zCv#36{-;xAb6qN@mr@L}Ed!csvx=};MXp=jZtxpaw!>2$skW$eK-a#Su0VP+b)2IO zeO+OnYSOc)5K-gtjLeCSkXM@Y277#W#-w50$lK~t;mEB?UD>AZ-g%X4X{Z+65%pIX zbNA+`?kr**Uy~U#*1hK7T+xNJaKKtE27uEYj6$xi)bWp6wDwc8- zG5*zqhmw3@+T=IF5M)RBr3gnyGhT%dj!xG#Zs-3U~*{PQ$SB^;Xx z1aU^1$9Nlk4Q*{zA{DB0<-{kVT1%(T(KD)aXT&W<(>EIaREoQI>bQN|=w$zKA;j)cKpZB`~KHbG9dlyNi%GIl0`A zJR-}9g*Kf1&3%B<0oU5(59gyZ{L)=|0 zzgnG^32sStm>tHU7g4T~-N(^Un)?uw{hx@l%jocRwcDf6Xih9A`s3zwuzVVbG;FM= z0u{x|Jj-9j>E%0#(soynfFg$8i{&-~T`(|+C6uf1?KKmH(xFJ=Lok9w#TU2%frD6N zpu&=wvvasL1o4ex%obUu2lG|*J0VUtXq#z5IWPp`8d7V+8&Mv-Lxo@qRhw?V%#9ZD zqaS~bFEXNk)SOheLI#39y-1*q{nuf4i4Eg|ih8&hGPaJ3W_}Uv-IzB-ie!fjmC7f_ zU{V4b&B8{LbJ%PFOvuLk&jUt+>R{VjNN82l@j)fHxS#$@c8r8U3Z`H-87{E(xv0Ma zY@dzl67|#$c-)0ts1wKN8Jm|~K>zLx@y{|U8bnCYvj$`GZSdy>=5ELY-t33{7Z-fz(sitgs(y?FKg7CGwWb7|z;qD{JV&sl?uhR(4h@dCcZKVU@t zV0i%@zj*<1o})O=vAyv^nT}>tnoUZvP*_NA7R$UA6r-C)ZB87UqXlx#MLtczx3LCg z60|D8#r^aivrxA2U7_>G!dtp&G3};$>J2#e1uRNy6^|HLs9r48F0)X`blMr^4Z@}I zh25+8CFe(M4!fxy@q@u!#t-vs=4bq9)55Iu4_9B4WgpOVVQ|HTsWz{Qm=c$uDEiZI zSy~}p1)&IPtyG!cO9c`m!j^G5#<37*)VfT^n{Wx8mEhDeoovD-bUMMMmg(#!Ttert zg=?=aqmc({C&;HsVjHw(=3!wTOY$kuaAV__(=5rB0*$m0TrQ`H|?Xln`kM|9bc zFSar8Sp?_lZFCw3!EpSvp)+wM>;s-A2AMkO4sN4gN7A2W#`h&W7hqnfr$f2n`%E3Q zUk7c-a{-4C;KF&C>q697C=13f_$`HkpDx7mNAr#33geq`B?UiQKp%G&eDjp@xNhe$ z&$ur0h!=64=aK8hb&{u6QqSh8OzJF8RZ_R|R3~+vr*=|z@-&syTk_Q5<}u{7m^d;t zcEu+kT*;Qjv}xTRy?@2eVCHoy#>|AR@he&KS~hLkW88BgLDQ33^YhxXhOnva)NuT^ zwExM{!J(0np^<}EdU%=`F)vcfPgvmtRXyn&o;;G*w;FW`Z6$O~900^S;wLcBbD^if z%EeE?c-x7xb{}t1!0{xD-$~(sk#S@xwGhpvM;Rd`y`0o6L$nZVg+N%+hp7qG%} zxSGUmzXq=q0*Zs@t8_rp+E@knWIUSD$#c}KF!$$C*b8sb%TFIJ;q|cpNxT<^H{{{D zExab@J53&I?#ko0(CGD(e+X-LM+>zb*y+hs|4#gr68%Sb2o7J-7Ln)1(`B^^b}tK) z7B-D4k`qK0=mWS#)K|TTLm?bV0up=-gT#WGjJMZ(a5PCAG;zqpjj3olCQ*zhHGVzb Y2F%RrgLsBC4esk_pTBEaR0QY$1H;)tzyJUM literal 0 HcmV?d00001 diff --git a/include/apps/main/luaapp.h b/include/apps/main/luaapp.h index ed1f4d899..b8310633c 100644 --- a/include/apps/main/luaapp.h +++ b/include/apps/main/luaapp.h @@ -1,5 +1,3 @@ -#pragma once - #ifdef OSW_FEATURE_LUA #include #include @@ -10,10 +8,18 @@ #define LUA_LOOP_FUNC "loop" #define LUA_STOP_FUNC "stop" -#define LUA_APP_PATH FS_MOUNT_POINT "/lua/apps/" -#define lUA_APP_SEARCH_PATH FS_MOUNT_POINT LUA_APP_PATH "?.lua" -#define lUA_LIB_SEARCH_PATH FS_MOUNT_POINT "/lua/libs/?.lua" -#define LUA_PACKAGE_CMD "package.path = package.path .. ';" lUA_APP_SEARCH_PATH ";" lUA_LIB_SEARCH_PATH "'" +#ifdef OSW_EMULATOR +#define LUA_PATH "../data/lua/" +#else +#define LUA_PATH FS_MOUNT_POINT "/lua/" +#endif + +#define LUA_APP_PATH LUA_PATH "apps/" +#define LUA_LIB_PATH LUA_PATH "libs/" + +#define LUA_APP_SEARCH_PATH LUA_APP_PATH "?.lua" +#define LUA_LIB_SEARCH_PATH LUA_LIB_PATH "?.lua" +#define LUA_PACKAGE_CMD "package.path = package.path .. ';" LUA_APP_SEARCH_PATH ";" LUA_LIB_SEARCH_PATH "'" class OswLuaApp : public OswApp { public: diff --git a/include/apps/watchfaces/OswAppWatchfaceMix.h b/include/apps/watchfaces/OswAppWatchfaceMix.h index bc7c1023b..7275be91a 100644 --- a/include/apps/watchfaces/OswAppWatchfaceMix.h +++ b/include/apps/watchfaces/OswAppWatchfaceMix.h @@ -7,7 +7,7 @@ class OswAppWatchfaceMix : public OswAppV2 { public: - constexpr static const char* APP_ID = "osw.wf.mx"; + constexpr static const char* APP_ID = "osw.wf.mix"; const char* getAppId() override; const char* getAppName() override; diff --git a/include/apps/watchfaces/OswAppWatchfaceZwilight.h b/include/apps/watchfaces/OswAppWatchfaceZwilight.h new file mode 100644 index 000000000..4dd6d9687 --- /dev/null +++ b/include/apps/watchfaces/OswAppWatchfaceZwilight.h @@ -0,0 +1,76 @@ +#include +#include +#include + +#ifdef OSW_FEATURE_LUA +#include +#ifdef OSW_EMULATOR +#define LUA_PATH "../data/lua/" +#else +#define LUA_PATH FS_MOUNT_POINT "/lua/" +#endif + +#define LUA_APP_PATH LUA_PATH "apps/" +#define LUA_LIB_PATH LUA_PATH "libs/" + +#define LUA_APP_SEARCH_PATH LUA_APP_PATH "?.lua" +#define LUA_LIB_SEARCH_PATH LUA_LIB_PATH "?.lua" +#define LUA_PACKAGE_CMD "package.path = package.path .. ';" LUA_APP_SEARCH_PATH ";" LUA_LIB_SEARCH_PATH "'" +#endif + +enum SUNTIMES +{ + SUNTIME_ERROR = 0, + LAST_MIDNIGHT, + ASTRONOMIC_DAWN, + NAUTIC_DAWN, + CIVIL_DAWN, + SUN_RISE, + NOON, + SUN_SET, + CIVIL_DUSK, + NAUTIC_DUSK, + ASTRONOMIC_DUSK, + MIDNIGHT +}; + + + +class OswAppWatchfaceZwilight : public OswAppV2 { + public: + constexpr static const char* APP_ID = "osw.wf.zwilight"; + const char* getAppId() override; + const char* getAppName() override; + + void onStart() override; + void onLoop() override; + void onDraw() override; + void onStop() override; + void onButton(Button id, bool up, ButtonStateNames state) override; + + OswAppWatchfaceZwilight() { + lastTime = 0; + #ifdef OSW_FEATURE_LUA + luaState = nullptr; + #endif + } + + ~OswAppWatchfaceZwilight() { + cleanupLuaState(); + } + + private: + time_t lastTime = 0; + + void displayWeekDay2(const char* weekday); + void drawWatch(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t minute, int32_t second); + + bool getTwilights(); + void cleanupLuaState(); + void printLuaError(); + + float sunTimes[12]; + #ifdef OSW_FEATURE_LUA + lua_State* luaState = nullptr; + #endif +}; diff --git a/include/gfx_2d.h b/include/gfx_2d.h index c175466fc..0c24424a0 100644 --- a/include/gfx_2d.h +++ b/include/gfx_2d.h @@ -306,6 +306,10 @@ class Graphics2D { 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); + void drawArcAA(int16_t cx, int16_t cy, int16_t start, int16_t stop, int16_t radius, int16_t lineRadius, + uint16_t color, LINE_END_OPT eoa = ROUND_END); + + 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); diff --git a/include/locales/de-DE.h b/include/locales/de-DE.h index c94b34db4..90400befe 100644 --- a/include/locales/de-DE.h +++ b/include/locales/de-DE.h @@ -63,6 +63,9 @@ #define LANG_FITNESS_DISTANCE "Distanz" #define LANG_FITNESS_TOTAL "Insgesamt" +// App: Watchface Zwilight +#define LANG_ZWILIGHT "Sonnenstand" + // App: Step Statistics #define LANG_STEPSTATS_TITLE "Schritt-Statistik" diff --git a/include/locales/en-US.h b/include/locales/en-US.h index 9950139ed..77c51ae86 100644 --- a/include/locales/en-US.h +++ b/include/locales/en-US.h @@ -115,6 +115,10 @@ #ifndef LANG_AFIT #define LANG_AFIT "Analog+Fit" #endif +#ifndef LANG_ZWILIGHT +#define LANG_ZWILIGHT "SunTime" +#endif + // App: Time from web #ifndef LANG_TFW_UPDATE diff --git a/include/math_angles.h b/include/math_angles.h index 296404cc1..b3c492b49 100644 --- a/include/math_angles.h +++ b/include/math_angles.h @@ -6,6 +6,29 @@ extern const float sinDeg[]; +inline float sinf_tlu(int16_t angle) { + return sinDeg[angle]; +} + +inline float cosf_tlu(int16_t angle) { + if (angle + 90 <= 360) + return sinDeg[angle + 90]; + else + return sinDeg[angle + 90 - 360]; +} + +inline float tanf_tlu(int16_t angle) { + return sinf_tlu(angle)/cosf_tlu(angle); +} + + +inline int32_t roundAwayFromZero(float x) { + if (x >= 0) + return x + 0.5f; + else + return x - 0.5f; +} + /** * @brief Find the x-axis point which is at a distance r and an angle d of a point C(cx,cy). * @@ -19,7 +42,7 @@ extern const float sinDeg[]; * @return int32_t */ inline int32_t rpx(int32_t cx, int32_t r, float deg) { - return cx + (float) r * cosf((deg - 90) * (float) PI / 180) + 0.5f; + return cx + roundAwayFromZero((float) r * cosf((deg - 90) * (float) PI / 180)); } // integer version for deg @@ -30,7 +53,7 @@ inline int32_t rpx(int32_t cx, int32_t r, int32_t deg) { deg += 360; while (deg > 360) deg -= 360; - return cx + (float) r * sinDeg[deg] + 0.5f; + return cx + roundAwayFromZero((float) r * sinDeg[deg]); } /** @@ -45,7 +68,7 @@ inline int32_t rpx(int32_t cx, int32_t r, int32_t deg) { * @return float */ inline int32_t rpy(int32_t cy, int32_t r, float deg) { - return cy + (float) r * sinf((deg - 90) * (float) PI / 180) + 0.5f; + return cy + roundAwayFromZero((float) r * sinf((deg - 90) * (float) PI / 180)); } // integer version for deg @@ -55,7 +78,7 @@ inline int32_t rpy(int32_t cy, int32_t r, int32_t deg) { deg += 360; while (deg > 360) deg -= 360; - return cy + (float) r * sinDeg[deg] + 0.5f; + return cy + roundAwayFromZero((float) r * sinDeg[deg]); } // rotate a point around a point diff --git a/lib/LUA b/lib/LUA index 75ea9ccbe..a3a9873ac 160000 --- a/lib/LUA +++ b/lib/LUA @@ -1 +1 @@ -Subproject commit 75ea9ccbea7c4886f30da147fb67b693b2624c26 +Subproject commit a3a9873ac80239b7dd42bc839e5164fbf270372e diff --git a/platformio.ini b/platformio.ini index 651111c5f..134def122 100755 --- a/platformio.ini +++ b/platformio.ini @@ -39,26 +39,22 @@ extra_scripts = pre:scripts/build/prebuild_cppflags.py pre:scripts/build/prebuild_lua.py ; Needed to generate the .cxx file(s), enabled via "OSW_FEATURE_LUA" build flag build_unflags = -std=gnu++11 # The correct flag will be set by the cppflags python script... - -fno-jump-table [env:LIGHT_EDITION_V3_3_LUA] ;build_unflags = '-std=gnu++2a' - build_flags = -D OSW_TARGET_PLATFORM_HEADER='"platform/LIGHT_EDITION_V3_3.h"' -D OSW_FEATURE_STATS_STEPS -D OSW_FEATURE_WIFI ; -D OSW_FEATURE_LUA -; -D LUA_C89_NUMBERS ; Required by OSW_FEATURE_LUA -Wdouble-promotion -ffunction-sections -fdata-sections -Wno-deprecated-declarations -fpermissive ; -O2 - -fjump-tables build_type = debug @@ -85,11 +81,14 @@ build_type = debug [env:LIGHT_EDITION_DEV_LUA] build_flags = -D OSW_TARGET_PLATFORM_HEADER='"platform/LIGHT_EDITION_V3_3.h"' + -D OSW_FEATURE_STATS_STEPS -D OSW_FEATURE_LUA -D OSW_SERVICE_CONSOLE -D OSW_FEATURE_WIFI -D LUA_C89_NUMBERS ; Required by OSW_FEATURE_LUA -Wdouble-promotion + -D MAKE_LIB + extra_scripts = pre:scripts/build/prebuild_info.py pre:scripts/build/prebuild_assets.py diff --git a/scripts/build/compile_lua_libs.py b/scripts/build/compile_lua_libs.py new file mode 100644 index 000000000..48849ce57 --- /dev/null +++ b/scripts/build/compile_lua_libs.py @@ -0,0 +1,5 @@ +import "os" + + +print("Compiling lua bytecode with luac ...") +os.system("cd src.lua/libs/; for i in $(ls *.lua); do print $i; ../build/luac -s -o ../data/lua/libs/$i ; done ") diff --git a/scripts/build/prebuild_lua.py b/scripts/build/prebuild_lua.py old mode 100755 new mode 100644 index 48654ca18..036b5fc8d --- a/scripts/build/prebuild_lua.py +++ b/scripts/build/prebuild_lua.py @@ -66,5 +66,20 @@ def getSwigPath(): # Add define checks to disable the cxx file when it is not enabled to be build with open('./src/swig/osw_wrap.cxx', 'r') as original: data = original.read() with open('./src/swig/osw_wrap.cxx', 'w') as modified: modified.write("#ifdef OSW_FEATURE_LUA\n" + data + "\n#endif") + + # And compile luac for compressing lua chunks + print("Building luac ...") + ## return_code = os.system("cd ./lib/LUA/; gcc -O2 -std=c89 -DLUA_USE_C89 -DMAKE_LUAC -o ../../build/luac onelua.c -lm") + return_code = os.system("cd ./lib/LUA/; make luac; cp luac ../../build/luac") + if return_code != 0: + print("Building lua compiler (luac) failed with return code:") + print(return_code) + sys.exit(99) + else: + print("Building lua compiler (luac) successful") + + print("Compiling lua bytecode with luac ...") + os.system("cd src.lua; for i in $(find . -iname '*.lua'); do echo compiling $i; ../build/luac -s -o ../data/lua/$i $i; done") + else: - print("Skipping building osw_wrap.cxx with swig because OSW_FEATURE_LUA is not defined") \ No newline at end of file + print("Skipping building osw_wrap.cxx with swig because OSW_FEATURE_LUA is not defined") diff --git a/scripts/build/use_ccache.py b/scripts/build/use_ccache.py new file mode 100644 index 000000000..650821fe8 --- /dev/null +++ b/scripts/build/use_ccache.py @@ -0,0 +1,15 @@ + +from shutil import which + +Import("env") + +if which("ccache"): + env.Replace( + CC="ccache arm-none-eabi-gcc", + CXX="ccache arm-none-eabi-g++", + ) +else: + env.Replace( + CC="arm-none-eabi-gcc", + CXX="arm-none-eabi-g++", + ) diff --git a/src.lua/libs/suntime.lua b/src.lua/libs/suntime.lua new file mode 100644 index 000000000..9b6f46c6e --- /dev/null +++ b/src.lua/libs/suntime.lua @@ -0,0 +1,607 @@ +--[[-- +-- Author: Martin Zwicknagl (zwim) +-- Date: 2021-10-29 +-- The current source code of this file can be found on https://github.com/zwim/suntime. + +Module to calculate ephemeris and other times depending on the sun position. + +Maximal errors from 2020-2050 (compared to https://midcdmz.nrel.gov/spa/) are: + +* -43.52° Christchurch 66s +* -20.16° Mauritius: 25s +* 20.30° Honolulu: 47s +* 33.58° Casablanca: 24s +* 35.68° Tokio: 50s +* 37.97° Athene: 24s +* 38° Sacramento: 67s +* 41.91° Rome: 27s +* 47.25° Innsbruck: 13s +* 52.32° Berlin: 30s +* 59.92° Oslo: 42s +* 64.14° Reykjavik: 69s +* 65.69° Akureyri: <24s (except *) +* 70.67° Hammerfest: <105s (except **) + +*) A few days around beginning of summer (error <290s) + +**) A few days after and befor midnight sun (error <1200s) + +@usage + local SunTime = require("suntime") + + time_zone = 0 + altitude = 50 + degree = true + SunTime:setPosition("Reykjavik", 64.14381, -21.92626, timezone, altitude, degree) + + SunTime:setAdvanced() + + SunTime:setDate() + + SunTime:calculateTimes() + + print(SunTime.rise, SunTime.set, SunTime.set_civil) -- or similar see calculateTime() + +@module suntime +--]]-- + +-- math abbrevations +local pi = math.pi +local pi_2 = pi/2 + +local abs = math.abs +local floor = math.floor +local sin = math.sin +local cos = math.cos +local tan = math.tan +local asin = math.asin +local acos = math.acos +local atan = math.atan + +local toRad = pi/180 +local toDeg = 1/toRad + +local function Rad(x) + return x*toRad +end + +-------------------------------------------- +local speed_of_light = 2.99792E8 +local sun_radius = 6.96342e8 +local average_earth_radius = 6371e3 +local semimajor_axis = 149598022.96E3 -- earth orbit's major semi-axis in meter +local average_speed_earth = 29.7859e3 +local aberration = asin(average_speed_earth/speed_of_light) -- Aberration relativistic +-- local average_speed_equator = (2*pi * average_earth_radius) / (24*3600) +-------------------------------------------- + + -- minimal twillight times in hours +local min_civil_twilight = 20/60 +local min_nautic_twilight = 45/60 - min_civil_twilight +local min_astronomic_twilight = 20/60 - min_nautic_twilight + +local SunTime = { + astronomic = Rad(-18), + nautic = Rad(-12), + civil = Rad(-6), + -- eod = Rad(-49/60), -- approx. end of day + earth_flatten = 1 / 298.257223563, -- WGS84 + average_temperature = 10, -- °C + + times = {}, + } + +---------------------------------------------------------------- + +-- simple 'Equation of time' good for dates between 2008-2027 +-- errors for latitude 20° are within 1min +-- 47° are within 1min 30sec +-- 65° are within 5min +-- https://www.astronomie.info/zeitgleichung/#Auf-_und_Untergang (German) +function SunTime:getZglSimple() + local T = self.date.yday + return -0.171 * sin(0.0337 * T + 0.465) - 0.1299 * sin(0.01787 * T - 0.168) +end + +-- more advanced 'Equation of time' good for dates between 1800-2200 +-- errors are better than with the simple method +-- https://de.wikipedia.org/wiki/Zeitgleichung (German) and +-- more infos on http://www.hlmths.de/Scilab/Zeitgleichung.pdf (German) +function SunTime:getZglAdvanced() + local e = self.num_ex + local e2 = e*e + local e3 = e2*e + local e4 = e3*e + local e5 = e4*e + + -- https://de.wikibooks.org/wiki/Astronomische_Berechnungen_f%C3%BCr_Amateure/_Himmelsmechanik/_Sonne + local C = (2*e - e3/4 + 5/96*e5) * self.sin_M + + (5/4*e2 + 11/24*e4) * self.sin_2M + + (13/12*e3 - 43/64*e5) * self.sin_3M + + 103/96*e4 * self.sin_4M + + 1097/960*e5 * self.sin_5M -- rad + + local lamb = self.L + C + local tanL = tan(self.L) + local tanLamb = tan(lamb) + local cosEps = cos(self.epsilon) + + local zgl = atan( (tanL - tanLamb*cosEps) / (1 + tanL*tanLamb*cosEps) ) --rad + return zgl*toDeg/15 -- to hours *4'/60 +end + +-- set current date or year/month/day daylightsaving hh/mm/ss +-- if dst == nil use curent daylight saving of the system +local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} + +function SunTime:setDate(year, month, day, dst, hour, min, sec) + self.date = os.date("*t") -- get current day + + if year and month and day then + self.date.year = year + self.date.month = month + self.date.day = day + if year % 4 == 0 and (year % 100 ~= 0 or year % 400 == 0) then + days_in_month[2] = 29 + else + days_in_month[2] = 28 + end + self.date.yday = day + for i = 1, month-1 do + self.date.yday = self.date.yday + days_in_month[i] + end + self.date.hour = hour or 12 + self.date.min = min or 0 + self.date.sec = sec or 0 + if dst ~= nil then + self.date.isdst = dst + end + end +end + +--[[-- +Set position for later calculations + +@param name Name of the location +@param latitude Geographical latitude, North is positive +@param longitude Geographical longitude, West is negative +@param time_zone Timezone e.g. CET = +1; if nil try to autodetect the current zone +@param altitude Altitude of the location above the sea level +@param degree if `nil` latitude and longitue are in radian, else in decimal degree + --]]-- +function SunTime:setPosition(name, latitude, longitude, time_zone, altitude, degree) + altitude = altitude or 200 + if degree then + latitude = latitude * toRad + longitude = longitude * toRad + end + + -- check for sane values + -- latitudes are from -90° to +90° + if latitude > pi_2 then + latitude = pi_2 + elseif latitude < -pi_2 then + latitude = -pi_2 + end + -- longitudes are from -180° to +180° + if longitude > pi then + longitude = pi + elseif longitude < -pi then + longitude = -pi + end + + latitude = atan((1-self.earth_flatten)^2 * tan(latitude)) + + self.pos = {name = name, latitude = latitude, longitude = longitude, altitude = altitude} + self.time_zone = time_zone or self:getTimezoneOffset() +-- self.refract = Rad(36.35/60 * .5 ^ (altitude / 5538)) -- constant temperature + self.refract = Rad(36.20/60 * (1 - 0.0065*altitude/(273.15+self.average_temperature)) ^ 5.255 ) + + self.sin_latitude = sin(self.pos.latitude) + self.cos_latitude = cos(self.pos.latitude) +end + +--[[-- + Use a simple equation of time (valid for the years 2008-2027) +--]]-- +function SunTime:setSimple() + self.getZgl = self.getZglSimple +end +--[[-- + Use an advanced equation of time (valid for the years 1800-2200 at least) +--]]-- +function SunTime:setAdvanced() + self.getZgl = self.getZglAdvanced +end + +--[[-- + Function to get the equation of time, can be set by setSimple() or setAdvanced() +--]]-- +SunTime.getZgl = SunTime.getZglAdvanced + +function SunTime:daysSince2000(hour) + local delta = self.date.year - 2000 + local leap = floor((delta-1)/4) + return 365 * delta + leap + self.date.yday + (hour-12)/24 -- WMO No.8, rebased for 2000-01-01 12:00 +end + +-- more accurate parameters of earth orbit from +-- Title: Numerical expressions for precession formulae and mean elements for the Moon and the planets +-- Authors: Simon, J. L., Bretagnon, P., Chapront, J., Chapront-Touze, M., Francou, G., & Laskar, J., , +-- Journal: Astronomy and Astrophysics (ISSN 0004-6361), vol. 282, no. 2, p. 663-683 +-- Bibliographic Code: 1994A&A...282..663S +function SunTime:initVars(hour) + if not hour then + hour = 12 + end + + local after_noon = hour > 12 + + local T = self:daysSince2000(hour)/36525 -- in Julian centuries form 2000-01-01 12:00 + + -- self.num_ex = 0.0167086342 - 0.000042 * T + -- numerical eccentricity of earth's orbit + -- see wikipedia: https://de.wikipedia.org/wiki/Erdbahn-> Meeus + -- and Numerical expressions for preccession formulae + -- time is in Julian centuries + self.num_ex = 0.0167086342 + T*(-0.0004203654e-1 + + T*(-0.0000126734e-2 + T*( 0.0000001444e-3 + + T*(-0.0000000002e-4 + T* 0.0000000003e-5)))) + + -- self.epsilon = (23 + 26/60 + 21/3600 - 46.82/3600 * T) * toRad + -- earth's obliquity to the ecliptic + -- see Numerical expressions for precession formulae ... + -- Time is here in Julian centuries +-- local epsilon = 23 + 26/60 + (21.412 + T*(-468.0927E-1 +-- + T*(-0.0152E-2 + T*(1.9989E-3 +-- + T*(-0.0051E-4 - T*0.0025E-5)))))/3600 --° + + -- Astronomical Almanac 2010, p. B52 + -- Time is here in Julian centuries + local epsilon = 23 + 26/60 + (21.406 - T*(46.836769 + - T*( 0.0001831 + T*(0.00200340 + + T*(-5.76E-7 - T* 4.34E-8)))))/3600 --° + + self.epsilon = epsilon * toRad + + -- see Numerical expressions for precession formulae ... + -- mean longitude + local nT = T * (36000.7690/35999.3720) -- convert from equinox to date + local L = 100.46645683 + (nT*(1295977422.83429E-1 + + nT*(-2.04411E-2 - nT* 0.00523E-3)))/3600 --° + self.L = (L - floor(L/360)*360) * toRad + + -- see Numerical expressions for precession formulae ... + -- Time is here in Julian centuries + local omega = 102.93734808 + nT*(11612.35290e-1 + + nT*(53.27577e-2 + nT*(-0.14095e-3 + + nT*( 0.11440e-4 + nT* 0.00478e-5))))/3600 --° + + -- mean anomaly + local M = L - omega --° + self.M = (M - floor(M/360)*360) * toRad + + self.sin_M = sin(self.M) + self.cos_M = cos(self.M) + + -- sin(2x)=2 sin(x) cos(x) + self.sin_2M = 2 * self.sin_M * self.cos_M + + -- sin(3x) = 3 sin(x) − 4 sin(x)^3 + self.sin_3M = 3 * self.sin_M - 4 * self.sin_M^3 + + -- sin(4x) = 8 sin(x) cos(x)^3 - 4 sin(x) cos(x) + self.sin_4M = 8 * self.sin_M * self.cos_M^3 - 4 * self.sin_M * self.cos_M + + -- sin(5x) = 5 sin(x) - 20 sin(x)^3+ 16 sin(x)^5 + self.sin_5M = 5 * self.sin_M - 20 * self.sin_M^3 + 16 * self.sin_M^5 + + -- Deklination nach astronomie.info + -- local decl = 0.4095 * sin(0.016906 * (self.date.yday - 80.086)) + --Deklination nach Brodbeck (2001) + -- local decl = 0.40954 * sin(0.0172 * (self.date.yday - 79.349740)) + + -- Deklination WMO-No.8 page I-7-37 + --local T = self.days_since_2000 + --local L = 280.460 + 0.9856474 * T + --L = (L - floor(L/360)*360) * toRad + --local g = 357.528 + 0.9856003 * T -- mean anomaly + --g = (g - floor(g/360)*360) * toRad + --local l = L + (1.915 * sin (g) + 0.020 * sin (2*g))*toRad + --local ep = self.epsilon + -- -- sin(decl) = sin(ep)*sin(l) + --self.decl = asin(sin(ep)*sin(l)) + + -- Deklination WMO-No.8 page I-7-37 + local l = self.L + pi + (1.915 * self.sin_M + 0.020 * self.sin_2M)*toRad + self.decl = asin(sin(self.epsilon)*sin(l)) + + -- Nutation see https://de.wikipedia.org/wiki/Nutation_(Astronomie) + local A = { 2.18243920 - 33.7570460 * T, + -2.77624462 + 1256.66393 * T, + 7.62068856 + 16799.4182 * T, + 4.36487839 - 67.5140919 * T} + local B = {92025e-4 + 8.9e-4 * T, + 5736e-4 - 3.1e-4 * T, + 977e-4 - 0.5e-4 * T, + -895e-4 + 0.5e-4 * T} + local delta_epsilon = 0 --" + for i = 1, #A do + delta_epsilon = delta_epsilon + B[i]*cos(A[i]) + end + + -- add nutation to declination + self.decl = self.decl + delta_epsilon/3600*toRad + + -- https://de.wikipedia.org/wiki/Kepler-Gleichung#Wahre_Anomalie + self.E = self.M + self.num_ex * self.sin_M + self.num_ex^2 / 2 * self.sin_2M + self.r = semimajor_axis * (1 - self.num_ex * cos(self.E)) + + -- self.eod = -atan(sun_radius/self.r) - self.refract + -- ^- astronomical refraction (at altitude) + + if after_noon then + self.eod = -atan((sun_radius-average_earth_radius*self.cos_latitude)/self.r) - self.refract + self.eod = self.eod + aberration + else + self.eod = -atan((sun_radius+average_earth_radius*self.cos_latitude)/self.r) - self.refract + self.eod = self.eod - aberration + end + + self.zgl = self:getZgl() +end + +function SunTime:getTimeDiff(height) + local val = (sin(height) - self.sin_latitude*sin(self.decl)) + / (self.cos_latitude*cos(self.decl)) + + if abs(val) > 1 then + return + end + return 12/pi * acos(val) +end + +-- get the sun height for a given time +-- eod for considering sun diameter and astronomic refraction +function SunTime:getHeight(time, eod) + time = time - 12 -- subtrace 12, because JD starts at 12:00 + local val = cos(self.decl)*self.cos_latitude*cos(pi/12*time) + + sin(self.decl)*self.sin_latitude + + if abs(val) > 1 then + return + end + + if eod then + return asin(val) - eod -- self.eod might be a bit too small + else + return asin(val) + end +end + +-- Get time for a certain height +-- Set height to nil for sunset/rise +-- Set hour near to expected time +-- Set after_noon to true, if sunset is wanted +-- Set no_correct_dst if no daylight saving correction is wanted +-- Result rise or set time +-- nil sun does not reach the height +function SunTime:calculateTime(height, hour, after_noon, no_correct_dst) + if not no_correct_dst then + if self.date.isdst and hour then + hour = hour - 1 + end + end + self:initVars(hour) -- calculate self.eod + local timeDiff = self:getTimeDiff(height or self.eod, hour) + if not timeDiff then + return + end + + local local_correction = self.time_zone - self.pos.longitude*12/pi - self.zgl + if not after_noon then + hour = 12 - timeDiff + local_correction + else + hour = 12 + timeDiff + local_correction + end + if not no_correct_dst then + if self.date.isdst and hour then + hour = hour + 1 + end + end + return hour +end + +-- Calculates the hour, when the sun reaches height +-- If height is nil, use newly calculated self.eod +-- hour gives a start value, default is used when hour == nil +function SunTime:calculateTimeIter(height, hour, default_hour) + local after_noon = (hour and hour > 12) or (default_hour and default_hour > 12) + + if not hour then -- do the iteration with the default value + hour = self:calculateTime(height, default_hour, after_noon, true) + elseif hour and not default_hour then -- do the full iteration with value + hour = self:calculateTime(height, hour, after_noon, true) + end -- if hour and default_hour are given don't do the first step + + if hour ~= nil then -- do the last calculation step + hour = self:calculateTime(height, hour, hour > 12) + end + return hour +end + +function SunTime:calculateNoon(hour) + hour = hour or 12 + self:initVars(hour) + local aberration_time = aberration / pi * 12 -- aberration in hours (angle/(2pi)*24) + local dst = self.date.isdst and 1 or 0 + local local_correction = self.time_zone - self.pos.longitude*12/pi + dst - self.zgl + if self.pos.latitude >= 0 then -- northern hemisphere + if pi_2 - self.pos.latitude + self.decl > self.eod then + if self:getHeight(hour) > 0 then + return hour + local_correction + aberration_time + end + end + else -- sourthern hemisphere + if pi_2 + self.pos.latitude - self.decl > self.eod then + if self:getHeight(hour) > 0 then + return hour + local_correction + aberration_time + end + end + end +end + +function SunTime:calculateMidnight(hour) + -- hour: + -- 00 would be the beginning of the day + -- 24 is the midnight at the end of the current day, + hour = hour or 24 + self:initVars(hour) + local dst = self.date.isdst and 1 or 0 + -- no aberration correction here, as you can't see the sun on her nadir ;-) + local local_correction = self.time_zone - self.pos.longitude*12/pi + dst - self.zgl + if self.pos.latitude >= 0 then -- northern hemisphere + if pi_2 - self.pos.latitude - self.decl > self.eod then + if self:getHeight(hour) < 0 then + return hour + local_correction + end + end + else -- sourthern hemisphere + if pi_2 + self.pos.latitude + self.decl > self.eod then + if self:getHeight(hour) < 0 then + return hour + local_correction + end + end + end +end + +--[[-- +Calculates the ephemeris and twilight times + +@param fast_twilight If not nil, then exact twilight times will be calculated. + +@usage +SunTime:calculateTimes(fast_twilight) + + +Times are in hours or `nil` if not applicable. + +You can then access: + self.midnight_beginning + + self.rise_astronomic + self.rise_nautic + self.rise_civil + self.rise + + self.noon + + self.set + self.set_civil + self.set_nautic + self.set_astronomic + + self.midnight + +Or as values in a table: + self.times[1] midnight_beginning + self.times[2] rise_astronomic + self.times[3] rise_nautic + self.times[4] rise_civil + self.times[5] rise + self.times[6] noon + self.times[7] set + self.times[8] set_civil + self.times[9] set_nautic + self.times[10] set_astronomic + self.times[11] midnight +--]]-- +function SunTime:calculateTimes(fast_twilight) + -- All or some the times can be nil at great latitudes + -- but either noon or midnight is not nil + + if not fast_twilight then + -- The canonical way is to calculate everything from scratch + self.rise = self:calculateTimeIter(nil, 6) + self.set = self:calculateTimeIter(nil, 18) + + self.rise_civil = self:calculateTimeIter(self.civil, 6) + self.set_civil = self:calculateTimeIter(self.civil, 18) + self.rise_nautic = self:calculateTimeIter(self.nautic, 6) + self.set_nautic = self:calculateTimeIter(self.nautic, 18) + self.rise_astronomic = self:calculateTimeIter(self.astronomic, 6) + self.set_astronomic = self:calculateTimeIter(self.astronomic, 18) + else + -- Calculate rise and set from scratch, use these values for twilight times + self.rise = self:calculateTimeIter(nil, 6) + self.rise_civil = self:calculateTimeIter(self.civil, self.rise - min_civil_twilight, 6) + self.rise_nautic = self:calculateTimeIter(self.nautic, self.rise_civil - min_nautic_twilight, 6) + self.rise_astronomic = self:calculateTimeIter(self.astronomic, self.rise_nautic - min_astronomic_twilight, 6) + + self.set = self:calculateTimeIter(nil, 18) + self.set_civil = self:calculateTimeIter(self.civil, self.set + min_civil_twilight, 18) + self.set_nautic = self:calculateTimeIter(self.nautic, self.set_civil + min_nautic_twilight, 18) + self.set_astronomic = self:calculateTimeIter(self.astronomic, self.set_nautic + min_astronomic_twilight, 18) + end + + self.midnight_beginning = self:calculateMidnight(0) + self.noon = self:calculateNoon() + self.midnight = self:calculateMidnight() + + -- Sometimes at high latitudes noon or midnight does not get calculated. + -- Maybe there is a minor bug in the calculateNoon/calculateMidnight functions. + if self.rise and self.set then + if not self.noon and self.rise and self.set then + self.noon = (self.rise + self.set) / 2 + end + if not self.midnight and self.noon then + self.midnight = self.noon + 12 + end + if not self.midnight_beginning and self.midnight then + self.midnight_beginning = self.midnight - 24 + elseif not self.midnight and self.midnight_beginning then + self.midnight = self.midnight_beginning + 24 + end + elseif self.rise and not self.set then -- only sunrise on that day + self.midnight = nil + self.midnight_beginning = nil + elseif self.set and not self.rise then -- only sunset on that day + self.noon = nil + end + + self.times[1] = self.midnight_beginning + self.times[2] = self.rise_astronomic + self.times[3] = self.rise_nautic + self.times[4] = self.rise_civil + self.times[5] = self.rise + self.times[6] = self.noon + self.times[7] = self.set + self.times[8] = self.set_civil + self.times[9] = self.set_nautic + self.times[10] = self.set_astronomic + self.times[11] = self.midnight +end + +-- Get time in seconds, rounded to ms (either actual time in hours or date struct) +function SunTime:getTimeInSec(val) + if not val then + val = os.date("*t") + end + + if type(val) == "table" then + val = val.hour*3600 + val.min*60 + val.sec + else + val = val*3600 + end + return math.floor(val * 1000) * (1/1000) +end + +-- Get the timezone offset in hours (including dst). + function SunTime:getTimezoneOffset() + local now_ts = os.time() + local utcdate = os.date("!*t", now_ts) + local localdate = os.date("*t", now_ts) + return os.difftime(os.time(localdate), os.time(utcdate)) * (1/3600) +end + +return SunTime diff --git a/src/apps/watchfaces/OswAppWatchface.cpp b/src/apps/watchfaces/OswAppWatchface.cpp index 859c776d1..bdca73d5c 100644 --- a/src/apps/watchfaces/OswAppWatchface.cpp +++ b/src/apps/watchfaces/OswAppWatchface.cpp @@ -69,9 +69,13 @@ void OswAppWatchface::drawWatch() { #if OSW_PLATFORM_ENVIRONMENT_ACCELEROMETER == 1 uint32_t steps = hal->environment()->getStepsToday(); +#ifdef OSW_EMULATOR + steps = 4000; +#endif + uint32_t stepsTarget = OswConfigAllKeys::stepsPerDay.get(); - 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); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 90 + 360.0f * (float)(steps % stepsTarget) / (float)stepsTarget, 90, 93, 6, + steps > stepsTarget ? ui->getSuccessColor() : ui->getInfoColor()); #endif #ifdef OSW_FEATURE_STATS_STEPS diff --git a/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp b/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp new file mode 100644 index 000000000..adb86ca9e --- /dev/null +++ b/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp @@ -0,0 +1,358 @@ +#include +#include +#include + +#include "./apps/watchfaces/OswAppWatchfaceZwilight.h" + +#define CENTER_X (DISP_W / 2) +#define CENTER_Y (DISP_H / 2) + +void OswAppWatchfaceZwilight::cleanupLuaState() { + #ifdef OSW_FEATURE_LUA + if (luaState) { + lua_close(luaState); + luaState = NULL; + } + #endif +} + +inline void OswAppWatchfaceZwilight::printLuaError() { + #ifdef OSW_FEATURE_LUA + if (luaState) + OSW_LOG_E(lua_tostring(luaState, -1)); + #endif +} + +bool OswAppWatchfaceZwilight::getTwilights() { + + #ifdef OSW_FEATURE_LUA + cleanupLuaState(); + luaState = luaL_newstate(); + + if (!luaState) { + OSW_LOG_E("luaL_newstate failed\n"); + return false; + } + + luaL_openlibs(luaState); +// halToLua(luaState); + + //Include search paths + luaL_dostring(luaState, LUA_PACKAGE_CMD); + + if (luaL_dofile(luaState, LUA_APP_PATH "twilight.lua")) { + printLuaError(); + cleanupLuaState(); + return false; + } + +// lua_getnumber(lua_getglobal("varname")) + + int luaInitialStackSize = lua_gettop(luaState); + + uint32_t year = 0; + uint32_t month = 0; + uint32_t day = 0; + time_t timeZone = 0; + + hal->updateTimezoneOffsets(); + timeZone = hal->getTimezoneOffsetPrimary(); + hal->getDate(timeZone, &day, &month, &year); + + uint32_t timeZone_offset_h = timeZone / 3600; + +printf("xxx timeZone %d\n", (int) timeZone); + + // setParams(latitude, longitude, timezone, altitude, year, month, day, dst) + lua_getglobal(luaState, "setParams"); + + lua_pushnumber(luaState, 47.51f); + lua_pushnumber(luaState, 12.09f); + lua_pushnumber(luaState, timeZone_offset_h); + lua_pushnumber(luaState, 520.0f); + lua_pushnumber(luaState, year); + lua_pushnumber(luaState, month); + lua_pushnumber(luaState, day); + lua_pushboolean(luaState, false); // true + + if (lua_pcall(luaState, 8, 0, 0)) { + printf("xxxxx8\n"); + printLuaError(); + cleanupLuaState(); + return false; + } + + for (int i = LAST_MIDNIGHT; i <= MIDNIGHT; ++i) { + lua_getglobal(luaState, "getTimes"); + lua_pushnumber(luaState, i); + if (lua_pcall(luaState, 1, 1, 0)) { + printf("xxxx33333333 knock out\n"); + printLuaError(); + cleanupLuaState(); + return false; + } + sunTimes[i] = lua_tonumber(luaState, -1); + lua_pop(luaState, 1); + } + +/* + lua_getglobal(luaState, "SunTime"); + lua_getfield(luaState, -1, "times"); + for (int i = 1; i <=11; ++i) { + if ( lua_geti(luaState, -1, i) == LUA_TNUMBER) { // Times + sunTimes[i] = lua_tonumber(luaState, -1); + printf("xxxxx SunTime.times[%d]=%f\n", i, sunTimes[i]); + } else { + sunTimes[i] = -10000; + } + + } + lua_pop(luaState, 11+2); +*/ + + + if (luaInitialStackSize != lua_gettop(luaState)) { + OSW_LOG_E("Oh my god, the lua stack has grown from", luaInitialStackSize, "to", lua_gettop(luaState)); + } + + #else + #error "LUA not defined" + #endif + + return true; +} + +void OswAppWatchfaceZwilight::displayWeekDay2(const char* weekday) { + OswHal* hal = OswHal::getInstance(); + + char weekday2[3]; + weekday2[0] = weekday[0]; + weekday2[1] = weekday[1]; + weekday2[2] = '\0'; + hal->gfx()->print(weekday2); +} + +void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day, int32_t hour, int32_t minute, int32_t second) { + OswHal* hal = OswHal::getInstance(); + + uint16_t day_color = rgb565(0, 50, 255); + uint16_t civil_color = rgb565(0, 50 * .66, 255 * .66); + uint16_t nautic_color = rgb565(0, 50 * .40, 255 * .40); + uint16_t astronomic_color = rgb565(0, 50 * .35, 255 * .35); + uint16_t night_color = rgb565(0, 50 * 0.3, 255 * 0.3); + uint16_t sun_color = rgb565(255, 255, 10); + + const char* weekday = hal->getLocalWeekday(); + + // night + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[ASTRONOMIC_DAWN]*(360/24), 270 - sunTimes[ASTRONOMIC_DUSK]*(360/24), 103, 5, night_color, STRAIGHT_END); + + // astronomical + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[NAUTIC_DAWN]*(360/24), 270 - sunTimes[ASTRONOMIC_DAWN]*(360/24), 103, 5, astronomic_color, STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[ASTRONOMIC_DUSK]*(360/24), 270 - sunTimes[NAUTIC_DUSK]*(360/24), 103, 5, astronomic_color, STRAIGHT_END); + + // nautical + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[CIVIL_DAWN]*(360/24), 270 - sunTimes[NAUTIC_DAWN]*(360/24), 103, 5, nautic_color, STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[NAUTIC_DUSK]*(360/24), 270 - sunTimes[CIVIL_DUSK]*(360/24), 103, 5, nautic_color, STRAIGHT_END); + + // civil + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[SUN_RISE]*(360/24), 270 - sunTimes[CIVIL_DAWN]*(360/24), 103, 5, civil_color, STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[CIVIL_DUSK]*(360/24), 270 - sunTimes[SUN_SET]*(360/24), 103, 5, civil_color, STRAIGHT_END); + + // daytime + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[SUN_SET]*(360/24), 270 - sunTimes[SUN_RISE]*(360/24), 103, 5, day_color, STRAIGHT_END); + + + // outer indices + //hal->gfx()->drawNTicksAA(CENTER_X, CENTER_Y, 120, 110, 24*2, ui->getForegroundDimmedColor(), 2); + hal->gfx()->drawNTicksAA(CENTER_X, CENTER_Y, 120, 110, 24, ui->getForegroundDimmedColor()); + + // Time indicator + int16_t day_indicator_angle; + day_indicator_angle = 270 - 360/24 * (hour + minute / 60.0f); + while (day_indicator_angle > 360) + day_indicator_angle -= 360; + while (day_indicator_angle < 0) + day_indicator_angle += 360; + hal->gfx()->drawCircleAA(CENTER_X + 115*cosf_tlu(day_indicator_angle), CENTER_Y - 115*sinf_tlu(day_indicator_angle), + 5, 2, sun_color); + + // sunTimes + // Attention MIDNIGHT is for today; LAST_MIDNIGHT was for yesterday + // We only want to show one, as they differ a bit. + for (int i = MIDNIGHT; i>=LAST_MIDNIGHT+1; --i) { + day_indicator_angle = 270 - sunTimes[i] * (360/24); + while (day_indicator_angle > 360) + day_indicator_angle -= 360; + while (day_indicator_angle < 0) + day_indicator_angle += 360; + + int16_t x0, y0, x1, y1; + int16_t r1 = 120; + int16_t r2 = 105; + x0 = CENTER_X + r1*cosf_tlu(day_indicator_angle); + y0 = CENTER_Y - r1*sinf_tlu(day_indicator_angle); + x1 = CENTER_X + r2*cosf_tlu(day_indicator_angle); + y1 = CENTER_Y - r2*sinf_tlu(day_indicator_angle); + hal->gfx()->drawLineAA(x0, y0, x1, y1, sun_color); + } + + // Day complication + hal->gfx()->setTextColor(dimColor(ui->getForegroundColor(), 25)); + + hal->gfx()->setTextSize(2); + hal->gfx()->setTextLeftAligned(); + hal->gfx()->setTextCursor(120-30+2, 120-40); + OswAppWatchfaceZwilight::displayWeekDay2(weekday); + + // Day of Month complication + hal->gfx()->print(" "); + hal->gfx()->printDecimal(day, 2); + + // Steps complication + +#if OSW_PLATFORM_ENVIRONMENT_ACCELEROMETER == 1 + { // draw step arc + uint32_t steps = hal->environment()->getStepsToday(); + uint32_t stepsTarget = OswConfigAllKeys::stepsPerDay.get(); + + #ifdef OSW_EMULATOR + steps = 1798; + #endif + + int16_t radius = 50; + int16_t lw = 3; +// int16_t sa = 180+55; +// int16_t ea = 180-55; + int16_t sa = 360; + int16_t ea = 180; + int16_t angle_val = sa - (sa-ea) * (float)min(steps, stepsTarget) / (float)stepsTarget; + uint16_t color = day_color; + uint16_t dimmed_color = night_color; + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, radius + lw, lw*2, dimmed_color, ea, angle_val); + + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, radius + lw, lw*2, color, angle_val, sa); + + int x, y; + + x = CENTER_X + cosf_tlu(ea) * radius; + y = CENTER_Y - sinf_tlu(ea) * radius; + hal->gfx()->drawCircleAA(x, y, lw, 0, dimmed_color); + + x = CENTER_X + cosf_tlu(angle_val) * radius; + y = CENTER_Y - sinf_tlu(angle_val) * radius; + hal->gfx()->drawCircleAA(x, y, lw-1, 0, color); + + x = CENTER_X + cosf_tlu(sa) * radius; + y = CENTER_Y - sinf_tlu(sa) * radius; + hal->gfx()->drawCircleAA(x, y, lw, 0, color); + + hal->gfx()->setTextColor(day_color); + hal->gfx()->setTextSize(1); + hal->gfx()->setTextCenterAligned(); + hal->gfx()->setTextCursor(CENTER_X, CENTER_Y + radius + 20); + hal->gfx()->print(steps); + } +#endif + + // Now the hands + + const int16_t clock_r = 95; + + // Indices + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, clock_r, 5, rgb565(35, 35, 35)); + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, clock_r - 5, 5, rgb565(30, 30, 30)); + hal->gfx()->drawMinuteTicks(CENTER_X, CENTER_Y, clock_r, clock_r - 5, ui->getForegroundDimmedColor(), true); + hal->gfx()->drawHourTicks(CENTER_X, CENTER_Y, clock_r, clock_r - 10, 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, clock_r - 5, (int)(360.0f / 60.0f * (minute + second / 60.0f)), 7, ui->getForegroundColor(), true); + + // Seconds + hal->gfx()->fillCircleAA(CENTER_X, CENTER_Y, 6, ui->getDangerColor()); + hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, -16, clock_r - 5, 360 / 60 * second, 3, ui->getDangerColor(), true); + +} + +void OswAppWatchfaceZwilight::onStart() { + OswAppV2::onStart(); + + this->viewFlags = OswAppV2::ViewFlags::NO_OVERLAYS; // no overlay for this watchface + +#ifndef OSW_EMULATOR + unsigned long old_micros = micros(); +#else + unsigned long old_millis = millis(); +#endif + + hal->updateTimezoneOffsets(); + + this->getTwilights(); + +#ifndef OSW_EMULATOR + unsigned long ms_for_onDraw = (micros()-old_micros)/1000; +#else + unsigned long ms_for_onDraw = millis()-old_millis; +#endif + OSW_LOG_I("Time to start ", ms_for_onDraw, " ms"); + +} + +void OswAppWatchfaceZwilight::onLoop() { + OswAppV2::onLoop(); + + this->needsRedraw = this->needsRedraw or time(nullptr) != this->lastTime; // redraw every second +} + +void OswAppWatchfaceZwilight::onDraw() { + OswAppV2::onDraw(); + +#ifndef OSW_EMULATOR + unsigned long old_micros = micros(); +#else + unsigned long old_millis = millis(); +#endif + + uint32_t year = 0; + uint32_t month = 0; + uint32_t day = 0; + uint32_t hour = 0; + uint32_t minute = 0; + uint32_t second = 0; + time_t timeZone = 0; + + timeZone = hal->getTimezoneOffsetPrimary(); + hal->getDate(timeZone, &day, &month, &year); + hal->getTime(timeZone, &hour, &minute, &second); + + drawWatch(year, month, day, hour, minute, second); +#ifndef OSW_EMULATOR + unsigned long ms_for_onDraw = (micros()-old_micros)/1000; +#else + unsigned long ms_for_onDraw = millis()-old_millis; +#endif + OSW_LOG_I("Time to draw ", ms_for_onDraw, " ms"); + + this->lastTime = time(nullptr); +} + +void OswAppWatchfaceZwilight::onButton(Button id, bool up, OswAppV2::ButtonStateNames state) { + OswAppV2::onButton(id, up, state); +} + +void OswAppWatchfaceZwilight::onStop() { + OswAppV2::onStop(); +} + +const char* OswAppWatchfaceZwilight::getAppId() { + return OswAppWatchfaceZwilight::APP_ID; +} + +const char* OswAppWatchfaceZwilight::getAppName() { + return LANG_ZWILIGHT; +} diff --git a/src/devices/virtual.cpp b/src/devices/virtual.cpp index 08f00dc27..d217ca1ae 100644 --- a/src/devices/virtual.cpp +++ b/src/devices/virtual.cpp @@ -19,20 +19,18 @@ time_t Virtual::getTimezoneOffset(const time_t& timestamp, const String& timezon tzset(); std::tm localTm = *std::localtime(×tamp); - setenv("TZ", "UTC0", 1); // overwrite the TZ environment variable - tzset(); - std::tm utcTm = *std::localtime(×tamp); - std::time_t utc = std::mktime(&utcTm); - std::time_t local = std::mktime(&localTm); // this removes the "local"/dst offsets by UTC from our localized timestamp -> we get the UTC timestamp INCLUDING the local offsets! - OSW_LOG_I("local: ", local, " utc: ", utc); - OSW_LOG_I("diff: ", local - utc); - if(hasOldTimezone) { - setenv("TZ", oldTimezone.c_str(), 1); // restore the TZ environment variable - tzset(); - } - if(utc == -1 or local == -1) + time_t rawtime = time(NULL); + struct tm *ptm = gmtime(&rawtime); + time_t gmt = mktime(ptm); + ptm = localtime(&rawtime); + time_t offset = rawtime - gmt + (ptm->tm_isdst ? 3600 : 0); + + OSW_LOG_I("local: ", ptm, " utc: ", gmt); + OSW_LOG_I("diff: ", offset); + + if(gmt == -1 or rawtime == -1) throw std::logic_error("Could not represent times in std::time_t?!"); - return local - utc; + return offset; } #endif } diff --git a/src/gfx_2d.cpp b/src/gfx_2d.cpp index 183c11017..337e395ff 100644 --- a/src/gfx_2d.cpp +++ b/src/gfx_2d.cpp @@ -487,7 +487,6 @@ void Graphics2D::drawThickLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, 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 @@ -755,8 +754,10 @@ void Graphics2D::isPixelMaskedByAnglesInit(int32_t off_x, int32_t off_y, int32_t 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); +// tan_sa = tanf(start_angle*(float)PI/180.0f); +// tan_ea = tanf(end_angle*(float)PI/180.0f); + tan_sa = tanf_tlu(start_angle); + tan_ea = tanf_tlu(end_angle); } } @@ -1383,21 +1384,67 @@ void Graphics2D::drawNTicks(int16_t cx, int16_t cy, int16_t r1, int16_t r2, int1 */ 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); - } + const float deltaAngle = 360.0f / nTicks; + for (int h = nTicks-1; h >= 0; --h) { + if (h % skip_every_nth != 0 || skip_every_nth > 360) + drawTickAA(cx, cy, r1, r2, (int16_t) (h * deltaAngle), color); } } +/** + * Draw an anti aliased 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::drawArcAA(int16_t cx, int16_t cy, int16_t start, int16_t stop, int16_t radius, int16_t lineRadius, + uint16_t color, LINE_END_OPT eoa) { + + if (start == -1 && stop == -1) + drawCircleAA(cx, cy, radius+lineRadius, 2*lineRadius, color, start, stop); + + if (eoa == ROUND_END) { +// int x = cx + cosf(start*PI/180) * radius; +// int y = cy - sinf(start*PI/180) * radius; + int x = cx + cosf_tlu(start) * radius; + int y = cy - sinf_tlu(start) * radius; + drawCircleAA(x, y, lineRadius, 0, color); + +// x = cy + cosf(stop*PI/180) * radius; +// y = cy - sinf(stop*PI/180) * radius; + x = cy + cosf_tlu(stop) * radius; + y = cy - sinf_tlu(stop) * radius; + drawCircleAA(x, y, lineRadius, 0, color); + } + + while (start < 0) + start += 360; + while (start > 360) + start -= 360; + while (stop < 0) + stop += 360; + while (stop > 360) + stop -= 360; + + if (start <= stop) { + drawCircleAA(cx, cy, radius+lineRadius, 2*lineRadius, color, start, stop); + return; + } else if (stop < start) { + drawCircleAA(cx, cy, radius+lineRadius, 2*lineRadius, color, start, 360); + drawCircleAA(cx, cy, radius+lineRadius, 2*lineRadius, color, 0, stop); + return; + } + +} /** * Draw an arc * @@ -1423,7 +1470,7 @@ void Graphics2D::drawArc(int16_t cx, int16_t cy, float start, float stop, int16_ int32_t y = rpy(cy, radius, alpha); if (old_x != x || old_y != y) { if (anti_alias) - fillCircleAA(x, y, lineRadius, color); + fillCircleAA(x, y, lineRadius, color); // this is awfull and slow use drawArcAA instead else fillCircle(x, y, lineRadius, color); } diff --git a/src/hal/power.cpp b/src/hal/power.cpp index 3aaacd109..5dcba4e7a 100644 --- a/src/hal/power.cpp +++ b/src/hal/power.cpp @@ -66,7 +66,7 @@ static inline uint8_t asigmoidal(uint16_t voltage, uint16_t minVoltage, uint16_t */ static inline uint8_t linear(uint16_t voltage, uint16_t minVoltage, uint16_t maxVoltage) { int volt_diff = maxVoltage - minVoltage; - if ( volt_diff <= 0) + if (volt_diff <= 0) volt_diff = 1; return (unsigned long)(voltage - minVoltage) * 100 / volt_diff; diff --git a/src/hal/time.cpp b/src/hal/time.cpp index d0ce9cb71..0c41f8582 100644 --- a/src/hal/time.cpp +++ b/src/hal/time.cpp @@ -85,6 +85,7 @@ void OswHal::updateTimezoneOffsets() { } catch(const std::logic_error& e) { // Well, we tried... } + // Okay, either we have no time provider or it failed to provide a timezone offset -> ask all remaining time providers to resolve it! if(!updated) { for(auto& d : *OswTimeProvider::getAllTimeDevices()) { diff --git a/src/main.cpp b/src/main.cpp index c9761e063..c8e816aff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,6 +69,7 @@ #include "./apps/watchfaces/OswAppWatchfaceMonotimer.h" #include "./apps/watchfaces/OswAppWatchfaceNumerals.h" #include "./apps/watchfaces/OswAppWatchfaceFitnessAnalog.h" +#include "./apps/watchfaces/OswAppWatchfaceZwilight.h" #if OSW_PLATFORM_ENVIRONMENT_MAGNETOMETER == 1 && OSW_PLATFORM_HARDWARE_QMC5883L == 1 #include "./apps/_experiments/magnetometer_calibrate.h" #endif @@ -175,6 +176,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) { @@ -347,7 +349,7 @@ void loop() { main_mainDrawer.registerApp(LANG_GAMES, new OswAppV2Compat("osw.game.brick", "Brick Breaker", gameBrickBreaker, true, brickbreaker_png)); #endif -#ifdef OSW_FEATURE_LUA +#if defined(OSW_FEATURE_LUA) && defined(EXAMPLE_LUA) static OswLuaApp luaApp("stopwatch.lua"); main_mainDrawer.registerApp("LUA", new OswAppV2Compat("osw.lua", "Demo", luaApp)); #endif diff --git a/src/osw_config_keys.cpp b/src/osw_config_keys.cpp index 7d0ef86c7..17655c906 100644 --- a/src/osw_config_keys.cpp +++ b/src/osw_config_keys.cpp @@ -17,6 +17,7 @@ #include "apps/watchfaces/OswAppWatchfaceMonotimer.h" #include "apps/watchfaces/OswAppWatchfaceNumerals.h" #include "apps/watchfaces/OswAppWatchfaceFitnessAnalog.h" +#include "apps/watchfaces/OswAppWatchfaceZwilight.h" /** * !!!WARNING!!! @@ -65,7 +66,8 @@ OswConfigKeyDropDown settingDisplayDefaultWatchface("n", "Display", "Default Wat OswAppWatchfaceBinary::APP_ID, OswAppWatchfaceMonotimer::APP_ID, OswAppWatchfaceNumerals::APP_ID, - OswAppWatchfaceFitnessAnalog::APP_ID + OswAppWatchfaceFitnessAnalog::APP_ID, + OswAppWatchfaceZwilight::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 diff --git a/src/osw_lua.cpp b/src/osw_lua.cpp index 4c0ca58e8..af4a5bac9 100644 --- a/src/osw_lua.cpp +++ b/src/osw_lua.cpp @@ -4,9 +4,10 @@ ** See Copyright Notice in lua.h */ - #ifdef OSW_FEATURE_LUA +#include "Arduino.h" + #define linit_c #define LUA_LIB @@ -62,13 +63,46 @@ static const luaL_Reg loadedlibs[] = { {NULL, NULL} }; - +/* LUALIB_API void luaL_openlibs (lua_State* L) { const luaL_Reg* lib; - /* "require" functions from 'loadedlibs' and set results to global table */ + // "require" functions from 'loadedlibs' and set results to global table for (lib = loadedlibs; lib->func; lib++) { luaL_requiref(L, lib->name, lib->func, 1); - lua_pop(L, 1); /* remove lib */ + lua_pop(L, 1); // remove lib + } +} +*/ + +void printLuaStack(lua_State *L) { + int top = lua_gettop(L); + + Serial.println("From top to bottom, the lua stack is"); + for (unsigned index = top; index > 0; index--) + { + int type = lua_type(L, index); + switch (type) + { + // booleans + case LUA_TBOOLEAN: + Serial.printf("%s\n", lua_toboolean(L, index) ? "true" : "false"); + break; + + // numbers + case LUA_TNUMBER: + Serial.printf("%f\n", lua_tonumber(L, index)); + break; + + // strings + case LUA_TSTRING: + Serial.printf("%s\n", lua_tostring(L, index)); + break; + + // other + default: + Serial.printf("%s\n", lua_typename(L, type)); + break; + } } } #endif From 413683b0f677ea1f10eb410a800a0034328af077 Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Mon, 13 May 2024 19:59:19 +0200 Subject: [PATCH 08/12] Change font --- .../watchfaces/OswAppWatchfaceFitnessAnalog.h | 3 +++ include/gfx_2d.h | 4 +--- platformio.ini | 3 +-- src.lua/apps/twilight.lua | 21 ++++++++++++++++ .../OswAppWatchfaceFitnessAnalog.cpp | 22 +++++++---------- .../watchfaces/OswAppWatchfaceZwilight.cpp | 24 +++++++++++-------- src/gfx_2d.cpp | 1 + src/gfx_util.cpp | 11 --------- 8 files changed, 49 insertions(+), 40 deletions(-) create mode 100644 src.lua/apps/twilight.lua diff --git a/include/apps/watchfaces/OswAppWatchfaceFitnessAnalog.h b/include/apps/watchfaces/OswAppWatchfaceFitnessAnalog.h index 0613e3067..b0102e9f5 100644 --- a/include/apps/watchfaces/OswAppWatchfaceFitnessAnalog.h +++ b/include/apps/watchfaces/OswAppWatchfaceFitnessAnalog.h @@ -34,10 +34,13 @@ class OswAppWatchfaceFitnessAnalog : public OswAppV2 { private: time_t lastTime = 0; unsigned screen = 0; + unsigned long lastShortPressTime; + 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); + void drawFitnessFace(OswHal* hal, uint32_t hour, uint32_t minute, uint32_t second, bool afterNoon); #ifdef GIF_BG OswAppGifPlayer* bgGif = nullptr; diff --git a/include/gfx_2d.h b/include/gfx_2d.h index 0c24424a0..756f6a5c8 100644 --- a/include/gfx_2d.h +++ b/include/gfx_2d.h @@ -95,7 +95,7 @@ class Graphics2D { * @param y y axis coordinate * @param color color code of the pixel */ - void drawPixel(int32_t x, int32_t y, uint16_t color) { + void drawPixel(int32_t x, int32_t y, uint16_t color) { drawPixelClipped(x, y, color); } @@ -250,7 +250,6 @@ class Graphics2D { drawLineAA(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) @@ -317,7 +316,6 @@ class Graphics2D { void dim(uint8_t amount); - void drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source); void drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source, int16_t sourceOffsetX, diff --git a/platformio.ini b/platformio.ini index 134def122..ecbd86d84 100755 --- a/platformio.ini +++ b/platformio.ini @@ -54,7 +54,6 @@ build_flags = -fdata-sections -Wno-deprecated-declarations -fpermissive -; -O2 build_type = debug @@ -85,7 +84,7 @@ build_flags = -D OSW_FEATURE_LUA -D OSW_SERVICE_CONSOLE -D OSW_FEATURE_WIFI - -D LUA_C89_NUMBERS ; Required by OSW_FEATURE_LUA + ;-D LUA_C89_NUMBERS ; nor required by OSW_FEATURE_LUA any more -Wdouble-promotion -D MAKE_LIB diff --git a/src.lua/apps/twilight.lua b/src.lua/apps/twilight.lua new file mode 100644 index 000000000..65d66ea48 --- /dev/null +++ b/src.lua/apps/twilight.lua @@ -0,0 +1,21 @@ +SunTime = require("suntime") + +function setParams(latitude, longitude, timezone, altitude, year, month, day, dst) +--[[ + print("latitude:", latitude) + print("longitude:", longitude) + print("timezone:", timezone) + print("altitude", altitude) + print("year", year) + print("month", month) + print("day", day) + print("dst", dst) +]] + SunTime:setPosition("nixda", latitude, longitude, timezone, altitude, true) + SunTime:setDate(y, m, d, dst) + SunTime:calculateTimes() +end + +function getTimes(pos) + return SunTime.times[pos] +end diff --git a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp index 7da24d145..050c8c8c2 100644 --- a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp @@ -16,10 +16,6 @@ #define CENTER_X (DISP_W / 2) #define CENTER_Y (DISP_H / 2) -#ifdef GIF_BG -OswAppGifPlayer* bgGif = new OswAppGifPlayer(); -#endif - inline uint32_t OswAppWatchfaceFitnessAnalog::calculateDistance(uint32_t steps) { float userHeight = OswConfigAllKeys::configHeight.get(); float avgDist; @@ -243,27 +239,25 @@ void OswAppWatchfaceFitnessAnalog::onDraw() { bool afterNoon; hal->getLocalTime(&hour, &minute, &second, &afterNoon); - if (screen > 0 && millis() - lastShortPressTime > 5000) { - screen = 0; + if (this->screen > 0 && millis() - lastShortPressTime > 5000) { + this->screen = 0; } - if (screen == 0) { + if (this->screen == 0) { #if OSW_PLATFORM_ENVIRONMENT_ACCELEROMETER == 1 showFitnessTracking(hal); #endif drawWatchFace(hal, hour, minute, second, afterNoon); - } else if (screen == 1) { + } else if (this->screen == 1) { drawDateFace(hal, hour, minute, second, afterNoon); - } else if (screen == 2) { + } else if (this->screen == 2) { drawFitnessFace(hal, hour, minute, second, afterNoon); } else { - screen = 0; + this->screen = 0; } - - this->lastTime = time(nullptr); #ifndef OSW_EMULATOR @@ -281,8 +275,8 @@ void OswAppWatchfaceFitnessAnalog::onButton(Button id, bool up, OswAppV2::Button // Process short presses if (state == OswAppV2::ButtonStateNames::SHORT_PRESS) { - if (screen < 3) { - ++screen; + if (this->screen < 3) { + this->screen += 1; lastShortPressTime = millis(); } return; diff --git a/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp b/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp index adb86ca9e..880bf903d 100644 --- a/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp @@ -1,8 +1,10 @@ + #include #include #include #include "./apps/watchfaces/OswAppWatchfaceZwilight.h" +#include "fonts/FreeMonoBold9pt7b.h" #define CENTER_X (DISP_W / 2) #define CENTER_Y (DISP_H / 2) @@ -61,8 +63,6 @@ bool OswAppWatchfaceZwilight::getTwilights() { uint32_t timeZone_offset_h = timeZone / 3600; -printf("xxx timeZone %d\n", (int) timeZone); - // setParams(latitude, longitude, timezone, altitude, year, month, day, dst) lua_getglobal(luaState, "setParams"); @@ -110,7 +110,6 @@ printf("xxx timeZone %d\n", (int) timeZone); lua_pop(luaState, 11+2); */ - if (luaInitialStackSize != lua_gettop(luaState)) { OSW_LOG_E("Oh my god, the lua stack has grown from", luaInitialStackSize, "to", lua_gettop(luaState)); } @@ -142,6 +141,10 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day uint16_t night_color = rgb565(0, 50 * 0.3, 255 * 0.3); uint16_t sun_color = rgb565(255, 255, 10); + + hal->gfx()->setTextSize(1); + hal->gfx()->setFont(&FreeMonoBold9pt7b); + const char* weekday = hal->getLocalWeekday(); // night @@ -200,7 +203,7 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day // Day complication hal->gfx()->setTextColor(dimColor(ui->getForegroundColor(), 25)); - hal->gfx()->setTextSize(2); + hal->gfx()->setTextSize(1); hal->gfx()->setTextLeftAligned(); hal->gfx()->setTextCursor(120-30+2, 120-40); OswAppWatchfaceZwilight::displayWeekDay2(weekday); @@ -217,11 +220,11 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day uint32_t stepsTarget = OswConfigAllKeys::stepsPerDay.get(); #ifdef OSW_EMULATOR - steps = 1798; + steps = 6666; #endif - int16_t radius = 50; - int16_t lw = 3; + int16_t radius = 48; + int16_t lw = 4; // int16_t sa = 180+55; // int16_t ea = 180-55; int16_t sa = 360; @@ -248,9 +251,9 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day hal->gfx()->drawCircleAA(x, y, lw, 0, color); hal->gfx()->setTextColor(day_color); - hal->gfx()->setTextSize(1); hal->gfx()->setTextCenterAligned(); - hal->gfx()->setTextCursor(CENTER_X, CENTER_Y + radius + 20); + hal->gfx()->setTextCursor(CENTER_X, CENTER_Y + radius + 22); + hal->gfx()->print(steps); } #endif @@ -276,7 +279,6 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day // Seconds hal->gfx()->fillCircleAA(CENTER_X, CENTER_Y, 6, ui->getDangerColor()); hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, -16, clock_r - 5, 360 / 60 * second, 3, ui->getDangerColor(), true); - } void OswAppWatchfaceZwilight::onStart() { @@ -347,6 +349,8 @@ void OswAppWatchfaceZwilight::onButton(Button id, bool up, OswAppV2::ButtonState void OswAppWatchfaceZwilight::onStop() { OswAppV2::onStop(); + + hal->gfx()->clearFont(); } const char* OswAppWatchfaceZwilight::getAppId() { diff --git a/src/gfx_2d.cpp b/src/gfx_2d.cpp index 337e395ff..79451ffbb 100644 --- a/src/gfx_2d.cpp +++ b/src/gfx_2d.cpp @@ -1441,6 +1441,7 @@ void Graphics2D::drawArcAA(int16_t cx, int16_t cy, int16_t start, int16_t stop, } else if (stop < start) { drawCircleAA(cx, cy, radius+lineRadius, 2*lineRadius, color, start, 360); drawCircleAA(cx, cy, radius+lineRadius, 2*lineRadius, color, 0, stop); + drawLine(cx+radius-lineRadius+1, cy, cx+radius+lineRadius, cy, color); return; } diff --git a/src/gfx_util.cpp b/src/gfx_util.cpp index ed2390532..7316e41d3 100644 --- a/src/gfx_util.cpp +++ b/src/gfx_util.cpp @@ -21,17 +21,6 @@ uint16_t blend(uint16_t target, uint16_t source, uint8_t alpha) { return rgb565(r, g, b); } -// 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); -} - // ToDo check if this function is faster /** * Fast RGB565 pixel blending From 0c0dd80d03bd9f1c558f20cb89d55a042cd80fbe Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Tue, 21 May 2024 21:17:23 +0200 Subject: [PATCH 09/12] Bug with line endings --- src/gfx_2d.cpp | 149 +++++++++++++++++++++++++------------------------ 1 file changed, 77 insertions(+), 72 deletions(-) diff --git a/src/gfx_2d.cpp b/src/gfx_2d.cpp index 79451ffbb..3844b6b5f 100644 --- a/src/gfx_2d.cpp +++ b/src/gfx_2d.cpp @@ -477,79 +477,84 @@ void Graphics2D::drawThickLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, 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; - - - /* - 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); - + if (dx == 0) { + fillBoxHV(x0 - w2, y0, x1 - w2 + w, y1, color); + } + else if (dy == 0) { + fillBoxHV(x0, y0 - w2, x1, y1 - w2 + w, color); + } + else { + n = 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; + + /* + 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); + } + + // Draw the ending and the end ;) 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; + 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; } /* From 5ed3f054bacb941cca82c8155c8c688a61e0db72 Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Sat, 25 May 2024 18:02:54 +0200 Subject: [PATCH 10/12] Rebasing --- include/osw_pins.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/include/osw_pins.h b/include/osw_pins.h index 28c74f3e6..e3fcdc19e 100644 --- a/include/osw_pins.h +++ b/include/osw_pins.h @@ -57,12 +57,3 @@ #else #define BTN_STATE_ARRAY {LOW, HIGH, HIGH} #endif - -// button order is: select, up, down -#define BTN_NUMBER 3 -#define BTN_POSX_ARRAY {24, 208, 208} -#define BTN_POSY_ARRAY {190, 44, 190} -#define BTN_POS_ISTOP_ARRAY {false, true, false} -#define BTN_POS_ISLEFT_ARRAY {true, false, false} -#define BTN_NAME_ARRAY {"SELECT", "UP", "DOWN"} -#define BTN_PIN_ARRAY {BTN_1, BTN_2, BTN_3} From 69151fbd3284c5aae8f7e9dde4af79329f22337c Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Sat, 25 May 2024 18:47:04 +0200 Subject: [PATCH 11/12] Add brightness control --- include/apps/watchfaces/OswAppWatchfaceZwilight.h | 2 -- src/apps/watchfaces/OswAppWatchfaceZwilight.cpp | 11 ++++++++--- src/hal/buttons.cpp | 12 ++++++------ src/main.cpp | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/include/apps/watchfaces/OswAppWatchfaceZwilight.h b/include/apps/watchfaces/OswAppWatchfaceZwilight.h index 4dd6d9687..a3a370ae6 100644 --- a/include/apps/watchfaces/OswAppWatchfaceZwilight.h +++ b/include/apps/watchfaces/OswAppWatchfaceZwilight.h @@ -34,8 +34,6 @@ enum SUNTIMES MIDNIGHT }; - - class OswAppWatchfaceZwilight : public OswAppV2 { public: constexpr static const char* APP_ID = "osw.wf.zwilight"; diff --git a/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp b/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp index 880bf903d..9916cbec8 100644 --- a/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp @@ -3,7 +3,9 @@ #include #include +#include "apps/watchfaces/OswAppWatchface.h" #include "./apps/watchfaces/OswAppWatchfaceZwilight.h" + #include "fonts/FreeMonoBold9pt7b.h" #define CENTER_X (DISP_W / 2) @@ -283,6 +285,7 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day void OswAppWatchfaceZwilight::onStart() { OswAppV2::onStart(); + OswAppWatchface::addButtonDefaults(this->knownButtonStates); this->viewFlags = OswAppV2::ViewFlags::NO_OVERLAYS; // no overlay for this watchface @@ -301,8 +304,7 @@ void OswAppWatchfaceZwilight::onStart() { #else unsigned long ms_for_onDraw = millis()-old_millis; #endif - OSW_LOG_I("Time to start ", ms_for_onDraw, " ms"); - + OSW_LOG_D("Time to start ", ms_for_onDraw, " ms"); } void OswAppWatchfaceZwilight::onLoop() { @@ -338,15 +340,18 @@ void OswAppWatchfaceZwilight::onDraw() { #else unsigned long ms_for_onDraw = millis()-old_millis; #endif - OSW_LOG_I("Time to draw ", ms_for_onDraw, " ms"); + OSW_LOG_D("Time to draw ", ms_for_onDraw, " ms"); this->lastTime = time(nullptr); } void OswAppWatchfaceZwilight::onButton(Button id, bool up, OswAppV2::ButtonStateNames state) { OswAppV2::onButton(id, up, state); + if(OswAppWatchface::onButtonDefaults(*this, id, up, state)) + return; // if the button was handled by the defaults, we are done here } + void OswAppWatchfaceZwilight::onStop() { OswAppV2::onStop(); diff --git a/src/hal/buttons.cpp b/src/hal/buttons.cpp index dce66ad52..86d62d49d 100644 --- a/src/hal/buttons.cpp +++ b/src/hal/buttons.cpp @@ -30,15 +30,15 @@ void OswHal::setupButtons(void) { // rtc_gpio_deinit(GPIO_NUM_0); // rtc_gpio_deinit(GPIO_NUM_10); // rtc_gpio_deinit(GPIO_NUM_13); - pinMode(BTN_1, INPUT); - pinMode(BTN_2, INPUT); - pinMode(BTN_3, INPUT); + pinMode(BUTTON_SELECT, INPUT); + pinMode(BUTTON_DOWN, INPUT); + pinMode(BUTTON_UP, INPUT); #ifndef OSW_EMULATOR // raise speed to maximum if a button is pressed - attachInterrupt(BTN_1, ISR_BTN1, CHANGE); - attachInterrupt(BTN_2, ISR_BTN2, CHANGE); - attachInterrupt(BTN_3, ISR_BTN3, CHANGE); + attachInterrupt(BUTTON_SELECT, ISR_BTN1, CHANGE); + attachInterrupt(BUTTON_DOWN, ISR_BTN2, CHANGE); + attachInterrupt(BUTTON_UP, ISR_BTN3, CHANGE); #endif #if defined(GPS_EDITION) || defined(GPS_EDITION_ROTATED) diff --git a/src/main.cpp b/src/main.cpp index c8e816aff..dbccdee32 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -253,7 +253,7 @@ void loop() { if (wifiDisabled) setCpuFrequencyMhz(10); - + } catch(const std::exception& e) { OSW_LOG_E("CRITICAL ERROR AT APP: ", e.what()); sleep(_MAIN_CRASH_SLEEP); From 74ee0e261deac7f911e435620effadc8c685a6a7 Mon Sep 17 00:00:00 2001 From: zwim <36999612+zwim@users.noreply.github.com> Date: Mon, 27 May 2024 16:07:16 +0200 Subject: [PATCH 12/12] Simplify API --- include/gfx_2d.h | 201 +++++++++++------- include/math_angles.h | 2 +- src/apps/clock/OswAppAlarm.cpp | 4 +- src/apps/clock/OswAppTimer.cpp | 12 +- src/apps/games/brick_breaker.cpp | 6 +- src/apps/games/snake_game.cpp | 6 +- src/apps/tools/OswAppKcalStats.cpp | 4 +- src/apps/watchfaces/OswAppWatchface.cpp | 6 +- .../OswAppWatchfaceFitnessAnalog.cpp | 4 +- .../watchfaces/OswAppWatchfaceZwilight.cpp | 76 +++---- src/gfx_2d.cpp | 110 +++++----- 11 files changed, 240 insertions(+), 191 deletions(-) diff --git a/include/gfx_2d.h b/include/gfx_2d.h index 756f6a5c8..2ac9cb812 100644 --- a/include/gfx_2d.h +++ b/include/gfx_2d.h @@ -6,8 +6,8 @@ #include "gfx_util.h" #include "math_angles.h" -enum CIRC_OPT { DRAW_UPPER_RIGHT, DRAW_UPPER_LEFT, DRAW_LOWER_RIGHT, DRAW_LOWER_LEFT, DRAW_ALL }; -enum LINE_END_OPT { STRAIGHT_END, ROUND_END, TRIANGLE_END }; +enum class CIRC_OPT { DRAW_UPPER_RIGHT, DRAW_UPPER_LEFT, DRAW_LOWER_RIGHT, DRAW_LOWER_LEFT, DRAW_ALL }; +enum class LINE_END_OPT { NO_END, STRAIGHT_END, ROUND_END, TRIANGLE_END }; class DrawPixel { public: @@ -129,44 +129,27 @@ class Graphics2D { 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 + * Draw line (anti-)aliased from (x1,y1) to (x2,y2) point with color * * @param x1 * @param y1 * @param x2 * @param y2 * @param color + * @param anti_aliased if true */ + void drawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t color, bool anti_aliased) { + if (anti_aliased) + drawLineAA(x1, y1, x2, y2, color); + else + drawLine(x1, y1, x2, y2, color); + } void drawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint16_t color); - - /** - * 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 - * - * 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 drawThickLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint8_t radius, uint16_t color, bool highQuality = false); - - /** - * @brief Draw an anti-aliased line between (x1,y1) and (x2,y2) with a thicknes of line_width and with specific color + * @brief Draw a line between (x1,y1) and (x2,y2) with a thickness of line_width and with specific color + * high or low quality (with round end), anti_aliased (with different endings) * * @param x1 x-axis of the start point * @param y1 y-axis of the start point @@ -174,8 +157,19 @@ class Graphics2D { * @param y2 y-axis of the end point * @param line_width thickness of the line * @param color color code use to draw the line. + * @param quality true if high quality + * @param eol if a line ending is given, anti alias the whole 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); + void drawThickLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t line_width, const uint16_t color, + bool quality, LINE_END_OPT eol = LINE_END_OPT::NO_END) { + + if (quality && eol != LINE_END_OPT::NO_END) + drawThickLineAA(x0, y0, x1, y1, line_width, color, eol); + else + drawThickLine(x0, y0, x1, y1, line_width, color, quality, false); + } + void drawThickLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint8_t radius, uint16_t color, bool highQuality = false, bool no_alias = false); + 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 = LINE_END_OPT::ROUND_END); 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); @@ -195,68 +189,127 @@ class Graphics2D { * "Complex" Stuff: */ - void _drawCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, CIRC_OPT option); - /** - * @brief Draw a circle + * @brief Draw an (anti-aliased) circle with thickness 1 * * @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 r radius of the circle + * @param bw thickness of th circle * @param color color code of the circle - * @param option + * @param anti_aliased if true */ - void drawCircle(int16_t x0, int16_t y0, int16_t rad, uint16_t color, CIRC_OPT option = DRAW_ALL); + void drawCircle(int16_t x0, int16_t y0, int16_t rad, uint16_t color, bool anti_aliased) { + if (anti_aliased) + drawCircleAA(x0, y0, rad, 1, color); + else + drawCircle(x0, y0, rad, color); + } + void _drawCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, CIRC_OPT option); + void drawCircleQuarter(int16_t x0, int16_t y0, int16_t rad, uint16_t color, CIRC_OPT option); + void drawCircle(int16_t x0, int16_t y0, int16_t rad, uint16_t color) { + drawCircleQuarter(x0, y0, rad, color, CIRC_OPT::DRAW_ALL); + } + 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); /** - * @brief Draw an anti-aliased circle with thicknes + * @brief Draw a filled (anti-aliased) circle * * @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 + * @param anti_aliased if true */ - 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); - + void fillCircle(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color, bool anti_aliased) { + if (anti_aliased) + fillCircleAA(x0, y0, rad, color); + else + fillCircle(x0, y0, rad, color); + } + void _fillCircleQuarter(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, CIRC_OPT option = CIRC_OPT::DRAW_ALL); + void fillCircleQuarter(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color, CIRC_OPT option); + void fillCircle(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color) { + fillCircleQuarter(x0, y0, rad, color, CIRC_OPT::DRAW_ALL); + } 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); + drawCircleAA(off_x, off_y, r, 0, color); } - 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); - - 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); + /** + * @brief Draw a filled (anti-aliased) ring with thickness + * + * @param x0 x-axis of the center of the ring + * @param y0 y-axis of the center of the ring + * @param r inner radius of the ring + * @param bw thickness of th ring + * @param color color code + * @param anti_aliased if true + */ + void drawRing(int16_t x0, int16_t y0, int16_t r, int16_t thickness, uint16_t color, bool anti_aliased = false) { + if (anti_aliased) + drawCircleAA(x0, y0, r, thickness, color); + else { + for (int16_t i = 0; i <= thickness; ++i) + drawCircle(x0, y0, r+i, color, false); + } + } - 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 _drawEllipseQuarter(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, CIRC_OPT option = CIRC_OPT::DRAW_ALL); + void drawEllipseQuarter(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color, CIRC_OPT option ); + void drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color) { + drawEllipseQuarter(x0, y0, rx, ry, color, CIRC_OPT::DRAW_ALL); } - 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 _fillEllipseQuarter(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, CIRC_OPT option = CIRC_OPT::DRAW_ALL); + void fillEllipseQuarter(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color, CIRC_OPT option); + void fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, uint16_t color) { + fillEllipseQuarter(x0, y0, rx, ry, color, CIRC_OPT::DRAW_ALL); } - 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 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_aliased = true); + + void drawArcAA(int16_t cx, int16_t cy, int16_t start, int16_t stop, int16_t radius, int16_t lineRadius, + uint16_t color, LINE_END_OPT eoa = LINE_END_OPT::ROUND_END); + + 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); + + inline void drawTick(int16_t cx, int16_t cy, int16_t r1, int16_t r2, float angle, uint16_t color, bool anti_aliased = false) { + drawLine(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), color, anti_aliased); } - 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); + inline void drawTick(int16_t cx, int16_t cy, int16_t r1, int16_t r2, int angle, uint16_t color, bool anti_aliased = false) { + drawLine(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), color, anti_aliased); } 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) + bool anti_aliased = false, LINE_END_OPT eol = LINE_END_OPT::ROUND_END) { + if (anti_aliased) 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); + drawThickLine(rpx(cx, r1, angle), rpy(cy, r1, angle), rpx(cx, r2, angle), rpy(cy, r2, angle), radius, color, false); + } + + /** + * @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 + * @param anti_aliased do the anti alias magic + */ + 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, bool anti_aliased) { + if (anti_aliased) + drawNTicksAA(cx, cy, r1, r2, nTicks, color, skip_every_nth); + else + drawNTicks(cx, cy, r1, r2, nTicks, color, skip_every_nth); } 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); @@ -273,11 +326,11 @@ class Graphics2D { * @param r2 * @param color color code */ - 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) + inline void drawHourTicks(int16_t cx, int16_t cy, int16_t r1, int16_t r2, uint16_t color, bool anti_aliased = true) { + if (anti_aliased) drawNTicksAA(cx, cy, r1, r2, 12, color); else - drawNTicks(cx, cy, r1, r2, 12, color); + drawNTicks(cx, cy, r1, r2, 12, color, 361, false); } /** @@ -291,31 +344,19 @@ class Graphics2D { * @param r2 * @param color color code */ - 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) + inline void drawMinuteTicks(int16_t cx, int16_t cy, int16_t r1, int16_t r2, uint16_t color, bool anti_aliased = true) { + if (anti_aliased) drawNTicksAA(cx, cy, r1, r2, 60, color, 5); else - drawNTicks(cx, cy, r1, r2, 60, color, 5); + drawNTicks(cx, cy, r1, r2, 60, color, 5, false); } - 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 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); - - void drawArcAA(int16_t cx, int16_t cy, int16_t start, int16_t stop, int16_t radius, int16_t lineRadius, - uint16_t color, LINE_END_OPT eoa = ROUND_END); - - 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); void fill(uint16_t color); void dim(uint8_t amount); - void drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source); void drawGraphics2D(int16_t offsetX, int16_t offsetY, Graphics2D* source, int16_t sourceOffsetX, diff --git a/include/math_angles.h b/include/math_angles.h index b3c492b49..a1bb1911b 100644 --- a/include/math_angles.h +++ b/include/math_angles.h @@ -48,7 +48,7 @@ inline int32_t rpx(int32_t cx, int32_t r, float deg) { // integer version for deg inline int32_t rpx(int32_t cx, int32_t r, int32_t deg) { deg -= 90; - deg -= 90; // to use sine + deg -= 90; // to use sinus instead of cosinus while (deg < 0) deg += 360; while (deg > 360) diff --git a/src/apps/clock/OswAppAlarm.cpp b/src/apps/clock/OswAppAlarm.cpp index 5271655ba..1c41bd082 100644 --- a/src/apps/clock/OswAppAlarm.cpp +++ b/src/apps/clock/OswAppAlarm.cpp @@ -157,8 +157,8 @@ void drawTrashIcon(uint16_t color) { // Trash lid hal->gfx()->drawHLine(leftX - 2, topY, boxWidth + 4, color); hal->gfx()->drawHLine(leftX, topY - 2, boxWidth, color); - hal->gfx()->drawCircle(leftX, topY, 2, color, CIRC_OPT::DRAW_UPPER_LEFT); - hal->gfx()->drawCircle(leftX + boxWidth, topY, 2, color, CIRC_OPT::DRAW_UPPER_RIGHT); + hal->gfx()->drawCircleQuarter(leftX, topY, 2, color, CIRC_OPT::DRAW_UPPER_LEFT); + hal->gfx()->drawCircleQuarter(leftX + boxWidth, topY, 2, color, CIRC_OPT::DRAW_UPPER_RIGHT); // Trash lid handle hal->gfx()->drawFrame(leftX + 7, topY - 4, 6, 2, color); diff --git a/src/apps/clock/OswAppTimer.cpp b/src/apps/clock/OswAppTimer.cpp index 670a19ad7..551ee7128 100644 --- a/src/apps/clock/OswAppTimer.cpp +++ b/src/apps/clock/OswAppTimer.cpp @@ -136,13 +136,13 @@ void drawTimerIcon(uint16_t color) { // Clock arc const auto colorGreen = ui->getSuccessColor(); - hal->gfx()->drawCircle(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_UPPER_LEFT); - hal->gfx()->drawCircle(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_LOWER_LEFT); - hal->gfx()->drawCircle(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_LOWER_RIGHT); + hal->gfx()->drawCircleQuarter(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_UPPER_LEFT); + hal->gfx()->drawCircleQuarter(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_LOWER_LEFT); + hal->gfx()->drawCircleQuarter(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_LOWER_RIGHT); - hal->gfx()->fillCircle(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_UPPER_LEFT); - hal->gfx()->fillCircle(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_LOWER_LEFT); - hal->gfx()->fillCircle(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_LOWER_RIGHT); + hal->gfx()->fillCircleQuarter(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_UPPER_LEFT); + hal->gfx()->fillCircleQuarter(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_LOWER_LEFT); + hal->gfx()->fillCircleQuarter(centerX, centerY, radius - 4, colorGreen, CIRC_OPT::DRAW_LOWER_RIGHT); // Clock "buttons" hal->gfx()->drawThickLine(centerX - radius / 4, centerY - radius - 3, centerX + radius / 4, centerY - radius - 3, 1, color); diff --git a/src/apps/games/brick_breaker.cpp b/src/apps/games/brick_breaker.cpp index e177c5071..c0f1cab3c 100644 --- a/src/apps/games/brick_breaker.cpp +++ b/src/apps/games/brick_breaker.cpp @@ -50,14 +50,16 @@ void OswAppBrickBreaker::drawScore() { void OswAppBrickBreaker::drawButtonHints() { OswHal* hal = OswHal::getInstance(); const int diameter = 4; - hal->getCanvas()->drawArc(200, 240 - 48 + diameter, diameter + 1, diameter, 270, 360, ui->getDangerColor()); + hal->getCanvas()->drawArc(200, 240 - 48 + diameter, 270, 360, 10, diameter, 1, ui->getDangerColor()); + hal->getCanvas()->drawArc(200, 240 - 48 + diameter, 0, 90, 10, diameter, 1, ui->getDangerColor()); hal->getCanvas()->drawTriangle( // diameter + 200 - 2, 240 + diameter - 48, // diameter + 200 + 2, 240 + diameter - 48, // diameter + 200, 240 + diameter - 48 + 2, // ui->getDangerColor()); - hal->getCanvas()->drawArc(240 - 200, 240 - 48 + diameter, diameter + 1, diameter, 180, 270, ui->getDangerColor()); + hal->getCanvas()->drawArc(240 - 200, 240 - 48 + diameter, 270, 360, 10, diameter, 1, ui->getDangerColor()); + hal->getCanvas()->drawArc(240 - 200, 240 - 48 + diameter, 0, 90, 10, diameter, 1, ui->getDangerColor()); hal->getCanvas()->drawTriangle( // 40 - 2 - diameter, 240 + diameter - 48, // 40 + 2 - diameter, 240 + diameter - 48, // diff --git a/src/apps/games/snake_game.cpp b/src/apps/games/snake_game.cpp index 6e03f1856..52a7b31eb 100644 --- a/src/apps/games/snake_game.cpp +++ b/src/apps/games/snake_game.cpp @@ -148,14 +148,16 @@ void OswAppSnakeGame::drawScore() { void OswAppSnakeGame::drawButtonHints() { OswHal* hal = OswHal::getInstance(); const int diameter = 4; - hal->getCanvas()->drawArc(200, 240 - 48 + diameter, diameter + 1, diameter, 270, 360, ui->getDangerColor()); + hal->getCanvas()->drawArc(200, 240 - 48 + diameter, 270, 360, 10, diameter, 1, ui->getDangerColor()); + hal->getCanvas()->drawArc(200, 240 - 48 + diameter, 0, 90, 10, diameter, 1, ui->getDangerColor()); hal->getCanvas()->drawTriangle( // diameter + 200 - 2, 240 + diameter - 48, // diameter + 200 + 2, 240 + diameter - 48, // diameter + 200, 240 + diameter - 48 + 2, // ui->getDangerColor()); - hal->getCanvas()->drawArc(240 - 200, 240 - 48 + diameter, diameter + 1, diameter, 180, 270, ui->getDangerColor()); + hal->getCanvas()->drawArc(240 - 200, 240 - 48 + diameter, 270, 360, 10, diameter, 1, ui->getDangerColor()); + hal->getCanvas()->drawArc(240 - 200, 240 - 48 + diameter, 0, 90, 10, diameter, 1, ui->getDangerColor()); hal->getCanvas()->drawTriangle( // 40 - 2 - diameter, 240 + diameter - 48, // 40 + 2 - diameter, 240 + diameter - 48, // diff --git a/src/apps/tools/OswAppKcalStats.cpp b/src/apps/tools/OswAppKcalStats.cpp index 53889a15a..fb0824b2a 100644 --- a/src/apps/tools/OswAppKcalStats.cpp +++ b/src/apps/tools/OswAppKcalStats.cpp @@ -48,11 +48,11 @@ void OswAppKcalStats::drawCurvedChart() { } 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 + 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.5f,ui->getSuccessColor() ); + hal->gfx()->fillCircle(x2, y2, 2.5f,ui->getSuccessColor()); } } } diff --git a/src/apps/watchfaces/OswAppWatchface.cpp b/src/apps/watchfaces/OswAppWatchface.cpp index bdca73d5c..fc8774e11 100644 --- a/src/apps/watchfaces/OswAppWatchface.cpp +++ b/src/apps/watchfaces/OswAppWatchface.cpp @@ -107,15 +107,15 @@ void OswAppWatchface::drawWatch() { hal->getDualTime(&dualHour, &dualMinute, &dualSecond); // dual-hours - 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, 0, 16, (int)(360.0f / 12.0f * (dualHour + dualMinute / 60.0f)), 2, ui->getBackgroundDimmedColor(), true, LINE_END_OPT::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(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, 0, (int)16, (int)(360.0f / 12.0f * (hour + minute / 60.0f)), 1, ui->getForegroundColor(), true, LINE_END_OPT::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(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, 0, 16, (int)(360.0f / 60.0f * (minute + second / 60.0f)), 1, ui->getForegroundColor(), true, LINE_END_OPT::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 diff --git a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp index 050c8c8c2..c426e0353 100644 --- a/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceFitnessAnalog.cpp @@ -93,11 +93,11 @@ void OswAppWatchfaceFitnessAnalog::drawWatchFace(OswHal* hal, uint32_t hour, uin hal->gfx()->drawHourTicks(CENTER_X, CENTER_Y, 117, 106, 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, 0, 16, (int)(360.0f / 12.0f * (hour + minute / 60.0f)), 3, ui->getForegroundColor(), true, LINE_END_OPT::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, 0, 16, (int)(360.0f / 60.0f * (minute + second / 60.0f)), 3, ui->getForegroundColor(), true, LINE_END_OPT::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 diff --git a/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp b/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp index 9916cbec8..b8277529e 100644 --- a/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp +++ b/src/apps/watchfaces/OswAppWatchfaceZwilight.cpp @@ -150,27 +150,34 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day const char* weekday = hal->getLocalWeekday(); // night - hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[ASTRONOMIC_DAWN]*(360/24), 270 - sunTimes[ASTRONOMIC_DUSK]*(360/24), 103, 5, night_color, STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[ASTRONOMIC_DAWN]*(360/24), 270 - sunTimes[ASTRONOMIC_DUSK]*(360/24), 103, 5, night_color, + LINE_END_OPT::STRAIGHT_END); // astronomical - hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[NAUTIC_DAWN]*(360/24), 270 - sunTimes[ASTRONOMIC_DAWN]*(360/24), 103, 5, astronomic_color, STRAIGHT_END); - hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[ASTRONOMIC_DUSK]*(360/24), 270 - sunTimes[NAUTIC_DUSK]*(360/24), 103, 5, astronomic_color, STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[NAUTIC_DAWN]*(360/24), 270 - sunTimes[ASTRONOMIC_DAWN]*(360/24), 103, 5, astronomic_color, + LINE_END_OPT::STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[ASTRONOMIC_DUSK]*(360/24), 270 - sunTimes[NAUTIC_DUSK]*(360/24), 103, 5, astronomic_color, + LINE_END_OPT::STRAIGHT_END); // nautical - hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[CIVIL_DAWN]*(360/24), 270 - sunTimes[NAUTIC_DAWN]*(360/24), 103, 5, nautic_color, STRAIGHT_END); - hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[NAUTIC_DUSK]*(360/24), 270 - sunTimes[CIVIL_DUSK]*(360/24), 103, 5, nautic_color, STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[CIVIL_DAWN]*(360/24), 270 - sunTimes[NAUTIC_DAWN]*(360/24), 103, 5, nautic_color, + LINE_END_OPT::STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[NAUTIC_DUSK]*(360/24), 270 - sunTimes[CIVIL_DUSK]*(360/24), 103, 5, nautic_color, + LINE_END_OPT::STRAIGHT_END); // civil - hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[SUN_RISE]*(360/24), 270 - sunTimes[CIVIL_DAWN]*(360/24), 103, 5, civil_color, STRAIGHT_END); - hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[CIVIL_DUSK]*(360/24), 270 - sunTimes[SUN_SET]*(360/24), 103, 5, civil_color, STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[SUN_RISE]*(360/24), 270 - sunTimes[CIVIL_DAWN]*(360/24), 103, 5, civil_color, + LINE_END_OPT::STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[CIVIL_DUSK]*(360/24), 270 - sunTimes[SUN_SET]*(360/24), 103, 5, civil_color, + LINE_END_OPT::STRAIGHT_END); // daytime - hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[SUN_SET]*(360/24), 270 - sunTimes[SUN_RISE]*(360/24), 103, 5, day_color, STRAIGHT_END); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, 270 - sunTimes[SUN_SET]*(360/24), 270 - sunTimes[SUN_RISE]*(360/24), 103, 5, day_color, + LINE_END_OPT::STRAIGHT_END); // outer indices - //hal->gfx()->drawNTicksAA(CENTER_X, CENTER_Y, 120, 110, 24*2, ui->getForegroundDimmedColor(), 2); - hal->gfx()->drawNTicksAA(CENTER_X, CENTER_Y, 120, 110, 24, ui->getForegroundDimmedColor()); + hal->gfx()->drawNTicks(CENTER_X, CENTER_Y, 120, 110, 24, ui->getForegroundDimmedColor(), 361, true); // Time indicator int16_t day_indicator_angle; @@ -179,8 +186,8 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day day_indicator_angle -= 360; while (day_indicator_angle < 0) day_indicator_angle += 360; - hal->gfx()->drawCircleAA(CENTER_X + 115*cosf_tlu(day_indicator_angle), CENTER_Y - 115*sinf_tlu(day_indicator_angle), - 5, 2, sun_color); + hal->gfx()->drawRing(CENTER_X + 115*cosf_tlu(day_indicator_angle), CENTER_Y - 115*sinf_tlu(day_indicator_angle), + 5, 2, sun_color, true); // sunTimes // Attention MIDNIGHT is for today; LAST_MIDNIGHT was for yesterday @@ -199,7 +206,7 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day y0 = CENTER_Y - r1*sinf_tlu(day_indicator_angle); x1 = CENTER_X + r2*cosf_tlu(day_indicator_angle); y1 = CENTER_Y - r2*sinf_tlu(day_indicator_angle); - hal->gfx()->drawLineAA(x0, y0, x1, y1, sun_color); + hal->gfx()->drawLine(x0, y0, x1, y1, sun_color, true); } // Day complication @@ -225,37 +232,29 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day steps = 6666; #endif + uint16_t color = day_color; + uint16_t dimmed_color = night_color; + int16_t radius = 48; int16_t lw = 4; -// int16_t sa = 180+55; -// int16_t ea = 180-55; int16_t sa = 360; int16_t ea = 180; + int16_t x = CENTER_X + cosf_tlu(ea) * radius; + int16_t y = CENTER_Y - sinf_tlu(ea) * radius; int16_t angle_val = sa - (sa-ea) * (float)min(steps, stepsTarget) / (float)stepsTarget; - uint16_t color = day_color; - uint16_t dimmed_color = night_color; - hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, radius + lw, lw*2, dimmed_color, ea, angle_val); - hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, radius + lw, lw*2, color, angle_val, sa); - - int x, y; - - x = CENTER_X + cosf_tlu(ea) * radius; - y = CENTER_Y - sinf_tlu(ea) * radius; - hal->gfx()->drawCircleAA(x, y, lw, 0, dimmed_color); - - x = CENTER_X + cosf_tlu(angle_val) * radius; - y = CENTER_Y - sinf_tlu(angle_val) * radius; - hal->gfx()->drawCircleAA(x, y, lw-1, 0, color); + // draw the dimmed part of the step counter + hal->gfx()->fillCircle(x, y, lw, dimmed_color, true); + hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, radius + lw, lw*2, dimmed_color, ea, angle_val); + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, ea, angle_val, radius, lw, dimmed_color, LINE_END_OPT::STRAIGHT_END); - x = CENTER_X + cosf_tlu(sa) * radius; - y = CENTER_Y - sinf_tlu(sa) * radius; - hal->gfx()->drawCircleAA(x, y, lw, 0, color); + // draw the highlighted part of the step counter + hal->gfx()->drawArcAA(CENTER_X, CENTER_Y, angle_val, sa, radius, lw, color, LINE_END_OPT::ROUND_END); hal->gfx()->setTextColor(day_color); hal->gfx()->setTextCenterAligned(); hal->gfx()->setTextCursor(CENTER_X, CENTER_Y + radius + 22); - + hal->gfx()->print(steps); } #endif @@ -265,21 +264,22 @@ void OswAppWatchfaceZwilight::drawWatch(int32_t year, int32_t month, int32_t day const int16_t clock_r = 95; // Indices - hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, clock_r, 5, rgb565(35, 35, 35)); - hal->gfx()->drawCircleAA(CENTER_X, CENTER_Y, clock_r - 5, 5, rgb565(30, 30, 30)); + hal->gfx()->drawRing(CENTER_X, CENTER_Y, clock_r, 5, rgb565(35, 35, 35), true); + hal->gfx()->drawRing(CENTER_X, CENTER_Y, clock_r - 5, 5, rgb565(30, 30, 30), true); hal->gfx()->drawMinuteTicks(CENTER_X, CENTER_Y, clock_r, clock_r - 5, ui->getForegroundDimmedColor(), true); hal->gfx()->drawHourTicks(CENTER_X, CENTER_Y, clock_r, clock_r - 10, 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, 0, 16, (int)(360.0f / 12.0f * (hour + minute / 60.0f)), 3, ui->getForegroundColor(), true, + LINE_END_OPT::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, 0, 16, (int)(360.0f / 60.0f * (minute + second / 60.0f)), 3, ui->getForegroundColor(), true, LINE_END_OPT::STRAIGHT_END); hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, 16, clock_r - 5, (int)(360.0f / 60.0f * (minute + second / 60.0f)), 7, ui->getForegroundColor(), true); // Seconds - hal->gfx()->fillCircleAA(CENTER_X, CENTER_Y, 6, ui->getDangerColor()); + hal->gfx()->fillCircle(CENTER_X, CENTER_Y, 6, ui->getDangerColor(), true); hal->gfx()->drawThickTick(CENTER_X, CENTER_Y, -16, clock_r - 5, 360 / 60 * second, 3, ui->getDangerColor(), true); } diff --git a/src/gfx_2d.cpp b/src/gfx_2d.cpp index 3844b6b5f..aa4248df2 100644 --- a/src/gfx_2d.cpp +++ b/src/gfx_2d.cpp @@ -340,10 +340,11 @@ void Graphics2D::drawLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, cons * @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 + * @param highQuality very clumsy and slow + * @param no_alias only used for the function signature */ 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 + bool highQuality, bool no_alias) { // see p3dt_gfx_2d_license.txt // see p3dt_gfx_2d_license.txt int32_t tmp; @@ -479,9 +480,11 @@ void Graphics2D::drawThickLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, if (dx == 0) { fillBoxHV(x0 - w2, y0, x1 - w2 + w, y1, color); + // Todo define xa, ya, xb, yb } else if (dy == 0) { fillBoxHV(x0, y0 - w2, x1, y1 - w2 + w, color); + // Todo define xa, ya, xb, yb } else { n = sqrtf(dx*dx + dy*dy) + 0.5f; @@ -532,15 +535,17 @@ void Graphics2D::drawThickLineAA(int32_t x0, int32_t y0, int32_t x1, int32_t y1, // Draw the ending and the end ;) switch (eol) { - case STRAIGHT_END: + case LINE_END_OPT::NO_END: + break; + case LINE_END_OPT::STRAIGHT_END: break; - case ROUND_END: { + case LINE_END_OPT::ROUND_END: { // circle at end fillCircleAA(x0, y0, (line_width)/2, color); fillCircleAA(x1, y1, (line_width)/2, color); break; } - case TRIANGLE_END: { + case LINE_END_OPT::TRIANGLE_END: { // still experimental and not finished int32_t x, y; int32_t xo = sx * line_width * dx / n; @@ -689,22 +694,22 @@ void Graphics2D::drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1 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) { + if (option == CIRC_OPT::DRAW_UPPER_RIGHT || option == CIRC_OPT::DRAW_ALL) { drawPixel(x0 + x, y0 - y, color); drawPixel(x0 + y, y0 - x, color); } - if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_UPPER_LEFT || option == CIRC_OPT::DRAW_ALL) { drawPixel(x0 - x, y0 - y, color); drawPixel(x0 - y, y0 - x, color); } - if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_LOWER_RIGHT || option == CIRC_OPT::DRAW_ALL) { drawPixel(x0 + x, y0 + y, color); drawPixel(x0 + y, y0 + x, color); } - if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_LOWER_LEFT || option == CIRC_OPT::DRAW_ALL) { drawPixel(x0 - x, y0 + y, color); drawPixel(x0 - y, y0 + x, color); } @@ -717,9 +722,9 @@ void Graphics2D::_drawCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_ * @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 + * @param option seclect quarter */ -void Graphics2D::drawCircle(int16_t x0, int16_t y0, int16_t rad, uint16_t color, +void Graphics2D::drawCircleQuarter(int16_t x0, int16_t y0, int16_t rad, uint16_t color, CIRC_OPT option) { // see p3dt_gfx_2d_license.txt float f; @@ -825,9 +830,8 @@ bool Graphics2D::isPixelMaskedByAngles(int32_t x, int32_t y) { return false; } - /** - * @brief Draw an anti-aliased circle with thicknes + * @brief Draw an anti-aliased circle with thickness * * @param x0 x-axis of the center of the circle * @param y0 y-axis of the center of the circle @@ -953,31 +957,31 @@ void Graphics2D::drawCircleAA(int16_t off_x, int16_t off_y, int16_t r, int16_t b } while (x0 < x1); } -void Graphics2D::_fillCircleSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, +void Graphics2D::_fillCircleQuarter(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) { + if (option == CIRC_OPT::DRAW_UPPER_RIGHT || option == CIRC_OPT::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) { + if (option == CIRC_OPT::DRAW_UPPER_LEFT || option == CIRC_OPT::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) { + if (option == CIRC_OPT::DRAW_LOWER_RIGHT || option == CIRC_OPT::DRAW_ALL) { drawVLine(x0 + x, y0, y + 1, color); drawVLine(x0 + y, y0, x + 1, color); } - if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_LOWER_LEFT || option == CIRC_OPT::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, +void Graphics2D::fillCircleQuarter(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t color, CIRC_OPT option) { // see p3dt_gfx_2d_license.txt float f; @@ -995,7 +999,7 @@ void Graphics2D::fillCircle(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t col x = 0; y = rad; - _fillCircleSection(x, y, x0, y0, color, option); + _fillCircleQuarter(x, y, x0, y0, color, option); while (x < y) { if (f >= 0) { @@ -1007,35 +1011,35 @@ void Graphics2D::fillCircle(uint16_t x0, uint16_t y0, uint16_t rad, uint16_t col ddFx += 2; f += ddFx; - _fillCircleSection(x, y, x0, y0, color, option); + _fillCircleQuarter(x, y, x0, y0, color, option); } } -void Graphics2D::_drawEllipseSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, +void Graphics2D::_drawEllipseQuarter(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) { + if (option == CIRC_OPT::DRAW_UPPER_RIGHT || option == CIRC_OPT::DRAW_ALL) { drawPixel(x0 + x, y0 - y, color); } /* upper left */ - if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_UPPER_LEFT || option == CIRC_OPT::DRAW_ALL) { drawPixel(x0 - x, y0 - y, color); } /* lower right */ - if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_LOWER_RIGHT || option == CIRC_OPT::DRAW_ALL) { drawPixel(x0 + x, y0 + y, color); } /* lower left */ - if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_LOWER_LEFT || option == CIRC_OPT::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, +void Graphics2D::drawEllipseQuarter(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; @@ -1075,7 +1079,7 @@ void Graphics2D::drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, stopy = 0; while (stopx >= stopy) { - _drawEllipseSection(x, y, x0, y0, color, option); + _drawEllipseQuarter(x, y, x0, y0, color, option); y++; stopy += rxrx2; err += ychg; @@ -1108,7 +1112,7 @@ void Graphics2D::drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, stopy *= ry; while (stopx <= stopy) { - _drawEllipseSection(x, y, x0, y0, color, option); + _drawEllipseQuarter(x, y, x0, y0, color, option); x++; stopx += ryry2; err += xchg; @@ -1122,31 +1126,31 @@ void Graphics2D::drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, } } -void Graphics2D::_fillEllipseSection(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, uint16_t color, +void Graphics2D::_fillEllipseQuarter(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) { + if (option == CIRC_OPT::DRAW_UPPER_RIGHT || option == CIRC_OPT::DRAW_ALL) { drawVLine(x0 + x, y0 - y, y + 1, color); } /* upper left */ - if (option == DRAW_UPPER_LEFT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_UPPER_LEFT || option == CIRC_OPT::DRAW_ALL) { drawVLine(x0 - x, y0 - y, y + 1, color); } /* lower right */ - if (option == DRAW_LOWER_RIGHT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_LOWER_RIGHT || option == CIRC_OPT::DRAW_ALL) { drawVLine(x0 + x, y0, y + 1, color); } /* lower left */ - if (option == DRAW_LOWER_LEFT || option == DRAW_ALL) { + if (option == CIRC_OPT::DRAW_LOWER_LEFT || option == CIRC_OPT::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, +void Graphics2D::fillEllipseQuarter(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; @@ -1186,7 +1190,7 @@ void Graphics2D::fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, stopy = 0; while (stopx >= stopy) { - _fillEllipseSection(x, y, x0, y0, color, option); + _fillEllipseQuarter(x, y, x0, y0, color, option); y++; stopy += rxrx2; err += ychg; @@ -1219,7 +1223,7 @@ void Graphics2D::fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, stopy *= ry; while (stopx <= stopy) { - _fillEllipseSection(x, y, x0, y0, color, option); + _fillEllipseQuarter(x, y, x0, y0, color, option); x++; stopx += ryry2; err += xchg; @@ -1258,10 +1262,10 @@ void Graphics2D::drawRFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint 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); + drawCircleQuarter(xl, yu, r, color, CIRC_OPT::DRAW_UPPER_LEFT); + drawCircleQuarter(xr, yu, r, color, CIRC_OPT::DRAW_UPPER_RIGHT); + drawCircleQuarter(xl, yl, r, color, CIRC_OPT::DRAW_LOWER_LEFT); + drawCircleQuarter(xr, yl, r, color, CIRC_OPT::DRAW_LOWER_RIGHT); } { @@ -1319,10 +1323,10 @@ void Graphics2D::fillRFrame(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint 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); + fillCircleQuarter(xl, yu, r, color, CIRC_OPT::DRAW_UPPER_LEFT); + fillCircleQuarter(xr, yu, r, color, CIRC_OPT::DRAW_UPPER_RIGHT); + fillCircleQuarter(xl, yl, r, color, CIRC_OPT::DRAW_LOWER_LEFT); + fillCircleQuarter(xr, yl, r, color, CIRC_OPT::DRAW_LOWER_RIGHT); { uint16_t ww; @@ -1392,7 +1396,7 @@ void Graphics2D::drawNTicksAA(int16_t cx, int16_t cy, int16_t r1, int16_t r2, in const float deltaAngle = 360.0f / nTicks; for (int h = nTicks-1; h >= 0; --h) { if (h % skip_every_nth != 0 || skip_every_nth > 360) - drawTickAA(cx, cy, r1, r2, (int16_t) (h * deltaAngle), color); + drawTick(cx, cy, r1, r2, (int16_t) (h * deltaAngle), color, true); } } @@ -1408,8 +1412,7 @@ void Graphics2D::drawNTicksAA(int16_t cx, int16_t cy, int16_t r1, int16_t r2, in * @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 + * @param eoa end of line */ void Graphics2D::drawArcAA(int16_t cx, int16_t cy, int16_t start, int16_t stop, int16_t radius, int16_t lineRadius, uint16_t color, LINE_END_OPT eoa) { @@ -1417,7 +1420,7 @@ void Graphics2D::drawArcAA(int16_t cx, int16_t cy, int16_t start, int16_t stop, if (start == -1 && stop == -1) drawCircleAA(cx, cy, radius+lineRadius, 2*lineRadius, color, start, stop); - if (eoa == ROUND_END) { + if (eoa == LINE_END_OPT::ROUND_END) { // int x = cx + cosf(start*PI/180) * radius; // int y = cy - sinf(start*PI/180) * radius; int x = cx + cosf_tlu(start) * radius; @@ -1464,18 +1467,19 @@ void Graphics2D::drawArcAA(int16_t cx, int16_t cy, int16_t start, int16_t stop, * etc.... * @param color Color code of the arc * @param highQuality - * @param anti_alias + * @param anti_aliased */ 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) { + uint16_t color, bool highQuality, bool anti_aliased) { int32_t old_x = radius * 4; // impossible value int32_t old_y = radius * 4; - for (float alpha = start; alpha <= stop; alpha += 0.1f) { + for (int16_t i = 0; i < steps; ++i) { + float alpha = start + i * (stop - start) / steps; int32_t x = rpx(cx, radius, alpha); int32_t y = rpy(cy, radius, alpha); if (old_x != x || old_y != y) { - if (anti_alias) + if (anti_aliased) fillCircleAA(x, y, lineRadius, color); // this is awfull and slow use drawArcAA instead else fillCircle(x, y, lineRadius, color);