mirror of
https://github.com/meshtastic/firmware.git
synced 2025-08-13 16:55:08 +00:00

* Update Favorite Node Message Options to unify against other screens * Rebuild Horizontal Battery, Resolve overlap concerns * Update positioning on Message frame and fix drawCommonHeader overlay * Beginnings of creating isHighResolution bool * Fixup determineResolution() * Implement isHighResolution in place of SCREEN_WIDTH > 128 checks * Line Spacing bound to isHighResolution * Analog Clock for all * Add AM/PM to Analog Clock if isHighResolution and not TWatch * Simple Menu Queue, and add time menu * Fix prompt string for 12/24 hour picker * More menu banners into functions * Fix Action Menu on Home frame * Correct pop-up calculation size and continue to leverage isHighResolution * Move menu bits to MenuHandler * Plumb in the digital/analog picker * Correct Clock Face Picker title * Clock picker fixes * Migrate the rest of the menus to MenuHandler.* * Add compass menu and needle point option * Minor fix for compass point menu * Correct Home menu into typical format * Fix emoji bounce, overlap, and missing commonHeader * Sanitize long_names and removed unused variables * Slightly better sanitizeString variation * Resolved apostrophe being shown as upside down question mark * Gotta keep height and width in expected order * Remove Second Hand for Analog Clock on EInk displays * Fix Clock menu option decision tree * Improvements to Eink Navigation * Pause Banner for Eink moved to bottom * Updated working for 12-/24-hour menu and Added US/Arizona to timezone picker * Add Adhoc Ping and resolve error with std::string sanitized * Hide quick toggle as option is available within Action Menu, commented out for the moment * Remove old battery icon and option, use drawCommonHeader throughout, re-add battery to Clock frames * fix misc build warnings. NFC * Update Analog Clock on EInk to show more digits * Establish Action Menu on all node list screens, add NodeDB reset (with confirmation) option * Add Toggle Backlight for EInk Displays * Suppress action screen Full refresh for Eink * Adjust drawBluetoothConnectedIcon on TWatch * Maintain clock frame when switching between Clock Faces * Move modules beyond the clock in navigation * addressed the conflicts, and changed target branch to 2.7-MiscFixes-Week1 * cleanup, cheers * Add AM/PM to low resolution clock also * Small adjustments to AM/PM replacement across various devices * Resolve dangling pointer issues with sanitize code * Update comments for Screen.cpp related to module load change * Trunk runs * Update message caching to correct aged timestamp * Menu wording adjustments * Time Format wording * Use all the rows on EInk since with autohide the navigation bar * Finalize Time Format picker word change * Retired drawFunctionOverlay code No longer being used * Actually honor the points-north setting * Trunk * Compressed action list * Update no-op showOverlayBanner function * trunk * Correct T_Watch_S3 specific line * Autosized Action menu per screen * Finalize Autosized Action menu per screen * Unify Message Titles * Reorder Timezones to match expectations * Adjust text location for pop-ups * Revert "Actually honor the points-north setting" This reverts commit20988aa4fa
. * Make NodeDB sort its internal vector when lastheard is updated. Don't sort in NodeListRenderer * Update src/graphics/draw/NodeListRenderer.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/mesh/NodeDB.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Pass by reference -- Thanks Copilot! * Throttle sorting just a touch * Check more carefully for own node * Eliminate some now-unneeded sorting * Move function after include * Putting Modules back to position 0 and some trunk checks found * Add Scrollbar for Action menus * Second attempt to move modules down the navigation bar * Continue effort of moving modules in the navigation * Canned Messages tweak * Replicate Function + Space through the Menu System * Move init button parameters into config struct (#7145) * Remove bundling of web-ui from ESP32 devices (#7143) * Fixed triple click GPS toggle bungle * Move init button parameters into config struct * Reapply "Actually honor the points-north setting" This reverts commit42c1967e7b
. * Actually do compass pointings correctly * Tweak to node bearings * Menu wording tweaks * Get the compass_north_top logic right * Don't jump frames after setting Compass * Get rid of the extra bearingTo functions * Don't blink Mail on EInk Clock Screens * Actually set lat and long * Calibrate * Convert Radians to Degrees * More degree vs radians fixes * De-duplicate draw arrow function * Don't advertise compass calibration without an accell thread. --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz> Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com> Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com> Co-authored-by: csrutil <keming.cao@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
514 lines
18 KiB
C++
514 lines
18 KiB
C++
#include "configuration.h"
|
|
#if HAS_SCREEN
|
|
#include "ClockRenderer.h"
|
|
#include "NodeDB.h"
|
|
#include "UIRenderer.h"
|
|
#include "configuration.h"
|
|
#include "gps/GeoCoord.h"
|
|
#include "gps/RTC.h"
|
|
#include "graphics/ScreenFonts.h"
|
|
#include "graphics/SharedUIDisplay.h"
|
|
#include "graphics/emotes.h"
|
|
#include "graphics/images.h"
|
|
#include "main.h"
|
|
|
|
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
|
#include "nimble/NimbleBluetooth.h"
|
|
#endif
|
|
|
|
namespace graphics
|
|
{
|
|
|
|
namespace ClockRenderer
|
|
{
|
|
bool digitalWatchFace = true;
|
|
|
|
void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale)
|
|
{
|
|
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
|
|
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
|
|
|
|
uint16_t cellHeight = (segmentWidth * 2) + (segmentHeight * 3) + 8;
|
|
|
|
uint16_t topAndBottomX = x + (4 * scale);
|
|
|
|
uint16_t quarterCellHeight = cellHeight / 4;
|
|
|
|
uint16_t topY = y + quarterCellHeight;
|
|
uint16_t bottomY = y + (quarterCellHeight * 3);
|
|
|
|
display->fillRect(topAndBottomX, topY, segmentHeight, segmentHeight);
|
|
display->fillRect(topAndBottomX, bottomY, segmentHeight, segmentHeight);
|
|
}
|
|
|
|
void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale)
|
|
{
|
|
// the numbers 0-9, each expressed as an array of seven boolean (0|1) values encoding the on/off state of
|
|
// segment {innerIndex + 1}
|
|
// e.g., to display the numeral '0', segments 1-6 are on, and segment 7 is off.
|
|
uint8_t numbers[10][7] = {
|
|
{1, 1, 1, 1, 1, 1, 0}, // 0 Display segment key
|
|
{0, 1, 1, 0, 0, 0, 0}, // 1 1
|
|
{1, 1, 0, 1, 1, 0, 1}, // 2 ___
|
|
{1, 1, 1, 1, 0, 0, 1}, // 3 6 | | 2
|
|
{0, 1, 1, 0, 0, 1, 1}, // 4 |_7̲_|
|
|
{1, 0, 1, 1, 0, 1, 1}, // 5 5 | | 3
|
|
{1, 0, 1, 1, 1, 1, 1}, // 6 |___|
|
|
{1, 1, 1, 0, 0, 1, 0}, // 7
|
|
{1, 1, 1, 1, 1, 1, 1}, // 8 4
|
|
{1, 1, 1, 1, 0, 1, 1}, // 9
|
|
};
|
|
|
|
// the width and height of each segment's central rectangle:
|
|
// _____________________
|
|
// ⋰| (only this part, |⋱
|
|
// ⋰ | not including | ⋱
|
|
// ⋱ | the triangles | ⋰
|
|
// ⋱| on the ends) |⋰
|
|
// ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
|
|
|
|
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
|
|
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
|
|
|
|
// segment x and y coordinates
|
|
uint16_t segmentOneX = x + segmentHeight + 2;
|
|
uint16_t segmentOneY = y;
|
|
|
|
uint16_t segmentTwoX = segmentOneX + segmentWidth + 2;
|
|
uint16_t segmentTwoY = segmentOneY + segmentHeight + 2;
|
|
|
|
uint16_t segmentThreeX = segmentTwoX;
|
|
uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2 + segmentHeight + 2;
|
|
|
|
uint16_t segmentFourX = segmentOneX;
|
|
uint16_t segmentFourY = segmentThreeY + segmentWidth + 2;
|
|
|
|
uint16_t segmentFiveX = x;
|
|
uint16_t segmentFiveY = segmentThreeY;
|
|
|
|
uint16_t segmentSixX = x;
|
|
uint16_t segmentSixY = segmentTwoY;
|
|
|
|
uint16_t segmentSevenX = segmentOneX;
|
|
uint16_t segmentSevenY = segmentTwoY + segmentWidth + 2;
|
|
|
|
if (numbers[number][0]) {
|
|
graphics::ClockRenderer::drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight);
|
|
}
|
|
|
|
if (numbers[number][1]) {
|
|
graphics::ClockRenderer::drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight);
|
|
}
|
|
|
|
if (numbers[number][2]) {
|
|
graphics::ClockRenderer::drawVerticalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight);
|
|
}
|
|
|
|
if (numbers[number][3]) {
|
|
graphics::ClockRenderer::drawHorizontalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight);
|
|
}
|
|
|
|
if (numbers[number][4]) {
|
|
graphics::ClockRenderer::drawVerticalSegment(display, segmentFiveX, segmentFiveY, segmentWidth, segmentHeight);
|
|
}
|
|
|
|
if (numbers[number][5]) {
|
|
graphics::ClockRenderer::drawVerticalSegment(display, segmentSixX, segmentSixY, segmentWidth, segmentHeight);
|
|
}
|
|
|
|
if (numbers[number][6]) {
|
|
graphics::ClockRenderer::drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight);
|
|
}
|
|
}
|
|
|
|
void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height)
|
|
{
|
|
int halfHeight = height / 2;
|
|
|
|
// draw central rectangle
|
|
display->fillRect(x, y, width, height);
|
|
|
|
// draw end triangles
|
|
display->fillTriangle(x, y, x, y + height - 1, x - halfHeight, y + halfHeight);
|
|
|
|
display->fillTriangle(x + width, y, x + width + halfHeight, y + halfHeight, x + width, y + height - 1);
|
|
}
|
|
|
|
void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height)
|
|
{
|
|
int halfHeight = height / 2;
|
|
|
|
// draw central rectangle
|
|
display->fillRect(x, y, height, width);
|
|
|
|
// draw end triangles
|
|
display->fillTriangle(x + halfHeight, y - halfHeight, x + height - 1, y, x, y);
|
|
|
|
display->fillTriangle(x, y + width, x + height - 1, y + width, x + halfHeight, y + width + halfHeight);
|
|
}
|
|
|
|
/*
|
|
void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode, float scale)
|
|
{
|
|
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
|
|
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
|
|
|
|
if (digitalMode) {
|
|
uint16_t radius = (segmentWidth + (segmentHeight * 2) + 4) / 2;
|
|
uint16_t centerX = (x + segmentHeight + 2) + (radius / 2);
|
|
uint16_t centerY = (y + segmentHeight + 2) + (radius / 2);
|
|
|
|
display->drawCircle(centerX, centerY, radius);
|
|
display->drawCircle(centerX, centerY, radius + 1);
|
|
display->drawLine(centerX, centerY, centerX, centerY - radius + 3);
|
|
display->drawLine(centerX, centerY, centerX + radius - 3, centerY);
|
|
} else {
|
|
uint16_t segmentOneX = x + segmentHeight + 2;
|
|
uint16_t segmentOneY = y;
|
|
|
|
uint16_t segmentTwoX = segmentOneX + segmentWidth + 2;
|
|
uint16_t segmentTwoY = segmentOneY + segmentHeight + 2;
|
|
|
|
uint16_t segmentThreeX = segmentOneX;
|
|
uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2;
|
|
|
|
uint16_t segmentFourX = x;
|
|
uint16_t segmentFourY = y + segmentHeight + 2;
|
|
|
|
drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight);
|
|
drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight);
|
|
drawHorizontalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight);
|
|
drawVerticalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight);
|
|
}
|
|
}
|
|
*/
|
|
// Draw a digital clock
|
|
void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
|
{
|
|
display->clear();
|
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
int line = 1;
|
|
// === Set Title, Blank for Clock
|
|
const char *titleStr = "";
|
|
// === Header ===
|
|
graphics::drawCommonHeader(display, x, y, titleStr, true);
|
|
|
|
#ifdef T_WATCH_S3
|
|
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
|
graphics::ClockRenderer::drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
|
|
}
|
|
#endif
|
|
|
|
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
|
|
char timeString[16];
|
|
int hour = 0;
|
|
int minute = 0;
|
|
int second = 0;
|
|
if (rtc_sec > 0) {
|
|
long hms = rtc_sec % SEC_PER_DAY;
|
|
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
|
|
|
hour = hms / SEC_PER_HOUR;
|
|
minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
|
second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
|
}
|
|
|
|
bool isPM = hour >= 12;
|
|
// hour = hour > 12 ? hour - 12 : hour;
|
|
if (config.display.use_12h_clock) {
|
|
hour %= 12;
|
|
if (hour == 0)
|
|
hour = 12;
|
|
bool isPM = hour >= 12;
|
|
snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
|
|
} else {
|
|
snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
|
|
}
|
|
|
|
// Format seconds string
|
|
char secondString[8];
|
|
snprintf(secondString, sizeof(secondString), "%02d", second);
|
|
|
|
#ifdef T_WATCH_S3
|
|
float scale = 1.5;
|
|
#else
|
|
float scale = 0.75;
|
|
if (isHighResolution) {
|
|
scale = 1.5;
|
|
}
|
|
#endif
|
|
|
|
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
|
|
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
|
|
|
|
// calculate hours:minutes string width
|
|
uint16_t timeStringWidth = strlen(timeString) * 5;
|
|
|
|
for (uint8_t i = 0; i < strlen(timeString); i++) {
|
|
char character = timeString[i];
|
|
|
|
if (character == ':') {
|
|
timeStringWidth += segmentHeight;
|
|
} else {
|
|
timeStringWidth += segmentWidth + (segmentHeight * 2) + 4;
|
|
}
|
|
}
|
|
|
|
uint16_t hourMinuteTextX = (display->getWidth() / 2) - (timeStringWidth / 2);
|
|
|
|
uint16_t startingHourMinuteTextX = hourMinuteTextX;
|
|
|
|
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2);
|
|
|
|
// iterate over characters in hours:minutes string and draw segmented characters
|
|
for (uint8_t i = 0; i < strlen(timeString); i++) {
|
|
char character = timeString[i];
|
|
|
|
if (character == ':') {
|
|
drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
|
|
|
|
hourMinuteTextX += segmentHeight + 6;
|
|
} else {
|
|
drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character - '0', scale);
|
|
|
|
hourMinuteTextX += segmentWidth + (segmentHeight * 2) + 4;
|
|
}
|
|
|
|
hourMinuteTextX += 5;
|
|
}
|
|
|
|
// draw seconds string
|
|
display->setFont(FONT_SMALL);
|
|
int xOffset = (isHighResolution) ? 0 : -1;
|
|
if (hour >= 10) {
|
|
xOffset += (isHighResolution) ? 32 : 18;
|
|
}
|
|
int yOffset = (isHighResolution) ? 3 : 1;
|
|
if (config.display.use_12h_clock) {
|
|
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
|
|
isPM ? "pm" : "am");
|
|
}
|
|
#ifndef USE_EINK
|
|
xOffset = (isHighResolution) ? 18 : 10;
|
|
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
|
|
secondString);
|
|
#endif
|
|
}
|
|
|
|
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y)
|
|
{
|
|
display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
|
|
}
|
|
|
|
// Draw an analog clock
|
|
void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
|
{
|
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
// === Set Title, Blank for Clock
|
|
const char *titleStr = "";
|
|
// === Header ===
|
|
graphics::drawCommonHeader(display, x, y, titleStr, true);
|
|
|
|
#ifdef T_WATCH_S3
|
|
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
|
|
drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
|
|
}
|
|
#endif
|
|
// clock face center coordinates
|
|
int16_t centerX = display->getWidth() / 2;
|
|
int16_t centerY = display->getHeight() / 2;
|
|
|
|
// clock face radius
|
|
int16_t radius = 0;
|
|
if (display->getHeight() < display->getWidth()) {
|
|
radius = (display->getHeight() / 2) * 0.9;
|
|
} else {
|
|
radius = (display->getWidth() / 2) * 0.9;
|
|
}
|
|
#ifdef T_WATCH_S3
|
|
radius = (display->getWidth() / 2) * 0.8;
|
|
#endif
|
|
|
|
// noon (0 deg) coordinates (outermost circle)
|
|
int16_t noonX = centerX;
|
|
int16_t noonY = centerY - radius;
|
|
|
|
// second hand radius and y coordinate (outermost circle)
|
|
int16_t secondHandNoonY = noonY + 1;
|
|
|
|
// tick mark outer y coordinate; (first nested circle)
|
|
int16_t tickMarkOuterNoonY = secondHandNoonY;
|
|
|
|
// seconds tick mark inner y coordinate; (second nested circle)
|
|
double secondsTickMarkInnerNoonY = (double)noonY + 4;
|
|
if (isHighResolution) {
|
|
secondsTickMarkInnerNoonY = (double)noonY + 8;
|
|
}
|
|
|
|
// hours tick mark inner y coordinate; (third nested circle)
|
|
double hoursTickMarkInnerNoonY = (double)noonY + 6;
|
|
if (isHighResolution) {
|
|
hoursTickMarkInnerNoonY = (double)noonY + 16;
|
|
}
|
|
|
|
// minute hand y coordinate
|
|
int16_t minuteHandNoonY = secondsTickMarkInnerNoonY + 4;
|
|
|
|
// hour string y coordinate
|
|
int16_t hourStringNoonY = minuteHandNoonY + 18;
|
|
|
|
// hour hand radius and y coordinate
|
|
int16_t hourHandRadius = radius * 0.35;
|
|
if (isHighResolution) {
|
|
int16_t hourHandRadius = radius * 0.55;
|
|
}
|
|
int16_t hourHandNoonY = centerY - hourHandRadius;
|
|
|
|
display->setColor(OLEDDISPLAY_COLOR::WHITE);
|
|
display->drawCircle(centerX, centerY, radius);
|
|
|
|
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
|
|
if (rtc_sec > 0) {
|
|
long hms = rtc_sec % SEC_PER_DAY;
|
|
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
|
|
|
// Tear apart hms into h:m:s
|
|
int hour = hms / SEC_PER_HOUR;
|
|
int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
|
int second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
|
|
|
bool isPM = hour >= 12;
|
|
if (config.display.use_12h_clock) {
|
|
bool isPM = hour >= 12;
|
|
display->setFont(FONT_SMALL);
|
|
int yOffset = isHighResolution ? 1 : 0;
|
|
#ifdef USE_EINK
|
|
yOffset += 3;
|
|
#endif
|
|
display->drawString(centerX - (display->getStringWidth(isPM ? "pm" : "am") / 2), centerY + yOffset,
|
|
isPM ? "pm" : "am");
|
|
}
|
|
hour %= 12;
|
|
if (hour == 0)
|
|
hour = 12;
|
|
|
|
int16_t degreesPerHour = 30;
|
|
int16_t degreesPerMinuteOrSecond = 6;
|
|
|
|
double hourBaseAngle = hour * degreesPerHour;
|
|
double hourAngleOffset = ((double)minute / 60) * degreesPerHour;
|
|
double hourAngle = radians(hourBaseAngle + hourAngleOffset);
|
|
|
|
double minuteBaseAngle = minute * degreesPerMinuteOrSecond;
|
|
double minuteAngleOffset = ((double)second / 60) * degreesPerMinuteOrSecond;
|
|
double minuteAngle = radians(minuteBaseAngle + minuteAngleOffset);
|
|
|
|
double secondAngle = radians(second * degreesPerMinuteOrSecond);
|
|
|
|
double hourX = sin(-hourAngle) * (hourHandNoonY - centerY) + noonX;
|
|
double hourY = cos(-hourAngle) * (hourHandNoonY - centerY) + centerY;
|
|
|
|
double minuteX = sin(-minuteAngle) * (minuteHandNoonY - centerY) + noonX;
|
|
double minuteY = cos(-minuteAngle) * (minuteHandNoonY - centerY) + centerY;
|
|
|
|
double secondX = sin(-secondAngle) * (secondHandNoonY - centerY) + noonX;
|
|
double secondY = cos(-secondAngle) * (secondHandNoonY - centerY) + centerY;
|
|
|
|
display->setFont(FONT_MEDIUM);
|
|
|
|
// draw minute and hour tick marks and hour numbers
|
|
for (uint16_t angle = 0; angle < 360; angle += 6) {
|
|
double angleInRadians = radians(angle);
|
|
|
|
double sineAngleInRadians = sin(-angleInRadians);
|
|
double cosineAngleInRadians = cos(-angleInRadians);
|
|
|
|
double endX = sineAngleInRadians * (tickMarkOuterNoonY - centerY) + noonX;
|
|
double endY = cosineAngleInRadians * (tickMarkOuterNoonY - centerY) + centerY;
|
|
|
|
if (angle % degreesPerHour == 0) {
|
|
double startX = sineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + noonX;
|
|
double startY = cosineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + centerY;
|
|
|
|
// draw hour tick mark
|
|
display->drawLine(startX, startY, endX, endY);
|
|
|
|
static char buffer[2];
|
|
|
|
uint8_t hourInt = (angle / 30);
|
|
|
|
if (hourInt == 0) {
|
|
hourInt = 12;
|
|
}
|
|
|
|
// hour number x offset needs to be adjusted for some cases
|
|
int8_t hourStringXOffset;
|
|
int8_t hourStringYOffset = 13;
|
|
|
|
switch (hourInt) {
|
|
case 3:
|
|
hourStringXOffset = 5;
|
|
break;
|
|
case 9:
|
|
hourStringXOffset = 7;
|
|
break;
|
|
case 10:
|
|
case 11:
|
|
hourStringXOffset = 8;
|
|
break;
|
|
case 12:
|
|
hourStringXOffset = 13;
|
|
break;
|
|
default:
|
|
hourStringXOffset = 6;
|
|
break;
|
|
}
|
|
|
|
double hourStringX = (sineAngleInRadians * (hourStringNoonY - centerY) + noonX) - hourStringXOffset;
|
|
double hourStringY = (cosineAngleInRadians * (hourStringNoonY - centerY) + centerY) - hourStringYOffset;
|
|
|
|
#ifdef T_WATCH_S3
|
|
// draw hour number
|
|
display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
|
|
#else
|
|
#ifdef USE_EINK
|
|
if (isHighResolution) {
|
|
// draw hour number
|
|
display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
|
|
}
|
|
#else
|
|
if (isHighResolution && (hourInt == 3 || hourInt == 6 || hourInt == 9 || hourInt == 12)) {
|
|
// draw hour number
|
|
display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
if (angle % degreesPerMinuteOrSecond == 0) {
|
|
double startX = sineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + noonX;
|
|
double startY = cosineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + centerY;
|
|
|
|
if (isHighResolution) {
|
|
// draw minute tick mark
|
|
display->drawLine(startX, startY, endX, endY);
|
|
}
|
|
}
|
|
}
|
|
|
|
// draw hour hand
|
|
display->drawLine(centerX, centerY, hourX, hourY);
|
|
|
|
// draw minute hand
|
|
display->drawLine(centerX, centerY, minuteX, minuteY);
|
|
#ifndef USE_EINK
|
|
// draw second hand
|
|
display->drawLine(centerX, centerY, secondX, secondY);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
} // namespace ClockRenderer
|
|
|
|
} // namespace graphics
|
|
#endif |