garmin-decimalface/CLAUDE.md
2025-11-04 17:49:07 +01:00

5.7 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

This is a novelty Garmin Connect IQ watchface that displays time as decimal day progress (French Revolutionary Time style) instead of traditional 12/24 hour format. The day is divided into 10 equal parts, with time displayed as a decimal from 0.0 to 10.0.

Key Concept: The watchface converts standard time to decimal using the formula:

decimalTime = (hours + minutes/60 + seconds/3600) / 24 * 10

This means:

  • Midnight (00:00) → 0.0
  • 6:00 AM → 2.5
  • Noon (12:00) → 5.0
  • 6:00 PM → 7.5
  • End of day → 10.0

Development Commands

Building the Watchface

The project uses the Garmin Connect IQ SDK. You need the SDK installed with $SDK_PATH environment variable set.

Build for simulator:

$SDK_PATH/bin/monkeyc \
    -o bin/MetricWatchFace.prg \
    -f monkey.jungle \
    -y $SDK_PATH/bin/developer_key

Run in simulator:

$SDK_PATH/bin/connectiq
# Then load the generated .prg file in the simulator

Build for specific device (example for Fenix 7):

$SDK_PATH/bin/monkeyc \
    -o bin/MetricWatchFace.prg \
    -f monkey.jungle \
    -d fenix7 \
    -y $SDK_PATH/bin/developer_key

Note: The project uses monkey.jungle for build configuration, which references manifest.xml for project settings.

Architecture

Core Components

DecimalWatchFaceApp.mc (source/DecimalWatchFaceApp.mc:4-23)

  • Application entry point extending Application.AppBase
  • Minimal setup - primarily returns the watchface view
  • No complex application state management

DecimalWatchFaceView.mc (source/DecimalWatchFaceView.mc:8-201)

  • Main watchface implementation extending WatchUi.WatchFace
  • Handles all rendering and time calculation logic
  • Key methods:
    • onUpdate(dc) - Main render loop, called every second when active
    • computeDecimalTime(clockTime) - Converts standard time to decimal (0.0-10.0)
    • drawWatchFace(dc) - Renders the analog face with 10 divisions, tick marks, and numbers
    • drawHand(dc, decimalTime) - Draws both hour and minute hands
    • drawSingleHand(...) - Helper for rendering individual hands as filled polygons
    • drawDigitalTime(dc, decimalTime) - Displays decimal time as text

Rendering Architecture

The watchface uses programmatic drawing (no XML layouts). All graphics are drawn using Monkey C Graphics API:

  1. Watchface Circle: 100 tick marks drawn in a loop (10 major at whole numbers 0-9, 90 minor at 0.1 intervals)
  2. Two Hands:
    • Hour hand (red, shorter, wider): Shows which decimal unit (0-9), completes one rotation per day
    • Minute hand (white, longer, thinner): Shows fractional part within current decimal unit, completes 10 rotations per day
  3. Digital Display: Shows exact decimal time at bottom (formatted to 2 decimal places)

The hand rotation uses trigonometry with angles calculated from decimal time, offset by -90° to start at top (12 o'clock position).

Device Support

The manifest (manifest.xml) targets 57 modern Garmin devices including:

  • Fenix series (6, 7, 8, Epix 2)
  • Forerunner series (255, 265, 645, 945, 955, 965, 970)
  • Vivoactive series (3, 4, 5, 6)
  • Venu series (2 Plus, 3, 4)

All devices require Connect IQ API level 3.2.0 or higher.

Project Structure

MetricWatchFace/
├── manifest.xml                      # App metadata, device compatibility
├── monkey.jungle                     # Build configuration
├── source/
│   ├── DecimalWatchFaceApp.mc       # Application entry point
│   └── DecimalWatchFaceView.mc      # Main watchface view and rendering
└── resources/
    ├── drawables/
    │   ├── drawables.xml            # Drawable resource definitions
    │   └── launcher_icon.png        # App icon (placeholder)
    └── strings/
        └── strings.xml              # Localized strings

Important Implementation Details

Time Calculation Edge Cases

  • The decimal time formula ensures smooth progression from 0.0 to 10.0
  • The fractional part extraction (decimalTime - decimalTime.toNumber()) is used for the minute hand
  • Both hands update every second when the watchface is active

Drawing Performance

  • The watchface redraws completely on each onUpdate() call
  • 100 tick marks are drawn in every frame (major optimization opportunity)
  • Hands are drawn as filled polygons using triangle geometry
  • Center dot is redrawn after hands to ensure it's on top

Sleep Mode Handling

  • onEnterSleep() and onExitSleep() are currently empty
  • In sleep mode, the system reduces update frequency automatically
  • Consider implementing reduced complexity rendering for sleep mode

Known Technical Debt

From IMPROVEMENT_IDEAS.md:

  • Launcher icon is currently a placeholder PNG
  • No screen type optimization (MIP vs AMOLED)
  • No low power mode implementation
  • Hand polygon drawing could be optimized
  • No error handling for edge cases
  • No unit tests for time conversion

Monkey C Specifics

Language: Monkey C (Garmin's proprietary language, similar to Java/C) Key APIs used:

  • Toybox.WatchUi.WatchFace - Base class for watchfaces
  • Toybox.Graphics - Drawing primitives (drawLine, fillPolygon, drawText, etc.)
  • Toybox.System - System functions (getClockTime)
  • Toybox.Time / Toybox.Time.Gregorian - Time utilities
  • Toybox.Lang / Toybox.Math - Language and math utilities

Common patterns:

  • Drawing context (dc) is passed to all render methods
  • Colors use Graphics.COLOR_* constants
  • Coordinates are in pixels, origin at top-left
  • All trigonometry uses radians (convert with Math.toRadians())