krz/moonrise

Night-sky companion for iPhone, Apple Watch and Vision Pro: what's up tonight, the Moon, planets, launches and missions, and a Library of the solar system. SwiftUI app with a Cloudflare Workers backend. astronomy cloudflare-workers ios night-sky space swiftui visionos watchos

Commit 6cb5debb74

6cb5debb74c26b1211f5548dc1ad8d54f476bfbd

parent: 5a2ad3b532

Verified · cmc

cmc <hello@cleberg.net> · 2026-10-19 05:46 UTC

Scale of the universe: zoom buttons, page colours

Layout: unified · split

App/Sources/ScaleScreen.swift +23 −7
@@ -15,6 +15,22 @@ struct ScaleScreen: View {
1515
1616 private var width: Double { pow(10, exponent) }
1717
18 /// Zooms by a factor of ten per tap, snapping to the nearest whole power of ten first.
19 private func zoomButton(_ symbol: String, _ label: String, by step: Double) -> some View {
20 Button {
21 withAnimation(.easeInOut(duration: 0.35)) {
22 let snapped = step < 0 ? ceil(exponent) - 1 : floor(exponent) + 1
23 exponent = min(max(snapped, range.lowerBound), range.upperBound)
24 }
25 } label: {
26 Image(systemName: symbol).font(.title3).frame(width: 48, height: 48)
27 .background(ink.opacity(0.1), in: Circle())
28 }
29 .buttonStyle(.plain)
30 .accessibilityLabel(label)
31 .disabled(step < 0 ? exponent <= range.lowerBound : exponent >= range.upperBound)
32 }
33
1834 /// The width to two significant figures, for the label.
1935 private var roundedWidth: Double {
2036 let p = pow(10, floor(exponent) - 1)
@@ -41,7 +57,7 @@ struct ScaleScreen: View {
4157 selected = o.page.flatMap { Library.page(id: $0) }
4258 } label: {
4359 VStack(spacing: 6) {
44 Circle().fill(o.page == nil ? ink.opacity(0.18) : ink.opacity(0.55))
60 Circle().fill(o.page == nil ? ink.opacity(0.18) : (Color(hex: o.page.flatMap { Library.page(id: $0) }?.drawing?.color) ?? ink.opacity(0.55)))
4561 .overlay(Circle().stroke(ink.opacity(0.5), lineWidth: 0.5))
4662 .frame(width: d, height: d)
4763 Text(o.name).font(.caption2).lineLimit(1)
@@ -67,13 +83,13 @@ struct ScaleScreen: View {
6783 exponent = min(max(pinchStart! - log10(g.magnification), range.lowerBound), range.upperBound)
6884 }
6985 .onEnded { _ in pinchStart = nil })
70 Slider(value: $exponent, in: range) { Text("Width shown") } minimumValueLabel: {
71 Image(systemName: "plus.magnifyingglass")
72 } maximumValueLabel: {
73 Image(systemName: "minus.magnifyingglass")
86 HStack(spacing: 12) {
87 zoomButton("plus.magnifyingglass", "Zoom in", by: -1)
88 Slider(value: $exponent, in: range) { Text("Width shown") }
89 .accessibilityValue(Library.formatSize(roundedWidth))
90 zoomButton("minus.magnifyingglass", "Zoom out", by: 1)
7491 }
75 .accessibilityValue(Library.formatSize(roundedWidth))
76 Text("The slider spans 300 m to the observable universe; each tenth of it is a factor of ten. Pinch to zoom. Objects without a Library page are drawn faint.")
92 Text("Each tap of + or − is a factor of ten. You can also drag the slider or pinch. Objects without a Library page are drawn faint.")
7793 .font(.caption2).dimmed(0.5)
7894 }
7995 .padding(20)