154 lines
5.7 KiB
Markdown
154 lines
5.7 KiB
Markdown
# 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:**
|
|
```bash
|
|
$SDK_PATH/bin/monkeyc \
|
|
-o bin/MetricWatchFace.prg \
|
|
-f monkey.jungle \
|
|
-y $SDK_PATH/bin/developer_key
|
|
```
|
|
|
|
**Run in simulator:**
|
|
```bash
|
|
$SDK_PATH/bin/connectiq
|
|
# Then load the generated .prg file in the simulator
|
|
```
|
|
|
|
**Build for specific device (example for Fenix 7):**
|
|
```bash
|
|
$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()`)
|