From b09ea7f34fa7a8015b99752adcc3fa4a34560843 Mon Sep 17 00:00:00 2001 From: Owen from Canada Date: Fri, 13 Jun 2025 00:39:49 -0400 Subject: [PATCH 1/6] Added boolean return type to StatusIcons::Update To indicate whether anything updated --- src/displayapp/widgets/StatusIcons.cpp | 14 ++++++++++++-- src/displayapp/widgets/StatusIcons.h | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/displayapp/widgets/StatusIcons.cpp b/src/displayapp/widgets/StatusIcons.cpp index 777731a59f..f7c8b16c03 100644 --- a/src/displayapp/widgets/StatusIcons.cpp +++ b/src/displayapp/widgets/StatusIcons.cpp @@ -31,28 +31,38 @@ void StatusIcons::Create() { lv_obj_align(container, nullptr, LV_ALIGN_IN_TOP_RIGHT, 0, 0); } -void StatusIcons::Update() { +bool StatusIcons::Update() { + bool updated = false; + powerPresent = batteryController.IsPowerPresent(); if (powerPresent.IsUpdated()) { lv_obj_set_hidden(batteryPlug, !powerPresent.Get()); + updated = true; } batteryPercentRemaining = batteryController.PercentRemaining(); if (batteryPercentRemaining.IsUpdated()) { auto batteryPercent = batteryPercentRemaining.Get(); batteryIcon.SetBatteryPercentage(batteryPercent); + updated = true; } alarmEnabled = alarmController.IsEnabled(); if (alarmEnabled.IsUpdated()) { lv_obj_set_hidden(alarmIcon, !alarmEnabled.Get()); + updated = true; } bleState = bleController.IsConnected(); bleRadioEnabled = bleController.IsRadioEnabled(); if (bleState.IsUpdated() || bleRadioEnabled.IsUpdated()) { lv_obj_set_hidden(bleIcon, !bleState.Get()); + updated = true; + } + + if (updated) { + lv_obj_realign(container); } - lv_obj_realign(container); + return updated; } diff --git a/src/displayapp/widgets/StatusIcons.h b/src/displayapp/widgets/StatusIcons.h index 5524e996c5..16c866de89 100644 --- a/src/displayapp/widgets/StatusIcons.h +++ b/src/displayapp/widgets/StatusIcons.h @@ -24,7 +24,7 @@ namespace Pinetime { return container; } - void Update(); + bool Update(); private: Screens::BatteryIcon batteryIcon; From 01f5e006206e0bbd5ebb6f226c47e4e81deb80fc Mon Sep 17 00:00:00 2001 From: Owen from Canada Date: Fri, 20 Jun 2025 10:42:44 -0400 Subject: [PATCH 2/6] Added Azeret Mono font for Garden face --- src/displayapp/fonts/AzeretMono-Regular.ttf | Bin 0 -> 52692 bytes src/displayapp/fonts/CMakeLists.txt | 2 +- src/displayapp/fonts/fonts.json | 10 ++++++++++ src/libs/lv_conf.h | 3 ++- 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 src/displayapp/fonts/AzeretMono-Regular.ttf diff --git a/src/displayapp/fonts/AzeretMono-Regular.ttf b/src/displayapp/fonts/AzeretMono-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..cffa6ebc24c95f11e2751c6203890b9910d4d4e5 GIT binary patch literal 52692 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 Date: Fri, 20 Jun 2025 10:47:19 -0400 Subject: [PATCH 3/6] Created Garden watch face --- src/CMakeLists.txt | 2 + src/displayapp/UserApps.h | 1 + src/displayapp/apps/Apps.h.in | 1 + src/displayapp/apps/CMakeLists.txt | 1 + src/displayapp/screens/WatchFaceGarden.cpp | 263 +++++++++++++++++++++ src/displayapp/screens/WatchFaceGarden.h | 121 ++++++++++ src/displayapp/widgets/Flower.cpp | 131 ++++++++++ src/displayapp/widgets/Flower.h | 26 ++ 8 files changed, 546 insertions(+) create mode 100644 src/displayapp/screens/WatchFaceGarden.cpp create mode 100644 src/displayapp/screens/WatchFaceGarden.h create mode 100644 src/displayapp/widgets/Flower.cpp create mode 100644 src/displayapp/widgets/Flower.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5cd2e656a4..0883615e34 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -403,6 +403,7 @@ list(APPEND SOURCE_FILES displayapp/widgets/PageIndicator.cpp displayapp/widgets/DotIndicator.cpp displayapp/widgets/StatusIcons.cpp + displayapp/widgets/Flower.cpp ## Settings displayapp/screens/settings/QuickSettings.cpp @@ -428,6 +429,7 @@ list(APPEND SOURCE_FILES displayapp/screens/WatchFacePineTimeStyle.cpp displayapp/screens/WatchFaceCasioStyleG7710.cpp displayapp/screens/WatchFacePrideFlag.cpp + displayapp/screens/WatchFaceGarden.cpp ## diff --git a/src/displayapp/UserApps.h b/src/displayapp/UserApps.h index 8dc114429f..aeb2e0b63b 100644 --- a/src/displayapp/UserApps.h +++ b/src/displayapp/UserApps.h @@ -15,6 +15,7 @@ #include "displayapp/screens/WatchFacePineTimeStyle.h" #include "displayapp/screens/WatchFaceTerminal.h" #include "displayapp/screens/WatchFacePrideFlag.h" +#include "displayapp/screens/WatchFaceGarden.h" namespace Pinetime { namespace Applications { diff --git a/src/displayapp/apps/Apps.h.in b/src/displayapp/apps/Apps.h.in index f6feeb7b6d..b5fd694b5d 100644 --- a/src/displayapp/apps/Apps.h.in +++ b/src/displayapp/apps/Apps.h.in @@ -54,6 +54,7 @@ namespace Pinetime { Infineat, CasioStyleG7710, PrideFlag, + Garden, }; template diff --git a/src/displayapp/apps/CMakeLists.txt b/src/displayapp/apps/CMakeLists.txt index 93196ed6a0..6fe5c9bb3a 100644 --- a/src/displayapp/apps/CMakeLists.txt +++ b/src/displayapp/apps/CMakeLists.txt @@ -29,6 +29,7 @@ else() set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Infineat") set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::CasioStyleG7710") set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::PrideFlag") + set(DEFAULT_WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}, WatchFace::Garden") set(WATCHFACE_TYPES "${DEFAULT_WATCHFACE_TYPES}" CACHE STRING "List of watch faces to build into the firmware") endif() diff --git a/src/displayapp/screens/WatchFaceGarden.cpp b/src/displayapp/screens/WatchFaceGarden.cpp new file mode 100644 index 0000000000..26fe344e9b --- /dev/null +++ b/src/displayapp/screens/WatchFaceGarden.cpp @@ -0,0 +1,263 @@ +#include "displayapp/screens/WatchFaceGarden.h" + +#include + +#include "displayapp/screens/NotificationIcon.h" +#include "displayapp/screens/Symbols.h" +#include "displayapp/screens/WeatherSymbols.h" +#include "components/battery/BatteryController.h" +#include "components/ble/BleController.h" +#include "components/ble/NotificationManager.h" +#include "components/heartrate/HeartRateController.h" +#include "components/motion/MotionController.h" +#include "components/ble/SimpleWeatherService.h" +#include "components/settings/Settings.h" + +using namespace Pinetime::Applications::Screens; + +WatchFaceGarden::WatchFaceGarden(Controllers::DateTime& dateTimeController, + const Controllers::Battery& batteryController, + const Controllers::Ble& bleController, + const Controllers::AlarmController& alarmController, + Controllers::NotificationManager& notificationManager, + Controllers::Settings& settingsController, + Controllers::HeartRateController& heartRateController, + Controllers::MotionController& motionController, + Controllers::SimpleWeatherService& weatherService) + : currentDateTime {{}}, + dateTimeController {dateTimeController}, + notificationManager {notificationManager}, + settingsController {settingsController}, + heartRateController {heartRateController}, + motionController {motionController}, + weatherService {weatherService}, + statusIcons(batteryController, bleController, alarmController) { + + statusIcons.Create(); + + notificationIcon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x5FFF5F)); + lv_label_set_text_static(notificationIcon, ""); + lv_obj_align(notificationIcon, statusIcons.GetObject(), LV_ALIGN_OUT_LEFT_MID, -5, 0); + + + heartbeatIcon = lv_label_create(lv_scr_act(), nullptr); + lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat); + lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFF5F5F)); + lv_obj_align(heartbeatIcon, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 0, 0); + + heartbeatValue = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(heartbeatValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFF5F5F)); + lv_label_set_text_static(heartbeatValue, ""); + lv_obj_align(heartbeatValue, heartbeatIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); + + stepIcon = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(stepIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x5F8FFF)); + lv_label_set_text_static(stepIcon, Symbols::shoe); + lv_obj_align(stepIcon, lv_scr_act(), LV_ALIGN_IN_TOP_LEFT, 70, 0); + + stepValue = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(stepValue, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x5F8FFF)); + lv_label_set_text_static(stepValue, "0"); + lv_obj_align(stepValue, stepIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); + + + garden = lv_cont_create(lv_scr_act(), nullptr); + lv_obj_set_size(garden, 240, 217); + lv_obj_set_style_local_radius(garden, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 0); + lv_obj_set_style_local_bg_grad_dir(garden, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_GRAD_DIR_VER); + lv_obj_align(garden, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + + label_time_colon = lv_label_create(garden, nullptr); + lv_label_set_text(label_time_colon, ":"); + lv_obj_set_style_local_text_font(label_time_colon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &azeret_mono); + lv_obj_align(label_time_colon, garden, LV_ALIGN_IN_TOP_MID, 0, 10); + + label_time_hour = lv_label_create(garden, nullptr); + lv_obj_set_style_local_text_font(label_time_hour, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &azeret_mono); + lv_obj_align(label_time_hour, label_time_colon, LV_ALIGN_OUT_LEFT_MID, 12, 0); + + label_time_min = lv_label_create(garden, nullptr); + lv_obj_set_style_local_text_font(label_time_min, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &azeret_mono); + lv_obj_align(label_time_min, label_time_colon, LV_ALIGN_OUT_RIGHT_MID, -12, 0); + + label_time_ampm = lv_label_create(garden, nullptr); + lv_label_set_text(label_time_ampm, ""); + lv_obj_align(label_time_ampm, label_time_colon, LV_ALIGN_IN_TOP_MID, 0, -7); + + + label_date = lv_label_create(garden, nullptr); + lv_obj_align(label_date, garden, LV_ALIGN_IN_TOP_LEFT, 4, 75); + + temperature = lv_label_create(garden, nullptr); + lv_label_set_text(temperature, ""); + lv_obj_align(temperature, garden, LV_ALIGN_IN_TOP_RIGHT, -4, 75); + + weatherIcon = lv_label_create(garden, nullptr); + lv_obj_set_style_local_text_font(weatherIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &fontawesome_weathericons); + lv_label_set_text(weatherIcon, ""); + lv_obj_align(weatherIcon, temperature, LV_ALIGN_OUT_LEFT_MID, -3, 0); + + + for (int k = 0; k < NUM_FLOWERS; k++) { + flowers[k].Create(garden); + lv_obj_align(flowers[k].GetObject(), garden, LV_ALIGN_IN_BOTTOM_MID, X_POS_OFFSET + (X_POS_STEP * k), 0); + } + + + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); + Refresh(); +} + +WatchFaceGarden::~WatchFaceGarden() { + lv_task_del(taskRefresh); + lv_obj_clean(lv_scr_act()); +} + +void WatchFaceGarden::Refresh() { + bool refreshGarden = false; + bool realignNotification = statusIcons.Update(); + + notificationState = notificationManager.AreNewNotificationsAvailable(); + if (notificationState.IsUpdated()) { + lv_label_set_text(notificationIcon, (notificationState.Get() ? Symbols::lapsFlag : "")); + realignNotification = true; + } + + if (realignNotification) { + lv_obj_realign(notificationIcon); + } + + currentDateTime = std::chrono::time_point_cast(dateTimeController.CurrentDateTime()); + + if (currentDateTime.IsUpdated()) { + uint8_t hour = dateTimeController.Hours(); + uint8_t minute = dateTimeController.Minutes(); + int offset = 0; + + if (hour < 6) { + stage = 0; + } else if (hour < 9) { + stage = 1; + } else if (hour < 11) { + stage = 2; + } else if (hour < 13) { + stage = 3; + } else if (hour < 20) { + stage = 4; + } else if (hour < 23) { + stage = 5; + } else { + stage = 0; + } + + if (stage.IsUpdated()) { + lv_obj_set_style_local_bg_color(garden, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(BG_COLORS[stage.Get()*2])); + lv_obj_set_style_local_bg_grad_color(garden, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(BG_COLORS[stage.Get()*2+1])); + refreshGarden = true; + } + + if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { + lv_label_set_text(label_time_ampm, (hour >= 12 ? "PM" : "AM")); + + if (hour == 0) { + hour = 12; + } else if (hour > 12) { + hour = hour - 12; + } + lv_label_set_text_fmt(label_time_hour, "%2d", hour); + + if (hour < 10) { + offset = -24; // center time display + } + } else { + lv_label_set_text_fmt(label_time_hour, "%02d", hour); + } + lv_obj_align(label_time_colon, garden, LV_ALIGN_IN_TOP_MID, offset, 7); + lv_obj_realign(label_time_hour); + lv_obj_realign(label_time_ampm); + + lv_label_set_text_fmt(label_time_min, "%02d", minute); + lv_obj_realign(label_time_min); + + currentDate = std::chrono::time_point_cast(currentDateTime.Get()); + if (currentDate.IsUpdated()) { + uint8_t day = dateTimeController.Day(); + if (settingsController.GetClockType() == Controllers::Settings::ClockType::H24) { + lv_label_set_text_fmt(label_date, + "%s %d %s", + dateTimeController.DayOfWeekShortToString(), + day, + dateTimeController.MonthShortToString()); + } else { + lv_label_set_text_fmt(label_date, + "%s %s %d", + dateTimeController.DayOfWeekShortToString(), + dateTimeController.MonthShortToString(), + day); + } + lv_obj_realign(label_date); + + refreshGarden = true; + // base seed is the date + seeds[0] = ((uint8_t)dateTimeController.Month()*32U) + day; + for (int k = 1; k < NUM_FLOWERS; k++) { + // "sow" seeds using PRNG (xorshift32) + uint32_t s = seeds[k-1]; + s ^= s << 13; + s ^= s >> 17; + s ^= s << 5; + seeds[k] = s; + } + } + + if (refreshGarden) { + for (int k = 0; k < NUM_FLOWERS; k++) { + flowers[k].Update(seeds[k], stage.Get()); + lv_obj_realign(flowers[k].GetObject()); + } + } + } + + heartbeat = heartRateController.HeartRate(); + heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped; + if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) { + if (heartbeatRunning.Get()) { + lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xFF5F5F)); + lv_label_set_text_fmt(heartbeatValue, "%d", heartbeat.Get()); + } else { + lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x5F5F5F)); + lv_label_set_text_static(heartbeatValue, ""); + } + + lv_obj_realign(heartbeatIcon); + lv_obj_realign(heartbeatValue); + } + + stepCount = motionController.NbSteps(); + if (stepCount.IsUpdated()) { + lv_label_set_text_fmt(stepValue, "%lu", stepCount.Get()); + lv_obj_realign(stepIcon); + lv_obj_realign(stepValue); + } + + currentWeather = weatherService.Current(); + if (currentWeather.IsUpdated()) { + auto optCurrentWeather = currentWeather.Get(); + if (optCurrentWeather) { + int16_t temp = optCurrentWeather->temperature.Celsius(); + char tempUnit = 'C'; + if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) { + temp = optCurrentWeather->temperature.Fahrenheit(); + tempUnit = 'F'; + } + lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit); + lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId)); + } else { + lv_label_set_text_static(temperature, ""); + lv_label_set_text(weatherIcon, ""); + } + lv_obj_realign(temperature); + lv_obj_realign(weatherIcon); + } +} diff --git a/src/displayapp/screens/WatchFaceGarden.h b/src/displayapp/screens/WatchFaceGarden.h new file mode 100644 index 0000000000..21c8972c1a --- /dev/null +++ b/src/displayapp/screens/WatchFaceGarden.h @@ -0,0 +1,121 @@ +#pragma once + +#include +#include +#include +#include +#include "displayapp/screens/Screen.h" +#include "components/datetime/DateTimeController.h" +#include "components/ble/SimpleWeatherService.h" +#include "components/ble/BleController.h" +#include "displayapp/widgets/StatusIcons.h" +#include "displayapp/widgets/Flower.h" +#include "utility/DirtyValue.h" +#include "displayapp/apps/Apps.h" + +namespace Pinetime { + namespace Controllers { + class Settings; + class Battery; + class Ble; + class AlarmController; + class NotificationManager; + class HeartRateController; + class MotionController; + } + + namespace Applications { + namespace Screens { + + class WatchFaceGarden : public Screen { + public: + WatchFaceGarden(Controllers::DateTime& dateTimeController, + const Controllers::Battery& batteryController, + const Controllers::Ble& bleController, + const Controllers::AlarmController& alarmController, + Controllers::NotificationManager& notificationManager, + Controllers::Settings& settingsController, + Controllers::HeartRateController& heartRateController, + Controllers::MotionController& motionController, + Controllers::SimpleWeatherService& weather); + ~WatchFaceGarden() override; + + void Refresh() override; + + private: + static const int NUM_FLOWERS = 5; + static const int NUM_STAGES = 6; + static const int GARDEN_WIDTH = 180; + static constexpr int X_POS_OFFSET = -GARDEN_WIDTH/2; + static constexpr int X_POS_STEP = GARDEN_WIDTH/(NUM_FLOWERS-1); + + static constexpr uint32_t BG_COLORS[NUM_STAGES*2] = { + 0x000000, 0x16004C, + 0x080065, 0xC6B431, + 0x00539C, 0xABDFFF, + 0x2D7DFF, 0x87BBFF, + 0x2D7DFF, 0x87BBFF, + 0x090463, 0xBB112D + }; + + Utility::DirtyValue> currentDateTime {}; + Utility::DirtyValue stepCount {}; + Utility::DirtyValue heartbeat {}; + Utility::DirtyValue heartbeatRunning {}; + Utility::DirtyValue notificationState {}; + Utility::DirtyValue> currentWeather {}; + Utility::DirtyValue stage {}; + Utility::DirtyValue seed {}; + Utility::DirtyValue> currentDate; + + lv_obj_t* garden; + lv_obj_t* label_time_hour; + lv_obj_t* label_time_min; + lv_obj_t* label_time_colon; + lv_obj_t* label_time_ampm; + lv_obj_t* label_date; + lv_obj_t* heartbeatIcon; + lv_obj_t* heartbeatValue; + lv_obj_t* stepIcon; + lv_obj_t* stepValue; + lv_obj_t* notificationIcon; + lv_obj_t* weatherIcon; + lv_obj_t* temperature; + + Controllers::DateTime& dateTimeController; + Controllers::NotificationManager& notificationManager; + Controllers::Settings& settingsController; + Controllers::HeartRateController& heartRateController; + Controllers::MotionController& motionController; + Controllers::SimpleWeatherService& weatherService; + + lv_task_t* taskRefresh; + Widgets::StatusIcons statusIcons; + Widgets::Flower flowers[NUM_FLOWERS]; + uint16_t seeds[NUM_FLOWERS]; + }; + } + + template <> + struct WatchFaceTraits { + static constexpr WatchFace watchFace = WatchFace::Garden; + static constexpr const char* name = "Garden"; + + static Screens::Screen* Create(AppControllers& controllers) { + return new Screens::WatchFaceGarden(controllers.dateTimeController, + controllers.batteryController, + controllers.bleController, + controllers.alarmController, + controllers.notificationManager, + controllers.settingsController, + controllers.heartRateController, + controllers.motionController, + *controllers.weatherController); + }; + + static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) { + return true; + } + }; + } +} diff --git a/src/displayapp/widgets/Flower.cpp b/src/displayapp/widgets/Flower.cpp new file mode 100644 index 0000000000..5ff1961549 --- /dev/null +++ b/src/displayapp/widgets/Flower.cpp @@ -0,0 +1,131 @@ +#include "displayapp/widgets/Flower.h" +#include + +using namespace Pinetime::Applications::Widgets; + + +// total vertical playroom is 115 +#define LINE_WIDTH 3 +#define CONTAINER_HEIGHT 115 +#define MIN_PETAL_LENGTH 12 +#define MAX_PETAL_LENGTH 27 +#define MIN_PETALS 7 +#define MAX_STEM_HEIGHT (CONTAINER_HEIGHT - MAX_PETAL_LENGTH) +#define MIN_STEM_HEIGHT (MAX_PETAL_LENGTH + 5) +#define STEM_COLOR 0x135416 +#define NUM_PETAL_COLORS 8 +#define CONTAINER_WIDTH (MAX_PETAL_LENGTH*2 + LINE_WIDTH*2) + +#define PETAL_INDEX(seed) ((seed) & 0x07UL) +#define SIZE_INDEX(seed) (((seed) >> 3) & 0x07UL) +#define ANGLE_OFFS_INDEX(seed) (((seed) >> 6) & 0x07UL) +#define COLOR_INDEX(seed) (((seed) >> 9) % NUM_PETAL_COLORS) +#define HEIGHT_FROM_STAGE(max, stage) ((stage) > 5 ? (max)*3/4 : (max) * (stage) / 5) + +const uint32_t PETAL_COLORS[NUM_PETAL_COLORS] = { + 0xf43838, 0xf48829, 0xf9f44d, 0x56efe2, 0x6f7afc, 0x8f68f9, 0xdb49fc, 0xfc83b5 +}; + + +Flower::Flower() { +} + +void Flower::Create(lv_obj_t* parent) { + container = lv_obj_create(parent, nullptr); + lv_obj_set_size(container, CONTAINER_WIDTH, CONTAINER_HEIGHT); + lv_obj_set_style_local_radius(container, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 0); + lv_obj_set_style_local_bg_opa(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP); + + stem = lv_line_create(container, nullptr); + lv_obj_set_style_local_line_color(stem, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(STEM_COLOR)); + lv_obj_set_style_local_line_width(stem, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LINE_WIDTH); + lv_line_set_y_invert(stem, true); + lv_obj_align(stem, container, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + + petals = lv_line_create(container, nullptr); + lv_obj_set_style_local_line_width(petals, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LINE_WIDTH); + lv_obj_set_style_local_line_rounded(petals, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, LINE_WIDTH); + lv_line_set_y_invert(petals, true); + lv_obj_align(petals, container, LV_ALIGN_IN_BOTTOM_MID, 0, 0); +} + +void Flower::Update(uint16_t seed, int stage) { + uint32_t petalColor = PETAL_COLORS[COLOR_INDEX(seed)]; + int petalLength = MIN_PETAL_LENGTH + 2*SIZE_INDEX(seed); + int maxStemHeight = MIN_STEM_HEIGHT + 8*SIZE_INDEX(seed); + int numPetals = MIN_PETALS + PETAL_INDEX(seed); + int angleOffsIndex = ANGLE_OFFS_INDEX(seed); + int stemHeight = HEIGHT_FROM_STAGE(maxStemHeight, stage); + + lv_obj_set_hidden(stem, stage < 1); + if (stage >= 1) { + stemPoints[0].x = 0; + stemPoints[0].y = 0; + stemPoints[1].x = 0; + stemPoints[1].y = stemHeight; + lv_line_set_points(stem, stemPoints, 2); + lv_obj_realign(stem); + } + + lv_obj_set_hidden(petals, stage < 2); + lv_obj_set_style_local_line_color(petals, LV_LINE_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(petalColor)); + if (stage == 2) { + int xOffs = 1; + petalPoints[0].x = 0 + xOffs; + petalPoints[0].y = stemHeight; + petalPoints[1].x = -1 + xOffs; + petalPoints[1].y = 1 + stemHeight; + petalPoints[2].x = 0 + xOffs; + petalPoints[2].y = petalLength/2 + stemHeight; + petalPoints[3].x = 1 + xOffs; + petalPoints[3].y = 1 + stemHeight; + petalPoints[4].x = 0 + xOffs; + petalPoints[4].y = stemHeight; + lv_line_set_points(petals, petalPoints, 5); + lv_obj_realign(petals); + } else if (stage == 3) { + int xOffs = 2; + petalPoints[0].x = 0 + xOffs; + petalPoints[0].y = stemHeight; + petalPoints[1].x = -2 + xOffs; + petalPoints[1].y = 1 + stemHeight; + petalPoints[2].x = 0 + xOffs; + petalPoints[2].y = petalLength + stemHeight; + petalPoints[3].x = 2 + xOffs; + petalPoints[3].y = 1 + stemHeight; + petalPoints[4].x = 0 + xOffs; + petalPoints[4].y = stemHeight; + lv_line_set_points(petals, petalPoints, 5); + lv_obj_realign(petals); + } else if (stage == 4) { + int xOffs = petalLength; + float angleStep = 6.283185f / (numPetals*2); + float angleOffs = (angleStep * 0.25f) * angleOffsIndex; + for (int k = 0; k < numPetals; k++) { + int k2 = k*2; + petalPoints[k2].x = petalLength*sin((k2*angleStep)+angleOffs) + xOffs; + petalPoints[k2].y = stemHeight + petalLength*cos((k2*angleStep)+angleOffs); + petalPoints[k2+1].x = xOffs; + petalPoints[k2+1].y = stemHeight; + } + petalPoints[numPetals*2].x = petalPoints[0].x; + petalPoints[numPetals*2].y = petalPoints[0].y; + lv_line_set_points(petals, petalPoints, (numPetals*2)+1); + lv_obj_realign(petals); + } else if (stage == 5) { + int xOffs = petalLength/2; + float angleStep = (6.283185f / ((numPetals*2)-1)) * 0.25f; + float angleOffs = 6.283185f * 3.0f/8.0f; + for (int k = 0; k < numPetals; k++) { + int k2 = k*2; + petalPoints[k2].x = petalLength*sin((k2*angleStep)+angleOffs) + xOffs; + petalPoints[k2].y = stemHeight + petalLength*cos((k2*angleStep)+angleOffs); + petalPoints[k2+1].x = xOffs; + petalPoints[k2+1].y = stemHeight; + } + petalPoints[numPetals*2].x = petalPoints[0].x; + petalPoints[numPetals*2].y = petalPoints[0].y; + lv_line_set_points(petals, petalPoints, (numPetals*2)+1); + lv_obj_realign(petals); + } +} diff --git a/src/displayapp/widgets/Flower.h b/src/displayapp/widgets/Flower.h new file mode 100644 index 0000000000..7889912f84 --- /dev/null +++ b/src/displayapp/widgets/Flower.h @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace Pinetime { + namespace Applications { + namespace Widgets { + class Flower { + public: + Flower(); + void Create(lv_obj_t* parent); + void Update(uint16_t seed, int stage); + + lv_obj_t* GetObject() { + return container; + } + + private: + lv_obj_t* container; + lv_obj_t* stem; + lv_obj_t* petals; + lv_point_t stemPoints[2]; + lv_point_t petalPoints[29]; + }; + } + } +} From 5e94dbffee0dc8578c1e068ffcaf9cff0d2043f1 Mon Sep 17 00:00:00 2001 From: Owen from Canada Date: Fri, 20 Jun 2025 11:02:45 -0400 Subject: [PATCH 4/6] Added Garden face to documentation --- doc/gettingStarted/Watchfaces.md | 5 +++++ doc/gettingStarted/Watchfaces/Garden.png | Bin 0 -> 11152 bytes 2 files changed, 5 insertions(+) create mode 100644 doc/gettingStarted/Watchfaces/Garden.png diff --git a/doc/gettingStarted/Watchfaces.md b/doc/gettingStarted/Watchfaces.md index 9edff0bb51..67fc083c63 100644 --- a/doc/gettingStarted/Watchfaces.md +++ b/doc/gettingStarted/Watchfaces.md @@ -29,3 +29,8 @@ InfiniTime has 6 apps on the `main` branch at the time of writing. ### Casio G7710 ![Casio G7710 face](/doc/gettingStarted/Watchfaces/CasioG7710.png) + +### Garden +![Garden face](/doc/gettingStarted/Watchfaces/Garden.png) + - Flowers "grow" throughout the day + - Different flowers each day diff --git a/doc/gettingStarted/Watchfaces/Garden.png b/doc/gettingStarted/Watchfaces/Garden.png new file mode 100644 index 0000000000000000000000000000000000000000..70d7194e709457dc8932eb72f6d55dd68c525b87 GIT binary patch literal 11152 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 JLd+=me*uB3vIYPE literal 0 HcmV?d00001 From c18483ce2a87dafa62adb433f5b21ef59b5c200f Mon Sep 17 00:00:00 2001 From: Owen from Canada Date: Fri, 20 Jun 2025 11:29:27 -0400 Subject: [PATCH 5/6] Applied patches from github tidy (and cleaned up the Flower module a bit) --- src/displayapp/screens/WatchFaceGarden.cpp | 17 ++---- src/displayapp/screens/WatchFaceGarden.h | 30 ++++------ src/displayapp/widgets/Flower.cpp | 70 +++++++--------------- src/displayapp/widgets/Flower.h | 20 +++++++ 4 files changed, 61 insertions(+), 76 deletions(-) diff --git a/src/displayapp/screens/WatchFaceGarden.cpp b/src/displayapp/screens/WatchFaceGarden.cpp index 26fe344e9b..b35ecf5d52 100644 --- a/src/displayapp/screens/WatchFaceGarden.cpp +++ b/src/displayapp/screens/WatchFaceGarden.cpp @@ -32,14 +32,13 @@ WatchFaceGarden::WatchFaceGarden(Controllers::DateTime& dateTimeController, motionController {motionController}, weatherService {weatherService}, statusIcons(batteryController, bleController, alarmController) { - + statusIcons.Create(); notificationIcon = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(notificationIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x5FFF5F)); lv_label_set_text_static(notificationIcon, ""); lv_obj_align(notificationIcon, statusIcons.GetObject(), LV_ALIGN_OUT_LEFT_MID, -5, 0); - heartbeatIcon = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text_static(heartbeatIcon, Symbols::heartBeat); @@ -61,7 +60,6 @@ WatchFaceGarden::WatchFaceGarden(Controllers::DateTime& dateTimeController, lv_label_set_text_static(stepValue, "0"); lv_obj_align(stepValue, stepIcon, LV_ALIGN_OUT_RIGHT_MID, 5, 0); - garden = lv_cont_create(lv_scr_act(), nullptr); lv_obj_set_size(garden, 240, 217); lv_obj_set_style_local_radius(garden, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 0); @@ -85,7 +83,6 @@ WatchFaceGarden::WatchFaceGarden(Controllers::DateTime& dateTimeController, lv_label_set_text(label_time_ampm, ""); lv_obj_align(label_time_ampm, label_time_colon, LV_ALIGN_IN_TOP_MID, 0, -7); - label_date = lv_label_create(garden, nullptr); lv_obj_align(label_date, garden, LV_ALIGN_IN_TOP_LEFT, 4, 75); @@ -98,13 +95,11 @@ WatchFaceGarden::WatchFaceGarden(Controllers::DateTime& dateTimeController, lv_label_set_text(weatherIcon, ""); lv_obj_align(weatherIcon, temperature, LV_ALIGN_OUT_LEFT_MID, -3, 0); - for (int k = 0; k < NUM_FLOWERS; k++) { flowers[k].Create(garden); lv_obj_align(flowers[k].GetObject(), garden, LV_ALIGN_IN_BOTTOM_MID, X_POS_OFFSET + (X_POS_STEP * k), 0); } - taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); Refresh(); } @@ -152,8 +147,8 @@ void WatchFaceGarden::Refresh() { } if (stage.IsUpdated()) { - lv_obj_set_style_local_bg_color(garden, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(BG_COLORS[stage.Get()*2])); - lv_obj_set_style_local_bg_grad_color(garden, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(BG_COLORS[stage.Get()*2+1])); + lv_obj_set_style_local_bg_color(garden, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(BG_COLORS[stage.Get() * 2])); + lv_obj_set_style_local_bg_grad_color(garden, LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(BG_COLORS[stage.Get() * 2 + 1])); refreshGarden = true; } @@ -166,7 +161,7 @@ void WatchFaceGarden::Refresh() { hour = hour - 12; } lv_label_set_text_fmt(label_time_hour, "%2d", hour); - + if (hour < 10) { offset = -24; // center time display } @@ -200,10 +195,10 @@ void WatchFaceGarden::Refresh() { refreshGarden = true; // base seed is the date - seeds[0] = ((uint8_t)dateTimeController.Month()*32U) + day; + seeds[0] = ((uint8_t) dateTimeController.Month() * 32U) + day; for (int k = 1; k < NUM_FLOWERS; k++) { // "sow" seeds using PRNG (xorshift32) - uint32_t s = seeds[k-1]; + uint32_t s = seeds[k - 1]; s ^= s << 13; s ^= s >> 17; s ^= s << 5; diff --git a/src/displayapp/screens/WatchFaceGarden.h b/src/displayapp/screens/WatchFaceGarden.h index 21c8972c1a..8057d55ff4 100644 --- a/src/displayapp/screens/WatchFaceGarden.h +++ b/src/displayapp/screens/WatchFaceGarden.h @@ -30,14 +30,14 @@ namespace Pinetime { class WatchFaceGarden : public Screen { public: WatchFaceGarden(Controllers::DateTime& dateTimeController, - const Controllers::Battery& batteryController, - const Controllers::Ble& bleController, - const Controllers::AlarmController& alarmController, - Controllers::NotificationManager& notificationManager, - Controllers::Settings& settingsController, - Controllers::HeartRateController& heartRateController, - Controllers::MotionController& motionController, - Controllers::SimpleWeatherService& weather); + const Controllers::Battery& batteryController, + const Controllers::Ble& bleController, + const Controllers::AlarmController& alarmController, + Controllers::NotificationManager& notificationManager, + Controllers::Settings& settingsController, + Controllers::HeartRateController& heartRateController, + Controllers::MotionController& motionController, + Controllers::SimpleWeatherService& weather); ~WatchFaceGarden() override; void Refresh() override; @@ -46,17 +46,11 @@ namespace Pinetime { static const int NUM_FLOWERS = 5; static const int NUM_STAGES = 6; static const int GARDEN_WIDTH = 180; - static constexpr int X_POS_OFFSET = -GARDEN_WIDTH/2; - static constexpr int X_POS_STEP = GARDEN_WIDTH/(NUM_FLOWERS-1); + static constexpr int X_POS_OFFSET = -GARDEN_WIDTH / 2; + static constexpr int X_POS_STEP = GARDEN_WIDTH / (NUM_FLOWERS - 1); - static constexpr uint32_t BG_COLORS[NUM_STAGES*2] = { - 0x000000, 0x16004C, - 0x080065, 0xC6B431, - 0x00539C, 0xABDFFF, - 0x2D7DFF, 0x87BBFF, - 0x2D7DFF, 0x87BBFF, - 0x090463, 0xBB112D - }; + static constexpr uint32_t BG_COLORS[NUM_STAGES * 2] = + {0x000000, 0x16004C, 0x080065, 0xC6B431, 0x00539C, 0xABDFFF, 0x2D7DFF, 0x87BBFF, 0x2D7DFF, 0x87BBFF, 0x090463, 0xBB112D}; Utility::DirtyValue> currentDateTime {}; Utility::DirtyValue stepCount {}; diff --git a/src/displayapp/widgets/Flower.cpp b/src/displayapp/widgets/Flower.cpp index 5ff1961549..3810bc6e7e 100644 --- a/src/displayapp/widgets/Flower.cpp +++ b/src/displayapp/widgets/Flower.cpp @@ -3,30 +3,6 @@ using namespace Pinetime::Applications::Widgets; - -// total vertical playroom is 115 -#define LINE_WIDTH 3 -#define CONTAINER_HEIGHT 115 -#define MIN_PETAL_LENGTH 12 -#define MAX_PETAL_LENGTH 27 -#define MIN_PETALS 7 -#define MAX_STEM_HEIGHT (CONTAINER_HEIGHT - MAX_PETAL_LENGTH) -#define MIN_STEM_HEIGHT (MAX_PETAL_LENGTH + 5) -#define STEM_COLOR 0x135416 -#define NUM_PETAL_COLORS 8 -#define CONTAINER_WIDTH (MAX_PETAL_LENGTH*2 + LINE_WIDTH*2) - -#define PETAL_INDEX(seed) ((seed) & 0x07UL) -#define SIZE_INDEX(seed) (((seed) >> 3) & 0x07UL) -#define ANGLE_OFFS_INDEX(seed) (((seed) >> 6) & 0x07UL) -#define COLOR_INDEX(seed) (((seed) >> 9) % NUM_PETAL_COLORS) -#define HEIGHT_FROM_STAGE(max, stage) ((stage) > 5 ? (max)*3/4 : (max) * (stage) / 5) - -const uint32_t PETAL_COLORS[NUM_PETAL_COLORS] = { - 0xf43838, 0xf48829, 0xf9f44d, 0x56efe2, 0x6f7afc, 0x8f68f9, 0xdb49fc, 0xfc83b5 -}; - - Flower::Flower() { } @@ -51,8 +27,8 @@ void Flower::Create(lv_obj_t* parent) { void Flower::Update(uint16_t seed, int stage) { uint32_t petalColor = PETAL_COLORS[COLOR_INDEX(seed)]; - int petalLength = MIN_PETAL_LENGTH + 2*SIZE_INDEX(seed); - int maxStemHeight = MIN_STEM_HEIGHT + 8*SIZE_INDEX(seed); + int petalLength = MIN_PETAL_LENGTH + 2 * SIZE_INDEX(seed); + int maxStemHeight = MIN_STEM_HEIGHT + 8 * SIZE_INDEX(seed); int numPetals = MIN_PETALS + PETAL_INDEX(seed); int angleOffsIndex = ANGLE_OFFS_INDEX(seed); int stemHeight = HEIGHT_FROM_STAGE(maxStemHeight, stage); @@ -76,7 +52,7 @@ void Flower::Update(uint16_t seed, int stage) { petalPoints[1].x = -1 + xOffs; petalPoints[1].y = 1 + stemHeight; petalPoints[2].x = 0 + xOffs; - petalPoints[2].y = petalLength/2 + stemHeight; + petalPoints[2].y = petalLength / 2 + stemHeight; petalPoints[3].x = 1 + xOffs; petalPoints[3].y = 1 + stemHeight; petalPoints[4].x = 0 + xOffs; @@ -99,33 +75,33 @@ void Flower::Update(uint16_t seed, int stage) { lv_obj_realign(petals); } else if (stage == 4) { int xOffs = petalLength; - float angleStep = 6.283185f / (numPetals*2); + float angleStep = 6.283185f / (numPetals * 2); float angleOffs = (angleStep * 0.25f) * angleOffsIndex; for (int k = 0; k < numPetals; k++) { - int k2 = k*2; - petalPoints[k2].x = petalLength*sin((k2*angleStep)+angleOffs) + xOffs; - petalPoints[k2].y = stemHeight + petalLength*cos((k2*angleStep)+angleOffs); - petalPoints[k2+1].x = xOffs; - petalPoints[k2+1].y = stemHeight; + int k2 = k * 2; + petalPoints[k2].x = petalLength * sin((k2 * angleStep) + angleOffs) + xOffs; + petalPoints[k2].y = stemHeight + petalLength * cos((k2 * angleStep) + angleOffs); + petalPoints[k2 + 1].x = xOffs; + petalPoints[k2 + 1].y = stemHeight; } - petalPoints[numPetals*2].x = petalPoints[0].x; - petalPoints[numPetals*2].y = petalPoints[0].y; - lv_line_set_points(petals, petalPoints, (numPetals*2)+1); + petalPoints[numPetals * 2].x = petalPoints[0].x; + petalPoints[numPetals * 2].y = petalPoints[0].y; + lv_line_set_points(petals, petalPoints, (numPetals * 2) + 1); lv_obj_realign(petals); } else if (stage == 5) { - int xOffs = petalLength/2; - float angleStep = (6.283185f / ((numPetals*2)-1)) * 0.25f; - float angleOffs = 6.283185f * 3.0f/8.0f; + int xOffs = petalLength / 2; + float angleStep = (6.283185f / ((numPetals * 2) - 1)) * 0.25f; + float angleOffs = 6.283185f * 3.0f / 8.0f; for (int k = 0; k < numPetals; k++) { - int k2 = k*2; - petalPoints[k2].x = petalLength*sin((k2*angleStep)+angleOffs) + xOffs; - petalPoints[k2].y = stemHeight + petalLength*cos((k2*angleStep)+angleOffs); - petalPoints[k2+1].x = xOffs; - petalPoints[k2+1].y = stemHeight; + int k2 = k * 2; + petalPoints[k2].x = petalLength * sin((k2 * angleStep) + angleOffs) + xOffs; + petalPoints[k2].y = stemHeight + petalLength * cos((k2 * angleStep) + angleOffs); + petalPoints[k2 + 1].x = xOffs; + petalPoints[k2 + 1].y = stemHeight; } - petalPoints[numPetals*2].x = petalPoints[0].x; - petalPoints[numPetals*2].y = petalPoints[0].y; - lv_line_set_points(petals, petalPoints, (numPetals*2)+1); + petalPoints[numPetals * 2].x = petalPoints[0].x; + petalPoints[numPetals * 2].y = petalPoints[0].y; + lv_line_set_points(petals, petalPoints, (numPetals * 2) + 1); lv_obj_realign(petals); } } diff --git a/src/displayapp/widgets/Flower.h b/src/displayapp/widgets/Flower.h index 7889912f84..7a585fb347 100644 --- a/src/displayapp/widgets/Flower.h +++ b/src/displayapp/widgets/Flower.h @@ -15,6 +15,26 @@ namespace Pinetime { } private: + static const int LINE_WIDTH = 3; + static const int CONTAINER_HEIGHT = 115; + static const int MIN_PETAL_LENGTH = 12; + static const int MAX_PETAL_LENGTH = 27; + static const int MIN_PETALS = 7; + static constexpr int MAX_STEM_HEIGHT = CONTAINER_HEIGHT - MAX_PETAL_LENGTH; + static constexpr int MIN_STEM_HEIGHT = MAX_PETAL_LENGTH + 5; + static const uint32_t STEM_COLOR = 0x135416; + static const int NUM_PETAL_COLORS = 8; + static constexpr int CONTAINER_WIDTH = MAX_PETAL_LENGTH * 2 + LINE_WIDTH * 2; + + static constexpr uint16_t PETAL_INDEX(uint16_t seed) { return seed & 0x07UL; } + static constexpr uint16_t SIZE_INDEX(uint16_t seed) { return (seed >> 3) & 0x07UL; } + static constexpr uint16_t ANGLE_OFFS_INDEX(uint16_t seed) { return (seed >> 6) & 0x07UL; } + static constexpr uint16_t COLOR_INDEX(uint16_t seed) { return (seed >> 9) % NUM_PETAL_COLORS; } + static constexpr int HEIGHT_FROM_STAGE(int max, int stage) { return stage > 5 ? max * 3 / 4 : max * stage / 5; } + + static constexpr uint32_t PETAL_COLORS[NUM_PETAL_COLORS] = + {0xf43838, 0xf48829, 0xf9f44d, 0x56efe2, 0x6f7afc, 0x8f68f9, 0xdb49fc, 0xfc83b5}; + lv_obj_t* container; lv_obj_t* stem; lv_obj_t* petals; From 919fbd7841c2bc0ac13ff80e347ecb374e55fe3d Mon Sep 17 00:00:00 2001 From: Owen from Canada Date: Fri, 20 Jun 2025 11:32:37 -0400 Subject: [PATCH 6/6] Formatting tweaks per clang-format patch --- src/displayapp/widgets/Flower.h | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/displayapp/widgets/Flower.h b/src/displayapp/widgets/Flower.h index 7a585fb347..bcd6c54d2b 100644 --- a/src/displayapp/widgets/Flower.h +++ b/src/displayapp/widgets/Flower.h @@ -26,11 +26,25 @@ namespace Pinetime { static const int NUM_PETAL_COLORS = 8; static constexpr int CONTAINER_WIDTH = MAX_PETAL_LENGTH * 2 + LINE_WIDTH * 2; - static constexpr uint16_t PETAL_INDEX(uint16_t seed) { return seed & 0x07UL; } - static constexpr uint16_t SIZE_INDEX(uint16_t seed) { return (seed >> 3) & 0x07UL; } - static constexpr uint16_t ANGLE_OFFS_INDEX(uint16_t seed) { return (seed >> 6) & 0x07UL; } - static constexpr uint16_t COLOR_INDEX(uint16_t seed) { return (seed >> 9) % NUM_PETAL_COLORS; } - static constexpr int HEIGHT_FROM_STAGE(int max, int stage) { return stage > 5 ? max * 3 / 4 : max * stage / 5; } + static constexpr uint16_t PETAL_INDEX(uint16_t seed) { + return seed & 0x07UL; + } + + static constexpr uint16_t SIZE_INDEX(uint16_t seed) { + return (seed >> 3) & 0x07UL; + } + + static constexpr uint16_t ANGLE_OFFS_INDEX(uint16_t seed) { + return (seed >> 6) & 0x07UL; + } + + static constexpr uint16_t COLOR_INDEX(uint16_t seed) { + return (seed >> 9) % NUM_PETAL_COLORS; + } + + static constexpr int HEIGHT_FROM_STAGE(int max, int stage) { + return stage > 5 ? max * 3 / 4 : max * stage / 5; + } static constexpr uint32_t PETAL_COLORS[NUM_PETAL_COLORS] = {0xf43838, 0xf48829, 0xf9f44d, 0x56efe2, 0x6f7afc, 0x8f68f9, 0xdb49fc, 0xfc83b5};