"second" hand

This commit is contained in:
Gyuri Horák 2025-11-04 09:45:42 +01:00
parent 683569dca5
commit 7803d440ea
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG Key ID: 4993F07B3EAE8D38
3 changed files with 170 additions and 2 deletions

153
CLAUDE.md Normal file
View File

@ -0,0 +1,153 @@
# 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()`)

View File

@ -8,7 +8,7 @@ This document contains potential enhancements and features to improve the decima
### 1. Better Graphics & Styling ### 1. Better Graphics & Styling
- **Proper launcher icon** - Replace placeholder with actual designed icon - **Proper launcher icon** - Replace placeholder with actual designed icon
- **Sub-divisions/minor tick marks** - Add marks at 0.5 intervals for finer precision - **Sub-divisions/minor tick marks** - Add marks at 0.5 intervals for finer precision (/)
- **Color themes** - **Color themes**
- Dark mode / Light mode - Dark mode / Light mode
- "Metric blue/orange" scheme - "Metric blue/orange" scheme

View File

@ -7,6 +7,8 @@ using Toybox.Time.Gregorian;
class DecimalWatchFaceView extends WatchUi.WatchFace { class DecimalWatchFaceView extends WatchUi.WatchFace {
var isHighPowerMode = true; // Track if we're in high power mode
function initialize() { function initialize() {
WatchFace.initialize(); WatchFace.initialize();
} }
@ -50,10 +52,12 @@ class DecimalWatchFaceView extends WatchUi.WatchFace {
// The user has just looked at their watch. Timers and animations may be started here. // The user has just looked at their watch. Timers and animations may be started here.
function onExitSleep() { function onExitSleep() {
isHighPowerMode = true;
} }
// Terminate any active timers and prepare for slow updates. // Terminate any active timers and prepare for slow updates.
function onEnterSleep() { function onEnterSleep() {
isHighPowerMode = false;
} }
// Compute decimal time from current clock time // Compute decimal time from current clock time
@ -124,7 +128,7 @@ class DecimalWatchFaceView extends WatchUi.WatchFace {
dc.fillCircle(centerX, centerY, 5); dc.fillCircle(centerX, centerY, 5);
} }
// Draw both hour and minute hands for current decimal time // Draw hour, minute, and second hands for current decimal time
function drawHand(dc, decimalTime) { function drawHand(dc, decimalTime) {
var width = dc.getWidth(); var width = dc.getWidth();
var height = dc.getHeight(); var height = dc.getHeight();
@ -143,6 +147,17 @@ class DecimalWatchFaceView extends WatchUi.WatchFace {
var minuteAngleRad = Math.toRadians(minuteAngle); var minuteAngleRad = Math.toRadians(minuteAngle);
var minuteHandLength = radius - 45; // Longer hand var minuteHandLength = radius - 45; // Longer hand
// Calculate second hand (shows finer detail, 100 rotations per day)
// Only visible in high power mode
var secondFractionalPart = (decimalTime * 10.0) - (decimalTime * 10.0).toNumber();
var secondAngle = secondFractionalPart * 360.0 - 90.0;
var secondHandLength = radius - 35; // Longest hand
// Draw second hand first (if in high power mode), so it appears behind other hands
if (isHighPowerMode) {
drawSingleHand(dc, centerX, centerY, secondAngle, secondHandLength, 2, Graphics.COLOR_DK_GRAY);
}
// Draw minute hand (longer, thinner) // Draw minute hand (longer, thinner)
drawSingleHand(dc, centerX, centerY, minuteAngle, minuteHandLength, 4, Graphics.COLOR_WHITE); drawSingleHand(dc, centerX, centerY, minuteAngle, minuteHandLength, 4, Graphics.COLOR_WHITE);