|
|
|
|
@ -1,10 +1,15 @@
|
|
|
|
|
#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/draw/UIRenderer.h"
|
|
|
|
|
#include "graphics/emotes.h"
|
|
|
|
|
#include "graphics/images.h"
|
|
|
|
|
#include "main.h"
|
|
|
|
|
|
|
|
|
|
@ -18,31 +23,6 @@ namespace graphics
|
|
|
|
|
namespace ClockRenderer
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// Segment bitmaps for numerals 0-9 stored in flash to save RAM.
|
|
|
|
|
// Each row is a digit, each column is a segment state (1 = on, 0 = off).
|
|
|
|
|
// Segment layout reference:
|
|
|
|
|
//
|
|
|
|
|
// ___1___
|
|
|
|
|
// 6 | | 2
|
|
|
|
|
// |_7___|
|
|
|
|
|
// 5 | | 3
|
|
|
|
|
// |___4_|
|
|
|
|
|
//
|
|
|
|
|
// Segment order: [1, 2, 3, 4, 5, 6, 7]
|
|
|
|
|
//
|
|
|
|
|
static const uint8_t PROGMEM digitSegments[10][7] = {
|
|
|
|
|
{1, 1, 1, 1, 1, 1, 0}, // 0
|
|
|
|
|
{0, 1, 1, 0, 0, 0, 0}, // 1
|
|
|
|
|
{1, 1, 0, 1, 1, 0, 1}, // 2
|
|
|
|
|
{1, 1, 1, 1, 0, 0, 1}, // 3
|
|
|
|
|
{0, 1, 1, 0, 0, 1, 1}, // 4
|
|
|
|
|
{1, 0, 1, 1, 0, 1, 1}, // 5
|
|
|
|
|
{1, 0, 1, 1, 1, 1, 1}, // 6
|
|
|
|
|
{1, 1, 1, 0, 0, 1, 0}, // 7
|
|
|
|
|
{1, 1, 1, 1, 1, 1, 1}, // 8
|
|
|
|
|
{1, 1, 1, 1, 0, 1, 1} // 9
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale)
|
|
|
|
|
{
|
|
|
|
|
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
|
|
|
|
|
@ -50,7 +30,7 @@ void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale)
|
|
|
|
|
|
|
|
|
|
uint16_t cellHeight = (segmentWidth * 2) + (segmentHeight * 3) + 8;
|
|
|
|
|
|
|
|
|
|
uint16_t topAndBottomX = x + static_cast<uint16_t>(4 * scale);
|
|
|
|
|
uint16_t topAndBottomX = x + (4 * scale);
|
|
|
|
|
|
|
|
|
|
uint16_t quarterCellHeight = cellHeight / 4;
|
|
|
|
|
|
|
|
|
|
@ -63,16 +43,34 @@ void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale)
|
|
|
|
|
|
|
|
|
|
void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale)
|
|
|
|
|
{
|
|
|
|
|
// Read 7-segment pattern for the digit from flash
|
|
|
|
|
uint8_t seg[7];
|
|
|
|
|
for (uint8_t i = 0; i < 7; i++) {
|
|
|
|
|
seg[i] = pgm_read_byte(&digitSegments[number][i]);
|
|
|
|
|
}
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
|
// Precompute segment positions
|
|
|
|
|
// segment x and y coordinates
|
|
|
|
|
uint16_t segmentOneX = x + segmentHeight + 2;
|
|
|
|
|
uint16_t segmentOneY = y;
|
|
|
|
|
|
|
|
|
|
@ -94,21 +92,33 @@ void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t n
|
|
|
|
|
uint16_t segmentSevenX = segmentOneX;
|
|
|
|
|
uint16_t segmentSevenY = segmentTwoY + segmentWidth + 2;
|
|
|
|
|
|
|
|
|
|
// Draw only the active segments
|
|
|
|
|
if (seg[0])
|
|
|
|
|
drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight);
|
|
|
|
|
if (seg[1])
|
|
|
|
|
drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight);
|
|
|
|
|
if (seg[2])
|
|
|
|
|
drawVerticalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight);
|
|
|
|
|
if (seg[3])
|
|
|
|
|
drawHorizontalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight);
|
|
|
|
|
if (seg[4])
|
|
|
|
|
drawVerticalSegment(display, segmentFiveX, segmentFiveY, segmentWidth, segmentHeight);
|
|
|
|
|
if (seg[5])
|
|
|
|
|
drawVerticalSegment(display, segmentSixX, segmentSixY, segmentWidth, segmentHeight);
|
|
|
|
|
if (seg[6])
|
|
|
|
|
drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight);
|
|
|
|
|
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)
|
|
|
|
|
@ -137,6 +147,42 @@ void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int heig
|
|
|
|
|
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();
|
|
|
|
|
@ -155,8 +201,17 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|
|
|
|
|
|
|
|
|
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
|
|
|
|
|
char timeString[16];
|
|
|
|
|
int hour, minute, second;
|
|
|
|
|
decomposeTime(rtc_sec, hour, minute, second);
|
|
|
|
|
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;
|
|
|
|
|
@ -188,10 +243,9 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|
|
|
|
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
|
|
|
|
|
|
|
|
|
|
// calculate hours:minutes string width
|
|
|
|
|
size_t len = strlen(timeString);
|
|
|
|
|
uint16_t timeStringWidth = len * 5;
|
|
|
|
|
uint16_t timeStringWidth = strlen(timeString) * 5;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
|
for (uint8_t i = 0; i < strlen(timeString); i++) {
|
|
|
|
|
char character = timeString[i];
|
|
|
|
|
|
|
|
|
|
if (character == ':') {
|
|
|
|
|
@ -208,7 +262,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
|
|
|
|
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 < len; i++) {
|
|
|
|
|
for (uint8_t i = 0; i < strlen(timeString); i++) {
|
|
|
|
|
char character = timeString[i];
|
|
|
|
|
|
|
|
|
|
if (character == ':') {
|
|
|
|
|
@ -273,7 +327,12 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|
|
|
|
int16_t centerY = display->getHeight() / 2;
|
|
|
|
|
|
|
|
|
|
// clock face radius
|
|
|
|
|
int16_t radius = (std::min(display->getWidth(), display->getHeight()) / 2) * 0.9;
|
|
|
|
|
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
|
|
|
|
|
@ -288,8 +347,17 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|
|
|
|
// tick mark outer y coordinate; (first nested circle)
|
|
|
|
|
int16_t tickMarkOuterNoonY = secondHandNoonY;
|
|
|
|
|
|
|
|
|
|
double secondsTickMarkInnerNoonY = noonY + (isHighResolution ? 8 : 4);
|
|
|
|
|
double hoursTickMarkInnerNoonY = noonY + (isHighResolution ? 16 : 6);
|
|
|
|
|
// 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;
|
|
|
|
|
@ -309,11 +377,17 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|
|
|
|
|
|
|
|
|
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
|
|
|
|
|
if (rtc_sec > 0) {
|
|
|
|
|
int hour, minute, second;
|
|
|
|
|
decomposeTime(rtc_sec, hour, minute, second);
|
|
|
|
|
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;
|
|
|
|
|
isPM = hour >= 12;
|
|
|
|
|
display->setFont(FONT_SMALL);
|
|
|
|
|
int yOffset = isHighResolution ? 1 : 0;
|
|
|
|
|
#ifdef USE_EINK
|
|
|
|
|
@ -326,8 +400,8 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|
|
|
|
if (hour == 0)
|
|
|
|
|
hour = 12;
|
|
|
|
|
|
|
|
|
|
constexpr int16_t degreesPerHour = 30;
|
|
|
|
|
constexpr int16_t degreesPerMinuteOrSecond = 6;
|
|
|
|
|
int16_t degreesPerHour = 30;
|
|
|
|
|
int16_t degreesPerMinuteOrSecond = 6;
|
|
|
|
|
|
|
|
|
|
double hourBaseAngle = hour * degreesPerHour;
|
|
|
|
|
double hourAngleOffset = ((double)minute / 60) * degreesPerHour;
|
|
|
|
|
|