diff --git a/CHANGELOG.md b/CHANGELOG.md index 60546df..5206276 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.19] — 2026-02-23 + +### Fixed +- Corrected Timer screen compact `MAX View` moon/sun sizing so moon-size ratios now match the full preview geometry instead of using oversized fixed moon radii. + +### Changed +- Bumped `apps/mobile` version to `1.1.19`. + +### Tests +- Added regression tests to verify compact-stage moon/sun ratio scaling and C1/C4 tangency distance in the Timer screen `MAX View` size profile. + ## [1.1.18] — 2026-02-23 ### Added diff --git a/apps/mobile/package.json b/apps/mobile/package.json index e09caa1..ab1491d 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@eclipse-timer/mobile", - "version": "1.1.18", + "version": "1.1.19", "private": true, "main": "index.js", "scripts": { diff --git a/apps/mobile/src/screens/TimerScreen.tsx b/apps/mobile/src/screens/TimerScreen.tsx index 03ad756..4c87f2c 100644 --- a/apps/mobile/src/screens/TimerScreen.tsx +++ b/apps/mobile/src/screens/TimerScreen.tsx @@ -27,6 +27,8 @@ import { eclipseCenterForRecord, kindCodeForRecord } from "../utils/eclipse"; import { calculatePreviewMoonGeometry, determinePreviewTravelVector, + PREVIEW_STAGE_SIZE, + PREVIEW_SUN_RADIUS, } from "../utils/previewGeometry"; const VISIBLE_PATH_COLOR = "rgba(79, 195, 247, 0.22)"; @@ -36,7 +38,8 @@ const FAVORITE_COORD_EPSILON = 0.0001; const DEG2RAD = Math.PI / 180; const RAD2DEG = 180 / Math.PI; const TIMER_HERO_PREVIEW_STAGE_SIZE = 84; -const TIMER_HERO_PREVIEW_SUN_RADIUS = 19; +const TIMER_HERO_PREVIEW_SUN_RADIUS = + (TIMER_HERO_PREVIEW_STAGE_SIZE * PREVIEW_SUN_RADIUS) / PREVIEW_STAGE_SIZE; const TIMER_HERO_PREVIEW_GLOW_SIZE = 118; function localKindLabel(kind: "none" | "partial" | "total" | "annular") { diff --git a/apps/mobile/src/utils/previewGeometry.ts b/apps/mobile/src/utils/previewGeometry.ts index bf056d5..860a304 100644 --- a/apps/mobile/src/utils/previewGeometry.ts +++ b/apps/mobile/src/utils/previewGeometry.ts @@ -2,6 +2,12 @@ import type { EclipseKindAtLocation } from "@eclipse-timer/shared"; export const PREVIEW_SUN_RADIUS = 72; export const PREVIEW_STAGE_SIZE = 300; +const ANNULAR_RADIUS_RATIO = 58 / PREVIEW_SUN_RADIUS; +const TOTAL_RADIUS_RATIO = 76 / PREVIEW_SUN_RADIUS; +const PARTIAL_RADIUS_RATIO = 68 / PREVIEW_SUN_RADIUS; +const NONE_RADIUS_RATIO = 66 / PREVIEW_SUN_RADIUS; +const NONE_GAP_RATIO = 14 / PREVIEW_SUN_RADIUS; +const PARTIAL_INTERNAL_MARGIN_RATIO = 6 / PREVIEW_SUN_RADIUS; export type PreviewMotionContacts = { c1?: number; @@ -98,11 +104,14 @@ export function describePreviewTravelDirection(vector: PreviewTravelVector): str return `${vertical}, ${horizontal}`; } -export function determineMoonRadius(kindAtLocation: EclipseKindAtLocation) { - if (kindAtLocation === "annular") return 58; - if (kindAtLocation === "total") return 76; - if (kindAtLocation === "partial") return 68; - return 66; +export function determineMoonRadius( + kindAtLocation: EclipseKindAtLocation, + sunRadius = PREVIEW_SUN_RADIUS, +) { + if (kindAtLocation === "annular") return sunRadius * ANNULAR_RADIUS_RATIO; + if (kindAtLocation === "total") return sunRadius * TOTAL_RADIUS_RATIO; + if (kindAtLocation === "partial") return sunRadius * PARTIAL_RADIUS_RATIO; + return sunRadius * NONE_RADIUS_RATIO; } export function determineApproachOffset( @@ -112,13 +121,13 @@ export function determineApproachOffset( sunRadius = PREVIEW_SUN_RADIUS, ) { if (kindAtLocation === "none") { - return sunRadius + moonRadius + 14; + return sunRadius + moonRadius + sunRadius * NONE_GAP_RATIO; } if (kindAtLocation === "partial") { const safeMag = typeof magnitude === "number" && Number.isFinite(magnitude) ? clamp01(magnitude) : 0.6; - return (1 - safeMag) * (sunRadius + moonRadius - 6); + return (1 - safeMag) * (sunRadius + moonRadius - sunRadius * PARTIAL_INTERNAL_MARGIN_RATIO); } return 0; @@ -196,7 +205,7 @@ export function calculatePreviewMoonGeometry(params: { }): PreviewMoonGeometry { const stageSize = params.stageSize ?? PREVIEW_STAGE_SIZE; const sunRadius = params.sunRadius ?? PREVIEW_SUN_RADIUS; - const moonRadius = determineMoonRadius(params.kindAtLocation); + const moonRadius = determineMoonRadius(params.kindAtLocation, sunRadius); const moonClosestOffset = determineApproachOffset( params.kindAtLocation, params.magnitude, diff --git a/apps/mobile/tests/preview-geometry.test.ts b/apps/mobile/tests/preview-geometry.test.ts index 1bdba6e..e1955a5 100644 --- a/apps/mobile/tests/preview-geometry.test.ts +++ b/apps/mobile/tests/preview-geometry.test.ts @@ -4,6 +4,7 @@ import { calculatePreviewMoonGeometry, describePreviewTravelDirection, determinePreviewTravelVector, + PREVIEW_STAGE_SIZE, PREVIEW_SUN_RADIUS, } from "../src/utils/previewGeometry"; @@ -130,4 +131,54 @@ describe("preview moon geometry", () => { expect(c1Distance).toBeCloseTo(PREVIEW_SUN_RADIUS + c1Geometry.moonRadius, 6); expect(c4Distance).toBeCloseTo(PREVIEW_SUN_RADIUS + c4Geometry.moonRadius, 6); }); + + it("keeps moon-size ratio consistent when rendered in compact max-view stage", () => { + const compactStageSize = 84; + const compactSunRadius = (compactStageSize * PREVIEW_SUN_RADIUS) / PREVIEW_STAGE_SIZE; + const fullGeometry = calculatePreviewMoonGeometry({ + progress: 0.5, + kindAtLocation: "total", + contacts: { c1: 0, c2: 0.25, max: 0.5, c3: 0.75, c4: 1 }, + }); + const compactGeometry = calculatePreviewMoonGeometry({ + progress: 0.5, + kindAtLocation: "total", + contacts: { c1: 0, c2: 0.25, max: 0.5, c3: 0.75, c4: 1 }, + stageSize: compactStageSize, + sunRadius: compactSunRadius, + }); + + const fullRatio = fullGeometry.moonRadius / PREVIEW_SUN_RADIUS; + const compactRatio = compactGeometry.moonRadius / compactSunRadius; + expect(compactRatio).toBeCloseTo(fullRatio, 10); + }); + + it("preserves C1/C4 tangency distance in compact max-view stage", () => { + const compactStageSize = 84; + const compactSunRadius = (compactStageSize * PREVIEW_SUN_RADIUS) / PREVIEW_STAGE_SIZE; + const stageCenter = compactStageSize / 2; + const contacts = { c1: 0, c2: 0.25, max: 0.5, c3: 0.75, c4: 1 }; + + const c1Geometry = calculatePreviewMoonGeometry({ + progress: 0, + kindAtLocation: "annular", + contacts, + stageSize: compactStageSize, + sunRadius: compactSunRadius, + }); + const c4Geometry = calculatePreviewMoonGeometry({ + progress: 1, + kindAtLocation: "annular", + contacts, + stageSize: compactStageSize, + sunRadius: compactSunRadius, + }); + + const c1Distance = Math.abs(c1Geometry.moonCenterX - stageCenter); + const c4Distance = Math.abs(c4Geometry.moonCenterX - stageCenter); + const expectedDistance = compactSunRadius + c1Geometry.moonRadius; + + expect(c1Distance).toBeCloseTo(expectedDistance, 6); + expect(c4Distance).toBeCloseTo(expectedDistance, 6); + }); });