Packages/MoonriseKit/Sources/SkyRenderer/HitTest.swift
34 lines · 1289 bytes
1import CoreGraphics
2import SkyEngine
3
4public enum SkyObject: Sendable, Equatable {
5 case body(Body)
6 /// IAU abbreviation.
7 case constellation(String)
8}
9
10extension SkyScene {
11 /// The object under a tap: the nearest visible sun, moon or planet within
12 /// `radius` points, else the constellation whose figure has a star nearest the tap.
13 public func object(at point: CGPoint, projection p: Projection, radius: CGFloat = 28) -> SkyObject? {
14 func distance(_ h: Horizontal) -> CGFloat? {
15 guard h.altitude > 0, let q = p.point(h) else { return nil }
16 return hypot(q.x - point.x, q.y - point.y)
17 }
18 let bodyHit = bodies
19 .filter { $0.magnitude <= 6 }
20 .compactMap { b in distance(b.position).map { (b.body, $0) } }
21 .filter { $0.1 <= radius }
22 .min { $0.1 < $1.1 }
23 if let bodyHit { return .body(bodyHit.0) }
24
25 var best: (String, CGFloat)?
26 for c in Catalog.constellations {
27 for hr in c.lines.joined() {
28 guard let h = starsByHR[hr], let d = distance(h), d <= radius * 1.5 else { continue }
29 if best == nil || d < best!.1 { best = (c.id, d) }
30 }
31 }
32 return best.map { .constellation($0.0) }
33 }
34}