Round display support (gabbro): round template variants and arc status (issue #62) #125
No reviewers
Labels
No labels
area:companion
area:docs
area:shared
area:tooling
area:watchapp
blocker
kind:chore
kind:feature
kind:spike
kind:test
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
robert/PedalPebble!125
Loading…
Reference in a new issue
No description provided.
Delete branch "area/round-display-support"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes most of #62 — see "Scoped out" below for what is deliberately not in this PR.
Root cause fixed: page_render_content_rect_round()
page_render_content_rect()used to return gabbro's raw 260x240 rectangular bounding box, which is what #121 flagged as the source of the ~16px bezel overshoot on HERO1 and the wrong (non-fallback) GRID6 threshold decision. This PR addspage_render_content_rect_round(bounds): the largest square inscribed in the circleboundsdescribes, centred in it, side computed asdiameter/sqrt(2)via an integer Newton's-method-styleprv_isqrt_round()(no<math.h>, no float anywhere inpage_render_geometry.c— consistent with that file's own existing rule). For gabbro (260x260) this is exactly 184x184, matching D24/DESIGN.md's own figure.Critically, every other template composes with this unchanged:
page_render_hero1_hero_rect(content)— same function, just handed the round square instead of the rectangle.page_render_quad_cell_rect(content, slot)— same function; 184/2 = 92 exactly, so QUAD's four cells are 92x92 with no remainder, matching the issue's own number.page_render_resolve_template(requested, content)— same function, zero gabbro-specific code; a 184x184 square gives a 92x61 GRID6 cell, under the existing 100x69 threshold on both axes, so GRID6 falls back to QUAD automatically, exactly like basalt.This is the "every other function in this file can then correctly build on it" mechanism the issue asked for, rather than each template inventing its own round math.
HERO2: the one template that needed real chord math
HERO2 is different: DESIGN.md's own words are "hero centred, the two cells inset to the chord width at their vertical position", and re-using the shared inscribed square would under-use the wider chord available near the circle's true vertical centre (238px there vs the square's fixed 184px). So
page_render_hero2_round_hero_rect(bounds)/page_render_hero2_round_cell_rect(bounds, index)take the full round screen, notcontent, and compute each row's own safe half-width viaprv_chord_half_width()(Pythagoras, again via the integer isqrt) at the row's farther edge from centre — a rectangle sized this way is provably inside the circle on every corner (proof and a direct per-corner host-test assertion are both in the code/tests, not just argued in a comment). On gabbro this gives a 196px hero (wider than the rectangle's own 200px, despite gabbro's smaller inscribed square) and ~99x65 cells (bigger than QUAD's 92x92).Status arc
page_render_status_arc_slot(slot)(page_render_geometry.c, host-tested, no SDK) reports each of the same five status slots as an angular span inTRIG_MAX_ANGLEunits, -60°..+60° from the top of the circle, same "clock gets 2/5, four icons split the rest evenly" rule as the rectangular strip.page_render.c'sprv_draw_status_arc()is the one place that turns those angles into pixels, viagraphics_draw_arc()(the visible reference arc),grect_centered_from_polar()(each icon/clock's own centred rect) andgpoint_from_polar()'s underlying math — checked directly against the installed Core Devices SDK 4.33.1 header, not assumed from older docs. Every icon reuses the exact sameprv_draw_bluetooth_glyph()/prv_draw_gps_quality_glyph()/prv_draw_speed_source_glyph()/prv_draw_ride_state_glyph()functions the rectangular strip already uses — they only ever look at thePageRectthey're handed.Composes with #121, doesn't bypass it
page_render_fit_digit_height()(issue #121, width-only) is kept exactly as it shipped — same signature, same behaviour, all its own tests pass unmodified — by delegating to a newpage_render_fit_digit_height_2d(text, max_width, max_height, requested_height). The new function exists because round's tighter cells exposed a real vertical-overflow case #121 never had to handle: a digit-height ceiling that exceeds its own value area's height, for a value short enough that the width check alone never catches it. Two concrete, checked cases: HERO1's round 170px ceiling against its own 164px value area (184 square minus the 20px label strip), and QUAD's existing, unchanged 76px emphasised ceiling against a round 92x92 cell's 72px value area — the literal "QUAD's emphasised slot 1 (D29) works within the inscribed square" acceptance criterion.prv_draw_cell()(the one production call site) now calls the 2D version on every template, round or not; on emery/basalt this is a no-op, verified directly by a new host test asserting no existing ceiling ever exceeded its value area's height there.Host tests (issue #68)
20 new assertions in
test_page_render_geometry.c, 67/67 passing (up from 47/47): the round content rect's exact geometry (cross-checked against a real floating-pointsqrt()at a size other than gabbro's own, so it isn't just re-deriving the number the function was written against), GRID6→QUAD fallback composing correctly, QUAD's 92x92 cells and the D29 emphasised-slot criterion as a literal assertion, the status arc's angular tiling, HERO1's round ceiling, HERO2's rows checked directly against the circle equation (dx² + dy² ≤ r²on all four corners, not trusted from a comment), and the height-aware fit clamp — including a comprehensivetest_no_slot_exceeds_its_bounds_on_{emery_or_basalt,gabbro_round}pair that is the issue's own "no slot exceeds its bounds ... as a real assertion, not a look" criterion, verbatim.All four host suites (
test_fields/test_page/test_state/test_backlight/test_page_render_geometry) hand-compiled withgcc -std=c11 -Wall -Wextra -Werrorand run directly (no cmake in this sandbox): 28+16+55+27+67 = 193 assertions, 0 failures.Real build + emulator verification
pebble buildsucceeds clean on all three targets (emery/gabbro/basalt), no warnings. Screenshots taken in the real gabbro emulator: HERO2 (Ride page — chord-inset cells + status arc), QUAD (Effort page — 92x92 inscribed-square cells), and HERO1 (temporarily swapped into the Ride slot for screenshot purposes only — no default page ships HERO1 yet — then fully reverted,git diffonpage.cis clean). emery and basalt were re-screenshotted after this change too, to confirm no regression on the rectangular path.Scoped out: NAV and MAP
page_render.c's own dispatch switch has said for a while now that "NAV/MAP have no carousel entry point at all today" — confirmed again before starting this PR: issues #35 (view_nav) and #42 (view_map polyline renderer) are both still open, so there is no live NAV or MAP page anywhere in this app to lay out round-natively, let alone verify in the gabbro emulator. Building a temporary NAV/MAP page just to exercise this issue's own NAV/MAP criteria would be building real, unreviewed scope for #35/#42 as a side effect, so this PR does not do that. Dependency edges recorded on #62: depends on #35 and #42 (checked both for cycles first — neither depends, directly or transitively, on #62 — no cycle introduced).What's covered vs deferred, against the issue's own checklist
graphics_capture_frame_buffer()anywhere, grepped); flagged rather than silently skipped, same precedent as the existing GPath/heap-allocation note in this fileFiles:
watchapp/src/c/page_render_geometry.h/.c,watchapp/src/c/page_render.c/.h,watchapp/src/c/carousel.c(the text-flow-and-paging criterion),watchapp/tests/test_page_render_geometry.c. Nostatus_bar.c— the status strip/arc has always lived insidepage_render.c/page_render_geometry.c, confirmed by reading the current tree before starting rather than assuming the issue's file list.