Skip to content

Commit 96a3081

Browse files
Merge pull request #21 from ctrmint/feature/larger-countdown-font
Increase live countdown font size
2 parents 5c79065 + 5029797 commit 96a3081

3 files changed

Lines changed: 58 additions & 6 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ The timer utilizes the Waveshare 1.28-inch round touch display, allowing for con
3939

4040
The firmware includes a compact proportional bitmap font rendered directly at the display's native resolution. It replaces enlargement of MicroPython's 8x8 framebuffer font, so large countdown digits and labels retain smooth shapes instead of scaling into square pixels.
4141

42+
The running track and rest countdown uses the 74-pixel native font, the closest available pre-rendered size to a 10% increase from the previous 64-pixel countdown.
43+
4244
`font_data.py` and its flash-backed `font_data*.bin` glyph assets are generated from Montserrat SemiBold. The assets contain pre-rasterized native UI sizes, allowing the Pico to use its fast framebuffer blitter without holding the complete font in RAM. To regenerate them, install Pillow and run:
4345

4446
```sh

live_display.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77

88
LIVE_LOOP_DELAY_SEC = 0.05
9+
# Native font size 7 is 74 px: the nearest available size to 10% above the
10+
# previous 64 px countdown (target 70.4 px).
11+
COUNTDOWN_TEXT_SIZE = 7
912

1013

1114
def _visible_times(session, now):
@@ -21,13 +24,13 @@ def track_live_frame(session, now, lcd):
2124
"""Return all values visible on the track-session screen."""
2225
remaining, elapsed = _visible_times(session, now)
2326
if now >= session.end_time:
24-
return ("00:00", elapsed, 6, lcd.red, lcd.black)
27+
return ("00:00", elapsed, COUNTDOWN_TEXT_SIZE, lcd.red, lcd.black)
2528

2629
if now < session.last_15:
27-
return (remaining, elapsed, 6, None, None)
30+
return (remaining, elapsed, COUNTDOWN_TEXT_SIZE, None, None)
2831
if now < session.last_5:
29-
return (remaining, elapsed, 6, lcd.salmon, lcd.black)
30-
return (remaining, elapsed, 6, lcd.lilac, None)
32+
return (remaining, elapsed, COUNTDOWN_TEXT_SIZE, lcd.salmon, lcd.black)
33+
return (remaining, elapsed, COUNTDOWN_TEXT_SIZE, lcd.lilac, None)
3134

3235

3336
def rest_live_frame(session, now, lcd):
@@ -38,7 +41,7 @@ def rest_live_frame(session, now, lcd):
3841
return (
3942
remaining,
4043
elapsed,
41-
6,
44+
COUNTDOWN_TEXT_SIZE,
4245
lcd.blue,
4346
None,
4447
)

tests/test_live_display.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1+
import math
12
import unittest
23

3-
from live_display import rest_live_frame, run_live_display, track_live_frame
4+
from font_renderer import measure_text, pixel_height
5+
from live_display import (
6+
COUNTDOWN_TEXT_SIZE,
7+
rest_live_frame,
8+
run_live_display,
9+
track_live_frame,
10+
)
411
from timing import SessionTracker
512

613

@@ -26,6 +33,31 @@ class FakeLCD:
2633

2734

2835
class LiveDisplayTests(unittest.TestCase):
36+
def test_countdown_uses_nearest_native_size_to_ten_percent_larger(self):
37+
previous_height = pixel_height(6)
38+
target_height = previous_height * 1.10
39+
selected_height = pixel_height(COUNTDOWN_TEXT_SIZE)
40+
41+
self.assertEqual(7, COUNTDOWN_TEXT_SIZE)
42+
self.assertEqual(74, selected_height)
43+
self.assertLess(
44+
abs(selected_height - target_height),
45+
abs(previous_height - target_height),
46+
)
47+
48+
def test_maximum_countdown_width_fits_the_live_screen_position(self):
49+
display_radius = 120
50+
y_position = 82
51+
text_height = pixel_height(COUNTDOWN_TEXT_SIZE)
52+
text_width = measure_text("60:00", COUNTDOWN_TEXT_SIZE)
53+
54+
for edge_y in (y_position, y_position + text_height - 1):
55+
distance_from_center = edge_y - display_radius
56+
visible_width = 2 * math.sqrt(
57+
(display_radius ** 2) - (distance_from_center ** 2)
58+
)
59+
self.assertLessEqual(text_width, visible_width)
60+
2961
def test_simulated_session_redraws_at_most_once_per_visible_second(self):
3062
clock = FakeClock()
3163
lcd = FakeLCD()
@@ -111,12 +143,27 @@ def test_track_frames_include_warning_and_overrun_state(self):
111143
last_5 = track_live_frame(session, 670, lcd)
112144
overrun = track_live_frame(session, 700, lcd)
113145

146+
self.assertTrue(
147+
all(
148+
frame[2] == COUNTDOWN_TEXT_SIZE
149+
for frame in (running, last_15, last_5, overrun)
150+
)
151+
)
114152
self.assertEqual((None, None), running[3:])
115153
self.assertEqual((lcd.salmon, lcd.black), last_15[3:])
116154
self.assertEqual((lcd.lilac, None), last_5[3:])
117155
self.assertEqual("00:00", overrun[0])
118156
self.assertEqual((lcd.red, lcd.black), overrun[3:])
119157

158+
def test_rest_frame_uses_larger_countdown_size(self):
159+
lcd = FakeLCD()
160+
session = SessionTracker(duration_mins=10, clock=lambda: 100)
161+
session.start_session()
162+
163+
frame = rest_live_frame(session, 100, lcd)
164+
165+
self.assertEqual(COUNTDOWN_TEXT_SIZE, frame[2])
166+
120167
def test_loop_delay_must_be_bounded(self):
121168
session = SessionTracker(duration_mins=1, live=True)
122169
with self.assertRaises(ValueError):

0 commit comments

Comments
 (0)