// // MoonDiskView.swift // MoonWatch // import SwiftUI private struct MoonSouthernHemisphereKey: EnvironmentKey { static let defaultValue = false } extension EnvironmentValues { /// When true, waxing/waning orientation matches southern-sky convention (mirrored). var moonSouthernHemisphere: Bool { get { self[MoonSouthernHemisphereKey.self] } set { self[MoonSouthernHemisphereKey.self] = newValue } } } /// In-app moon view: photographic disk + atmospheric halo, drop shadows, /// and limb stroke. The image+shadow rendering lives in `MoonImageDisk` /// (in `Shared/`) so the widget can reuse it. struct MoonDiskView: View { /// Synodic phase in `[0, 1)`; 0 = new, 0.5 = full. var normalizedPhase: Double @Environment(\.moonSouthernHemisphere) private var southernHemisphere var body: some View { GeometryReader { geo in let side = min(geo.size.width, geo.size.height) ZStack { Circle() .fill( RadialGradient( colors: [ Color(red: 0.98, green: 0.94, blue: 0.78).opacity(0.32), Color(red: 0.55, green: 0.50, blue: 0.85).opacity(0.12), Color.clear, ], center: .center, startRadius: side * 0.32, endRadius: side * 0.95 ) ) .frame(width: side * 1.9, height: side * 1.9) .blendMode(.screen) .blur(radius: side * 0.05) MoonImageDisk( normalizedPhase: normalizedPhase, southernHemisphere: southernHemisphere ) .frame(width: side, height: side) } .shadow(color: Color(red: 0.98, green: 0.94, blue: 0.78).opacity(0.30), radius: side * 0.10) .shadow(color: Color(red: 0.45, green: 0.55, blue: 0.95).opacity(0.30), radius: side * 0.22) .frame(width: side, height: side) .frame(maxWidth: .infinity, maxHeight: .infinity) } .aspectRatio(1, contentMode: .fit) } } #Preview("Waxing Crescent") { ZStack { Color(red: 0.04, green: 0.05, blue: 0.12).ignoresSafeArea() MoonDiskView(normalizedPhase: 0.12) .frame(width: 280, height: 280) } } #Preview("First Quarter") { ZStack { Color(red: 0.04, green: 0.05, blue: 0.12).ignoresSafeArea() MoonDiskView(normalizedPhase: 0.25) .frame(width: 280, height: 280) } } #Preview("Full") { ZStack { Color(red: 0.04, green: 0.05, blue: 0.12).ignoresSafeArea() MoonDiskView(normalizedPhase: 0.50) .frame(width: 280, height: 280) } } #Preview("Waning Gibbous") { ZStack { Color(red: 0.04, green: 0.05, blue: 0.12).ignoresSafeArea() MoonDiskView(normalizedPhase: 0.62) .frame(width: 280, height: 280) } }